Skip to content

Commit

Permalink
feat: skeleton for challenges page
Browse files Browse the repository at this point in the history
  • Loading branch information
chenjunyu19 committed Aug 20, 2022
1 parent 98d4559 commit 47a937e
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 141 deletions.
60 changes: 31 additions & 29 deletions GZCTF/ClientApp/src/components/ChallengeNoticePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as signalR from '@microsoft/signalr'
import { FC, useEffect, useRef, useState } from 'react'
import { useParams } from 'react-router-dom'
import { Card, List, ScrollArea } from '@mantine/core'
import { Card, List, ScrollArea, Stack } from '@mantine/core'
import { showNotification } from '@mantine/notifications'
import { mdiClose } from '@mdi/js'
import { Icon } from '@mdi/react'
Expand Down Expand Up @@ -79,39 +79,41 @@ const ChallengeNoticePanel: FC = () => {

return (
<Card shadow="sm">
<ScrollArea
offsetScrollbars
scrollbarSize={4}
style={{ height: 'calc(100vh - 20rem)' }}
sx={{
scrollbar: {
'&:hover': {
backgroundColor: 'transparent',
{noticesToShow.length ? (
<ScrollArea
offsetScrollbars
scrollbarSize={4}
style={{ height: 'calc(100vh - 20rem)' }}
sx={{
scrollbar: {
'&:hover': {
backgroundColor: 'transparent',
},
},
},
}}
>
<List
size="sm"
spacing={3}
styles={(theme) => ({
item: {
fontWeight: 500,
color: theme.colorScheme === 'dark' ? theme.colors.dark[2] : theme.colors.gray[6],
},
})}
}}
>
{noticesToShow.length ? (
noticesToShow.map((notice) => (
<List
size="sm"
spacing={3}
styles={(theme) => ({
item: {
fontWeight: 500,
color: theme.colorScheme === 'dark' ? theme.colors.dark[2] : theme.colors.gray[6],
},
})}
>
{noticesToShow.map((notice) => (
<List.Item key={notice.id} icon={iconMap.get(notice.type)}>
{notice.content}
</List.Item>
))
) : (
<Empty description="暂无通知" />
)}
</List>
</ScrollArea>
))}
</List>
</ScrollArea>
) : (
<Stack justify="center" style={{ height: 'calc(100vh - 20rem)' }}>
<Empty description="暂无通知" />
</Stack>
)}
</Card>
)
}
Expand Down
293 changes: 185 additions & 108 deletions GZCTF/ClientApp/src/components/ChallengePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import React, { FC, useState } from 'react'
import { useParams } from 'react-router-dom'
import { Center, Group, ScrollArea, SimpleGrid, Tabs, Text } from '@mantine/core'
import {
Card,
Center,
Divider,
Group,
ScrollArea,
SimpleGrid,
Skeleton,
Stack,
Tabs,
Text,
} from '@mantine/core'
import { mdiFlagOutline, mdiPuzzle } from '@mdi/js'
import { Icon } from '@mdi/react'
import api, { ChallengeInfo, ChallengeTag, SubmissionType } from '@Api'
Expand All @@ -27,128 +38,194 @@ const ChallengePanel: FC = () => {
const [detailOpened, setDetailOpened] = useState(false)
const { iconMap, colorMap } = SubmissionTypeIconMap(0.8)

return allChallenges.length ? (
return challenges ? (
allChallenges.length ? (
<Group
spacing="sm"
noWrap
position="apart"
align="flex-start"
style={{ width: 'calc(100% - 20rem)' }}
>
<Tabs
orientation="vertical"
variant="pills"
value={activeTab}
onTabChange={(value) => setActiveTab(value as ChallengeTag)}
styles={{
tabsList: {
minWidth: '10rem',
},
tab: {
fontWeight: 700,
},
tabLabel: {
width: '100%',
},
}}
>
<Tabs.List>
<Tabs.Tab value={'All'} icon={<Icon path={mdiPuzzle} size={1} />}>
<Group position="apart">
<Text>All</Text>
<Text>{allChallenges.length}</Text>
</Group>
</Tabs.Tab>
{tags.map((tab) => {
const data = ChallengeTagLabelMap.get(tab as ChallengeTag)!
return (
<Tabs.Tab
key={tab}
value={tab}
icon={<Icon path={data?.icon} size={1} />}
color={data?.color}
>
<Group position="apart">
<Text>{data?.label}</Text>
<Text>{challenges && challenges[tab].length}</Text>
</Group>
</Tabs.Tab>
)
})}
</Tabs.List>
</Tabs>
<ScrollArea
style={{
width: 'calc(100% - 9rem)',
height: 'calc(100vh - 100px)',
position: 'relative',
}}
offsetScrollbars
scrollbarSize={4}
>
<SimpleGrid
cols={3}
spacing="sm"
p="xs"
style={{ paddingTop: 0 }}
breakpoints={[
{ maxWidth: 2900, cols: 6 },
{ maxWidth: 2500, cols: 5 },
{ maxWidth: 2100, cols: 4 },
{ maxWidth: 1700, cols: 3 },
{ maxWidth: 1300, cols: 2 },
{ maxWidth: 900, cols: 1 },
]}
>
{currentChallenges.map((chal) => (
<ChallengeCard
key={chal.id}
challenge={chal}
iconMap={iconMap}
colorMap={colorMap}
onClick={() => {
setChallenge(chal)
setDetailOpened(true)
}}
solved={
myteam &&
myteam.challenges?.find((c) => c.id === chal.id)?.type !==
SubmissionType.Unaccepted
}
teamId={myteam?.id}
/>
))}
</SimpleGrid>
</ScrollArea>
{challenge?.id && (
<ChallengeDetailModal
opened={detailOpened}
onClose={() => setDetailOpened(false)}
withCloseButton={false}
size="35%"
centered
gameId={numId}
solved={
myteam &&
myteam.challenges?.find((c) => c.id === challenge?.id)?.type !==
SubmissionType.Unaccepted
}
tagData={
ChallengeTagLabelMap.get((challenge?.tag as ChallengeTag) ?? ChallengeTag.Misc)!
}
title={challenge?.title ?? ''}
score={challenge?.score ?? 0}
challengeId={challenge.id}
/>
)}
</Group>
) : (
<Center sx={{ width: 'calc(100% - 20rem)', height: 'calc(100vh - 100px)' }}>
<Empty
bordered={true}
description="Ouch! 这个比赛还没有可用题目呢……"
fontSize="xl"
mdiPath={mdiFlagOutline}
iconSize={8}
/>
</Center>
)
) : (
<Group
spacing="sm"
noWrap
position="apart"
align="flex-start"
style={{ width: 'calc(100% - 20rem)' }}
>
<Tabs
orientation="vertical"
variant="pills"
value={activeTab}
onTabChange={(value) => setActiveTab(value as ChallengeTag)}
styles={{
tabsList: {
minWidth: '10rem',
},
tab: {
fontWeight: 700,
},
tabLabel: {
width: '100%',
},
}}
>
<Tabs.List>
<Tabs.Tab value={'All'} icon={<Icon path={mdiPuzzle} size={1} />}>
<Group position="apart">
<Text>All</Text>
<Text>{allChallenges.length}</Text>
<Stack sx={{ minWidth: '10rem' }} spacing={6}>
{Array(8)
.fill(null)
.map((_v, i) => (
<Group key={i} noWrap p={10}>
<Skeleton height="1.5rem" width="1.5rem" />
<Skeleton height="1rem" />
</Group>
</Tabs.Tab>
{tags.map((tab) => {
const data = ChallengeTagLabelMap.get(tab as ChallengeTag)!
return (
<Tabs.Tab
key={tab}
value={tab}
icon={<Icon path={data?.icon} size={1} />}
color={data?.color}
>
<Group position="apart">
<Text>{data?.label}</Text>
<Text>{challenges && challenges[tab].length}</Text>
</Group>
</Tabs.Tab>
)
})}
</Tabs.List>
</Tabs>
<ScrollArea
))}
</Stack>
<SimpleGrid
cols={3}
spacing="sm"
p="xs"
style={{
width: 'calc(100% - 9rem)',
height: 'calc(100vh - 100px)',
position: 'relative',
paddingTop: 0,
}}
offsetScrollbars
scrollbarSize={4}
breakpoints={[
{ maxWidth: 2900, cols: 6 },
{ maxWidth: 2500, cols: 5 },
{ maxWidth: 2100, cols: 4 },
{ maxWidth: 1700, cols: 3 },
{ maxWidth: 1300, cols: 2 },
{ maxWidth: 900, cols: 1 },
]}
>
<SimpleGrid
cols={3}
spacing="sm"
p="xs"
style={{ paddingTop: 0 }}
breakpoints={[
{ maxWidth: 2900, cols: 6 },
{ maxWidth: 2500, cols: 5 },
{ maxWidth: 2100, cols: 4 },
{ maxWidth: 1700, cols: 3 },
{ maxWidth: 1300, cols: 2 },
{ maxWidth: 900, cols: 1 },
]}
>
{currentChallenges.map((chal) => (
<ChallengeCard
key={chal.id}
challenge={chal}
iconMap={iconMap}
colorMap={colorMap}
onClick={() => {
setChallenge(chal)
setDetailOpened(true)
}}
solved={
myteam &&
myteam.challenges?.find((c) => c.id === chal.id)?.type !== SubmissionType.Unaccepted
}
teamId={myteam?.id}
/>
{Array(8)
.fill(null)
.map((_v, i) => (
<Card key={i} radius="md" shadow="sm">
<Stack spacing="sm" style={{ position: 'relative', zIndex: 99 }}>
<Skeleton height="1.5rem" width="70%" mt={4} />
<Divider />
<Group noWrap position="apart" align="start">
<Center>
<Skeleton height="1.5rem" width="5rem" />
</Center>
<Stack spacing="xs">
<Skeleton height="1rem" width="6rem" mt={5} />
<Group position="center" spacing="md" style={{ height: 20 }}>
<Skeleton height="1.2rem" width="1.2rem" />
<Skeleton height="1.2rem" width="1.2rem" />
<Skeleton height="1.2rem" width="1.2rem" />
</Group>
</Stack>
</Group>
</Stack>
</Card>
))}
</SimpleGrid>
</ScrollArea>
{challenge?.id && (
<ChallengeDetailModal
opened={detailOpened}
onClose={() => setDetailOpened(false)}
withCloseButton={false}
size="35%"
centered
gameId={numId}
solved={
myteam &&
myteam.challenges?.find((c) => c.id === challenge?.id)?.type !==
SubmissionType.Unaccepted
}
tagData={ChallengeTagLabelMap.get((challenge?.tag as ChallengeTag) ?? ChallengeTag.Misc)!}
title={challenge?.title ?? ''}
score={challenge?.score ?? 0}
challengeId={challenge.id}
/>
)}
</SimpleGrid>
</Group>
) : (
<Center sx={{ width: 'calc(100% - 20rem)' }}>
<Empty
bordered={true}
description="Ouch! 这个比赛还没有可用题目呢……"
fontSize="xl"
mdiPath={mdiFlagOutline}
iconSize={8}
/>
</Center>
)
}

Expand Down
Loading

0 comments on commit 47a937e

Please sign in to comment.