Skip to content

Commit

Permalink
Merge pull request #390 from skalenetwork/add-app-screenshots
Browse files Browse the repository at this point in the history
Add app screenshots component
  • Loading branch information
dmytrotkk authored Oct 15, 2024
2 parents 6f4c1ea + 91af5bf commit 0dc1b70
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 46 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@transak/transak-sdk": "^3.1.1",
"@types/react-copy-to-clipboard": "^5.0.4",
"@vercel/analytics": "^1.0.2",
"embla-carousel-react": "^8.3.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-standard-with-typescript": "^39.1.0",
"prettier": "^3.0.3",
Expand Down
8 changes: 1 addition & 7 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,13 @@ import Changelog from './pages/Changelog'

import MetricsWarning from './components/MetricsWarning'
import ScrollToTop from './components/ScrollToTop'
import ExternalRedirect from './components/ExternalRedirect'

import { getHistoryFromStorage, setHistoryToStorage } from './core/transferHistory'
import {
BRIDGE_PAGES,
MAINNET_CHAIN_NAME,
STAKING_PAGES,
STATS_API,
SUBMIT_PROJECT_URL
STATS_API
} from './core/constants'
import { type IValidator, type ISkaleContractsMap, type StakingInfoMap } from './core/interfaces'
import { getValidators } from './core/delegation/validators'
Expand Down Expand Up @@ -311,10 +309,6 @@ export default function Router() {
path="/epicgames"
element={<Navigate to="/ecosystem?categories=gaming_epic-games-store" replace />}
/>
<Route
path="/ecosystem/submit"
element={<ExternalRedirect to={SUBMIT_PROJECT_URL} />}
/>
<Route
path="ecosystem"
element={
Expand Down
37 changes: 0 additions & 37 deletions src/components/ExternalRedirect.tsx

This file was deleted.

100 changes: 100 additions & 0 deletions src/components/ecosystem/AppScreenshots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* @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 AppScreenshots.tsx
* @copyright SKALE Labs 2024-Present
*/

import React, { useState, useEffect } from 'react'
import { cls, cmn, SkPaper } from '@skalenetwork/metaport'

import { Collapse } from '@mui/material'
import ExploreRoundedIcon from '@mui/icons-material/ExploreRounded'

import ScreenshotCarousel from './ScreenshotCarousel'
import AccordionSection from '../AccordionSection'
import { BASE_METADATA_URL } from '../../core/constants'

interface AppScreenshotsProps {
chainName: string
appName: string
skaleNetwork: string
}

const AppScreenshots: React.FC<AppScreenshotsProps> = ({ chainName, appName, skaleNetwork }) => {
const [screenshots, setScreenshots] = useState<string[]>([])
const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string | null>(null)

const fetchScreenshots = async (): Promise<void> => {
setLoading(true)
setError(null)
const screenshotUrls: string[] = []
let index = 1

const baseUrl = `${BASE_METADATA_URL}/${skaleNetwork}/screenshots`

while (true) {
let found = false
for (const ext of ['png', 'jpg']) {
const url = `${baseUrl}/${chainName}-${appName}-${index}.${ext}`
try {
const response = await fetch(url, { method: 'HEAD' })
if (response.ok) {
screenshotUrls.push(url)
found = true
break
}
} catch (err) {
console.error(`Error checking screenshot: ${url}`, err)
}
}
if (!found) break
index++
}

if (screenshotUrls.length === 0) {
setError('No screenshots found for this app.')
} else {
setScreenshots(screenshotUrls)
}
setLoading(false)
}

useEffect(() => {
fetchScreenshots()
}, [chainName, appName, skaleNetwork])

return (
<Collapse in={!loading && !error}>
<SkPaper gray className={cls(cmn.mtop10, 'fwmobile')}>
<AccordionSection
expandedByDefault
title="Explore project"
icon={<ExploreRoundedIcon />}
marg={false}
>
<ScreenshotCarousel screenshots={screenshots} appName={appName} />
</AccordionSection>
</SkPaper>
</Collapse>
)
}

export default AppScreenshots
128 changes: 128 additions & 0 deletions src/components/ecosystem/ScreenshotCarousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* @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 ScreenshotCarousel.tsx
* @copyright SKALE Labs 2024-Present
*/

import React, { useState, useEffect, useCallback } from 'react'
import useEmblaCarousel from 'embla-carousel-react'
import { Box, IconButton } from '@mui/material'
import ArrowBackIosNewRoundedIcon from '@mui/icons-material/ArrowBackIosNewRounded'
import ArrowForwardIosRoundedIcon from '@mui/icons-material/ArrowForwardIosRounded'
import { cls, cmn, styles } from '@skalenetwork/metaport'

type PropType = {
screenshots: string[]
appName: string
}

const ScreenshotCarousel: React.FC<PropType> = ({ screenshots, appName }) => {
const [emblaRef, emblaApi] = useEmblaCarousel({
align: 'start',
loop: false,
dragFree: true
})

const [prevBtnEnabled, setPrevBtnEnabled] = useState(false)
const [nextBtnEnabled, setNextBtnEnabled] = useState(false)

const scrollPrev = useCallback(() => emblaApi && emblaApi.scrollPrev(), [emblaApi])
const scrollNext = useCallback(() => emblaApi && emblaApi.scrollNext(), [emblaApi])

const onSelect = useCallback(() => {
if (!emblaApi) return
setPrevBtnEnabled(emblaApi.canScrollPrev())
setNextBtnEnabled(emblaApi.canScrollNext())
}, [emblaApi])

useEffect(() => {
if (!emblaApi) return
onSelect()
emblaApi.on('select', onSelect)
emblaApi.on('reInit', onSelect)
}, [emblaApi, onSelect])

return (
<Box position="relative" className={cls(cmn.mtop10)}>
<Box className="embla" ref={emblaRef} sx={{ overflow: 'hidden' }}>
<Box className="embla__container" display="flex" height={400}>
{screenshots.map((screenshot, index) => (
<Box
key={index}
className="embla__slide"
flex="0 0 auto"
marginRight="10px"
height="100%"
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<img
src={screenshot}
alt={`${appName} screenshot ${index + 1}`}
style={{
height: '100%',
width: 'auto',
maxWidth: 'none',
objectFit: 'cover',
borderRadius: '25px'
}}
/>
</Box>
))}
</Box>
</Box>
<IconButton
onClick={scrollPrev}
disabled={!prevBtnEnabled}
className="filled"
size="small"
sx={{
position: 'absolute',
top: '50%',
left: '15px',
transform: 'translateY(-50%)',
zIndex: 1
}}
>
<ArrowBackIosNewRoundedIcon className={cls(cmn.pSec, styles.chainIconxs)} />
</IconButton>
<IconButton
onClick={scrollNext}
disabled={!nextBtnEnabled}
className="filled"
size="small"
sx={{
position: 'absolute',
top: '50%',
right: '15px',
transform: 'translateY(-50%)',
zIndex: 1
}}
>
<ArrowForwardIosRoundedIcon className={cls(cmn.pSec, styles.chainIconxs)} />
</IconButton>
</Box>
)
}

export default ScreenshotCarousel
2 changes: 2 additions & 0 deletions src/pages/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import ErrorTile from '../components/ErrorTile'
import { ChipNew, ChipPreTge, ChipTrending } from '../components/Chip'
import { getRecentApps, isNewApp, isTrending } from '../core/ecosystem/utils'
import { useApps } from '../useApps'
import AppScreenshots from '../components/ecosystem/AppScreenshots'

export default function App(props: {
mpc: MetaportCore
Expand Down Expand Up @@ -336,6 +337,7 @@ export default function App(props: {
)}
</Grid>
</SkPaper>
<AppScreenshots chainName={chain} appName={app} skaleNetwork={network} />
{chain !== OFFCHAIN_APP && (
<SkPaper gray className={cls(cmn.mtop10, 'fwmobile')}>
<AccordionSection
Expand Down
8 changes: 7 additions & 1 deletion vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,11 @@
"source": "/(.*)",
"destination": "/"
}
],
"redirects": [
{
"source": "/submit",
"destination": "https://github.com/skalenetwork/skale-network/issues/new?assignees=dmytrotkk&labels=metadata&projects=&template=app_submission.yml&title=App+Metadata+Submission"
}
]
}
}

0 comments on commit 0dc1b70

Please sign in to comment.