Skip to content

Commit

Permalink
refactor(frontend): use modern async
Browse files Browse the repository at this point in the history
  • Loading branch information
GZTimeWalker committed Dec 10, 2024
1 parent 3f44ccd commit fb2a667
Show file tree
Hide file tree
Showing 45 changed files with 1,254 additions and 1,250 deletions.
21 changes: 14 additions & 7 deletions src/GZCTF/ClientApp/src/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,7 @@ export class HttpClient<SecurityDataType = unknown> {
};
}

import { handleAxiosError } from "@Utils/ApiHelper";
import useSWR, { MutatorOptions, SWRConfiguration, mutate } from "swr";

/**
Expand Down Expand Up @@ -5087,11 +5088,17 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
const api = new Api();
export default api;

export const fetcher = async (path: string, query?: Record<string, unknown>) => {
return await api
.request({ path, query })
.then((res) => res.data)
.catch((err) => {
throw err.response.data;
});
export const fetcher = async (args: string | [string, Record<string, unknown>]) => {
try {
if (typeof args === "string") {
const response = await api.request({ path: args });
return response.data;
} else {
const [path, query] = args;
const response = await api.request({ path, query });
return response.data;
}
} catch (error) {
throw handleAxiosError(error);
}
};
190 changes: 95 additions & 95 deletions src/GZCTF/ClientApp/src/components/GameChallengeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,82 +41,84 @@ export const GameChallengeModal: FC<GameChallengeModalProps> = (props) => {
const [submitId, setSubmitId] = useState(0)
const [flag, setFlag] = useInputState('')

const onCreate = () => {
const onCreate = async () => {
if (!challengeId || disabled) return
setDisabled(true)
api.game
.gameCreateContainer(gameId, challengeId)
.then((res) => {
mutate({
...challenge,
context: {
...challenge?.context,
closeTime: res.data.expectStopAt,
instanceEntry: res.data.entry,
},
})
showNotification({
color: 'teal',
title: t('challenge.notification.instance.created.title'),
message: t('challenge.notification.instance.created.message'),
icon: <Icon path={mdiCheck} size={1} />,
})

try {
const res = await api.game.gameCreateContainer(gameId, challengeId)
mutate({
...challenge,
context: {
...challenge?.context,
closeTime: res.data.expectStopAt,
instanceEntry: res.data.entry,
},
})
showNotification({
color: 'teal',
title: t('challenge.notification.instance.created.title'),
message: t('challenge.notification.instance.created.message'),
icon: <Icon path={mdiCheck} size={1} />,
})
.catch((e) => showErrorNotification(e, t))
.finally(() => setDisabled(false))
} catch (e) {
showErrorNotification(e, t)
} finally {
setDisabled(false)
}
}

const onDestroy = () => {
const onDestroy = async () => {
if (!challengeId || disabled) return
setDisabled(true)

mutate()
.then((res) => {
// the instanceEntry is already destroyed
if (!res?.context?.instanceEntry) return

return api.game
.gameDeleteContainer(gameId, challengeId)
.then(() => {
mutate({
...challenge,
context: {
...challenge?.context,
closeTime: null,
instanceEntry: null,
},
})
showNotification({
color: 'teal',
title: t('challenge.notification.instance.destroyed.title'),
message: t('challenge.notification.instance.destroyed.message'),
icon: <Icon path={mdiCheck} size={1} />,
})
})
.catch((e) => showErrorNotification(e, t))
try {
await mutate()
if (!challenge?.context?.instanceEntry) return

await api.game.gameDeleteContainer(gameId, challengeId)
mutate({
...challenge,
context: {
...challenge?.context,
closeTime: null,
instanceEntry: null,
},
})
showNotification({
color: 'teal',
title: t('challenge.notification.instance.destroyed.title'),
message: t('challenge.notification.instance.destroyed.message'),
icon: <Icon path={mdiCheck} size={1} />,
})
.finally(() => setDisabled(false))
} catch (e) {
showErrorNotification(e, t)
} finally {
setDisabled(false)
}
}

const onExtend = () => {
const onExtend = async () => {
if (!challengeId || disabled) return
setDisabled(true)
api.game
.gameExtendContainerLifetime(gameId, challengeId)
.then((res) => {
mutate({
...challenge,
context: {
...challenge?.context,
closeTime: res.data.expectStopAt,
},
})

try {
const res = await api.game.gameExtendContainerLifetime(gameId, challengeId)
mutate({
...challenge,
context: {
...challenge?.context,
closeTime: res.data.expectStopAt,
},
})
.catch((e) => showErrorNotification(e, t))
.finally(() => setDisabled(false))
} catch (e) {
showErrorNotification(e, t)
} finally {
setDisabled(false)
}
}

const onSubmit = () => {
const onSubmit = async () => {
if (!challengeId || !flag) {
showNotification({
color: 'red',
Expand All @@ -127,51 +129,49 @@ export const GameChallengeModal: FC<GameChallengeModalProps> = (props) => {
}

setDisabled(true)
api.game
.gameSubmit(gameId, challengeId, {

try {
const res = await api.game.gameSubmit(gameId, challengeId, {
flag,
})
.then((res) => {
setSubmitId(res.data)
notifications.clean()
showNotification({
id: 'flag-submitted',
color: 'orange',
title: t('challenge.notification.flag.submitted.title'),
message: t('challenge.notification.flag.submitted.message'),
loading: true,
autoClose: false,
})
setSubmitId(res.data)
notifications.clean()
showNotification({
id: 'flag-submitted',
color: 'orange',
title: t('challenge.notification.flag.submitted.title'),
message: t('challenge.notification.flag.submitted.message'),
loading: true,
autoClose: false,
})
.catch((e) => showErrorNotification(e, t))
} catch (e) {
showErrorNotification(e, t)
}
}

useEffect(() => {
// submitId initialization will trigger useEffect
if (!submitId) return

const polling = setInterval(() => {
api.game
.gameStatus(gameId, challengeId, submitId)
.then((res) => {
if (res.data !== AnswerResult.FlagSubmitted) {
setDisabled(false)
setFlag('')
checkDataFlag(submitId, res.data)
clearInterval(polling)
}
})
.catch((err) => {
setDisabled(false)
setFlag('')
showErrorNotification(err, t)
clearInterval(polling)
})
}, 500)
const pollingStatus = async () => {
try {
const res = await api.game.gameStatus(gameId, challengeId, submitId)
if (res.data === AnswerResult.FlagSubmitted) return
setDisabled(false)
setFlag('')
checkDataFlag(submitId, res.data)
} catch (err) {
setDisabled(false)
setFlag('')
showErrorNotification(err, t)
}
}

const polling = setInterval(pollingStatus, 500)
return () => clearInterval(polling)
}, [submitId])

const checkDataFlag = (id: number, data: string) => {
const checkDataFlag = async (id: number, data: string) => {
if (data === AnswerResult.Accepted) {
updateNotification({
id: 'flag-submitted',
Expand All @@ -184,8 +184,8 @@ export const GameChallengeModal: FC<GameChallengeModalProps> = (props) => {
autoClose: 8000,
loading: false,
})
if (isDynamic && challenge.context?.instanceEntry) onDestroy()
mutate()
if (isDynamic && challenge.context?.instanceEntry) await onDestroy()
await mutate()
props.onClose()
} else if (data === AnswerResult.WrongAnswer) {
updateNotification({
Expand Down
19 changes: 2 additions & 17 deletions src/GZCTF/ClientApp/src/components/GameNoticePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Empty } from '@Components/Empty'
import { InlineMarkdown } from '@Components/MarkdownRenderer'
import { useLanguage } from '@Utils/I18n'
import { NoticTypeIconMap } from '@Utils/Shared'
import { OnceSWRConfig } from '@Hooks/useConfig'
import api, { GameNotice, NoticeType } from '@Api'
import misc from '@Styles/Misc.module.css'
import typoClasses from '@Styles/Typography.module.css'
Expand Down Expand Up @@ -97,29 +98,14 @@ export const GameNoticePanel: FC = () => {

const [, update] = useState(new Date())
const newNotices = useRef<GameNotice[]>([])
const [notices, setNotices] = useState<GameNotice[]>()
const [filter, setFilter] = useState<NoticeFilter>(NoticeFilter.All)
const iconMap = NoticTypeIconMap(0.8)

const { t } = useTranslation()
const { locale } = useLanguage()
const theme = useMantineTheme()

useEffect(() => {
api.game
.gameNotices(numId)
.then((data) => {
setNotices(data.data)
})
.catch((err) => {
showNotification({
color: 'red',
title: t('game.notification.fetch_failed.notice'),
message: err.response.data.title,
icon: <Icon path={mdiClose} size={1} />,
})
})
}, [numId, t])
const { data: notices } = api.game.useGameNotices(numId, {}, OnceSWRConfig)

useEffect(() => {
newNotices.current = []
Expand All @@ -137,7 +123,6 @@ export const GameNoticePanel: FC = () => {
connection.serverTimeoutInMilliseconds = 60 * 1000 * 60 * 2

connection.on('ReceivedGameNotice', (message: GameNotice) => {
console.log(message)
newNotices.current = [message, ...newNotices.current]

if (message.type === NoticeType.NewChallenge || message.type === NoticeType.NewHint) {
Expand Down
26 changes: 13 additions & 13 deletions src/GZCTF/ClientApp/src/components/PasswordChangeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const PasswordChangeModal: FC<ModalProps> = (props) => {

const { t } = useTranslation()

const onChangePwd = () => {
const onChangePwd = async () => {
if (!pwd || !retypedPwd) {
showNotification({
color: 'red',
Expand All @@ -28,22 +28,22 @@ export const PasswordChangeModal: FC<ModalProps> = (props) => {
icon: <Icon path={mdiClose} size={1} />,
})
} else if (pwd === retypedPwd) {
api.account
.accountChangePassword({
try {
await api.account.accountChangePassword({
old: oldPwd,
new: pwd,
})
.then(() => {
showNotification({
color: 'teal',
message: t('account.notification.profile.password_updated'),
icon: <Icon path={mdiCheck} size={1} />,
})
props.onClose()
api.account.accountLogOut()
navigate('/account/login')
showNotification({
color: 'teal',
message: t('account.notification.profile.password_updated'),
icon: <Icon path={mdiCheck} size={1} />,
})
.catch((e) => showErrorNotification(e, t))
props.onClose()
api.account.accountLogOut()
navigate('/account/login')
} catch (e) {
showErrorNotification(e, t)
}
} else {
showNotification({
color: 'red',
Expand Down
Loading

0 comments on commit fb2a667

Please sign in to comment.