Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Portal 3.1 Hotfixes to Beta #403

Merged
merged 8 commits into from
Oct 29, 2024
2 changes: 1 addition & 1 deletion src/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,4 @@ export const useAuth = () => {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}
}
9 changes: 5 additions & 4 deletions src/components/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ import { cmn, cls } from '@skalenetwork/metaport'
interface CarouselProps {
children: ReactNode[]
showArrows?: boolean
className?: string
}

const Carousel: React.FC<CarouselProps> = ({ children, showArrows = true }) => {
const Carousel: React.FC<CarouselProps> = ({ children, showArrows = true, className }) => {
const [startIndex, setStartIndex] = useState(0)
const theme = useTheme()
const isXs = useMediaQuery(theme.breakpoints.only('xs'))
Expand All @@ -61,7 +62,7 @@ const Carousel: React.FC<CarouselProps> = ({ children, showArrows = true }) => {
const visibleChildren = children.slice(startIndex, startIndex + itemsToShow)

return (
<Box sx={{ position: 'relative' }}>
<Box sx={{ position: 'relative' }} className={className}>
<Box
sx={{
display: 'flex',
Expand Down Expand Up @@ -92,15 +93,15 @@ const Carousel: React.FC<CarouselProps> = ({ children, showArrows = true }) => {
onClick={handlePrev}
disabled={startIndex === 0}
size="small"
className={cls('outlined', cmn.mri5)}
className={cls('filled', cmn.mri5)}
>
<ArrowBackIosRoundedIcon />
</IconButton>
<IconButton
onClick={handleNext}
disabled={startIndex >= children.length - itemsToShow}
size="small"
className={cls(cmn.pSec, 'outlined', cmn.mleft5)}
className={cls(cmn.pSec, 'filled', cmn.mleft5)}
>
<ArrowForwardIosRoundedIcon />
</IconButton>
Expand Down
3 changes: 1 addition & 2 deletions src/components/MetricsWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ import { timestampToDate } from '../core/helper'
const MAX_DELAY_SECONDS = 48 * 60 * 60

export default function MetricsWarning(props: { metrics: types.IMetrics | null }) {
if (!props.metrics || Date.now() / 1000 - props.metrics.last_updated < MAX_DELAY_SECONDS)
return
if (!props.metrics || Date.now() / 1000 - props.metrics.last_updated < MAX_DELAY_SECONDS) return
return (
<Container maxWidth="md">
<Message
Expand Down
5 changes: 4 additions & 1 deletion src/components/ecosystem/AppCardV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,20 @@ export default function AppCard(props: {
isNew?: boolean
mostLiked?: number
trending?: boolean
gray?: boolean
}) {
const shortAlias = getChainShortAlias(props.chainsMeta, props.schainName)
const url = `/ecosystem/${shortAlias}/${props.appName}`
const appMeta = props.chainsMeta[props.schainName]?.apps?.[props.appName]

const gray = props.gray ?? true

if (!appMeta) return

const appDescription = appMeta.description ?? 'No description'

return (
<SkPaper gray fullHeight className="sk-app-card">
<SkPaper gray={gray} fullHeight className="sk-app-card">
<Link to={url}>
<div className={cls(cmn.flex)}>
<div className="sk-app-logo sk-logo-sm br__tile">
Expand Down
102 changes: 102 additions & 0 deletions src/components/ecosystem/RecommendedApps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @license
* SKALE portal
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
* @file RecommendedApps.tsx
* @copyright SKALE Labs 2024-Present
*/

import React, { useMemo } from 'react'
import { Grid, Box } from '@mui/material'
import { cls } from '@skalenetwork/metaport'

import { type types } from '@/core'

import { isNewApp, isTrending } from '../../core/ecosystem/utils'
import { findSimilarApps, type SimilarApp } from '../../core/ecosystem/similarApps'

import AppCardV2 from './AppCardV2'
import Carousel from '../Carousel'
import { useLikedApps } from '../../LikedAppsContext'

interface RecommendedAppsProps {
skaleNetwork: types.SkaleNetwork
chainsMeta: types.ChainsMetadataMap
allApps: types.AppWithChainAndName[]
currentApp?: types.AppWithChainAndName
favoriteApps?: types.AppWithChainAndName[]
newApps: types.AppWithChainAndName[]
trendingApps: types.AppWithChainAndName[]
useCarousel?: boolean
className?: string
gray?: boolean
}

const RecommendedApps: React.FC<RecommendedAppsProps> = ({
skaleNetwork,
chainsMeta,
allApps,
currentApp,
favoriteApps,
newApps,
trendingApps,
useCarousel = false,
className,
gray = false
}) => {
const { getMostLikedApps, getAppId, getMostLikedRank } = useLikedApps()
const mostLikedAppIds = useMemo(() => getMostLikedApps(), [getMostLikedApps])

const similarApps = useMemo(() => {
return findSimilarApps(currentApp, allApps, favoriteApps)
}, [currentApp, allApps, favoriteApps])

if (similarApps.length === 0) return null

const renderAppCard = (app: SimilarApp) => {
const appId = getAppId(app.chain, app.appName)
return (
<Box key={`${app.chain}-${app.appName}`} className={cls('fl-centered dappCard')}>
<AppCardV2
skaleNetwork={skaleNetwork}
schainName={app.chain}
appName={app.appName}
chainsMeta={chainsMeta}
isNew={isNewApp({ chain: app.chain, app: app.appName }, newApps)}
mostLiked={getMostLikedRank(mostLikedAppIds, appId)}
trending={isTrending(trendingApps, app.chain, app.appName)}
gray={gray}
/>
</Box>
)
}
if (useCarousel) {
return <Carousel className={className}>{similarApps.map(renderAppCard)}</Carousel>
}
return (
<Grid container spacing={2} className={className}>
{similarApps.map((app) => (
<Grid key={`${app.chain}-${app.appName}`} item xs={12} sm={6} md={4} lg={4}>
{renderAppCard(app)}
</Grid>
))}
</Grid>
)
}

export default React.memo(RecommendedApps)
67 changes: 67 additions & 0 deletions src/components/ecosystem/UserRecommendations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @license
* SKALE portal
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
* @file UserRecommendations.tsx
* @copyright SKALE Labs 2024-Present
*/

import React from 'react'
import { cmn, cls } from '@skalenetwork/metaport'
import AutoAwesomeRoundedIcon from '@mui/icons-material/AutoAwesomeRounded'

import { type types } from '@/core'

import RecommendedApps from '../ecosystem/RecommendedApps'
import { useAuth } from '../../AuthContext'
import { useApps } from '../../useApps'
import Headline from '../Headline'

const UserRecommendations: React.FC<{
skaleNetwork: types.SkaleNetwork
chainsMeta: types.ChainsMetadataMap
metrics: types.IMetrics | null
}> = ({ skaleNetwork, chainsMeta, metrics }) => {
const { isSignedIn } = useAuth()
const { allApps, favoriteApps, newApps, trendingApps } = useApps(chainsMeta, metrics)

const showRecommendations = isSignedIn && favoriteApps.length > 0
if (!showRecommendations) return null

return (
<div className={cls(cmn.mbott10, cmn.mtop20, cmn.ptop20)}>
<Headline
className={cls(cmn.mbott10)}
text="Recommended for you"
icon={<AutoAwesomeRoundedIcon color="primary" />}
/>
<RecommendedApps
skaleNetwork={skaleNetwork}
chainsMeta={chainsMeta}
allApps={allApps}
favoriteApps={favoriteApps}
newApps={newApps}
trendingApps={trendingApps}
useCarousel={true}
gray
/>
</div>
)
}

export default UserRecommendations
6 changes: 4 additions & 2 deletions src/components/profile/EmailSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface EmailSectionProps {
handleStartEditing: () => void
handleUpdateEmail: () => void
handleCancelEditing: () => void
className?: string
}

const EmailSection: React.FC<EmailSectionProps> = ({
Expand All @@ -48,7 +49,8 @@ const EmailSection: React.FC<EmailSectionProps> = ({
setNewEmail,
handleStartEditing,
handleUpdateEmail,
handleCancelEditing
handleCancelEditing,
className
}) => {
const inputRef = useRef<HTMLInputElement>(null)

Expand All @@ -61,7 +63,7 @@ const EmailSection: React.FC<EmailSectionProps> = ({
return (
<Tile
text="Email Address"
className={cls(styles.inputAmount)}
className={cls(styles.inputAmount, className)}
icon={<EmailRoundedIcon />}
value={!isEditing ? email ?? 'Not set' : undefined}
children={
Expand Down
104 changes: 52 additions & 52 deletions src/components/profile/ProfileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
*/

import React, { useState, useCallback } from 'react'
import { Modal, Box, Grid, useTheme, useMediaQuery } from '@mui/material'
import { SkPaper, useWagmiAccount } from '@skalenetwork/metaport'
import { Modal, Box, useTheme, useMediaQuery } from '@mui/material'
import { cls, cmn, SkPaper, useWagmiAccount } from '@skalenetwork/metaport'
import { useAuth } from '../../AuthContext'
import Tile from '../Tile'
import Message from '../Message'
Expand Down Expand Up @@ -77,61 +77,61 @@ const ProfileModal: React.FC = () => {
<Box className="profileModal">
<SkPaper gray>
<ProfileModalHeader address={address} isSignedIn={isSignedIn} />

{!address || !isSignedIn ? (
<ConnectWallet customText="Connect your wallet and sign-in to use your profile" />
) : (
<Grid container spacing={2}>
<Grid item xs={12}>
<Tile
text="Wallet Address"
value={address}
icon={<Jazzicon diameter={20} seed={jsNumberForAddress(address)} />}
copy={address}
/>
</Grid>
<Grid item xs={12}>
<EmailSection
email={email}
isEditing={isEditing}
isEmailLoading={isEmailLoading}
isEmailUpdating={isEmailUpdating}
newEmail={newEmail}
setNewEmail={setNewEmail}
handleStartEditing={handleStartEditing}
handleUpdateEmail={handleUpdateEmail}
handleCancelEditing={handleCancelEditing}
/>
</Grid>
<div></div>
)}
{address && isSignedIn ? (
<div>
<Tile
text="Wallet Address"
value={address}
icon={<Jazzicon diameter={20} seed={jsNumberForAddress(address)} />}
copy={address}
className={cls(cmn.mbott10)}
/>
<EmailSection
email={email}
isEditing={isEditing}
isEmailLoading={isEmailLoading}
isEmailUpdating={isEmailUpdating}
newEmail={newEmail}
setNewEmail={setNewEmail}
handleStartEditing={handleStartEditing}
handleUpdateEmail={handleUpdateEmail}
handleCancelEditing={handleCancelEditing}
className={cls(cmn.mbott10)}
/>
{emailError && (
<Grid item xs={12}>
<Message
text={emailError}
type="error"
icon={<EmailRoundedIcon />}
closable={false}
/>
</Grid>
)}

<Grid item xs={12}>
<SwellMessage
email={email}
isEditing={isEditing}
handleStartEditing={handleStartEditing}
<Message
text={emailError}
type="error"
icon={<EmailRoundedIcon />}
closable={false}
className={cls(cmn.mbott10)}
/>
</Grid>

<Grid item xs={12}>
<ProfileModalActions
address={address}
isSignedIn={isSignedIn}
isMobile={isMobile}
handleSignIn={handleSignIn}
handleSignOut={handleSignOut}
/>
</Grid>
</Grid>
)}
<SwellMessage
email={email}
isEditing={isEditing}
handleStartEditing={handleStartEditing}
/>
</div>
) : (
<div></div>
)}
{address ? (
<ProfileModalActions
className={cls(cmn.mtop20)}
address={address}
isSignedIn={isSignedIn}
isMobile={isMobile}
handleSignIn={handleSignIn}
handleSignOut={handleSignOut}
/>
) : (
<div></div>
)}
</SkPaper>
</Box>
Expand Down
Loading
Loading