diff --git a/gui/src/components/Header.tsx b/gui/src/components/Header.tsx index 77ff659..3ddab8e 100644 --- a/gui/src/components/Header.tsx +++ b/gui/src/components/Header.tsx @@ -9,6 +9,6 @@ type HeaderProps = { export const Header: React.FC = ({ title }) => { const navigate = useNavigate(); return <> -

{title}

+

{title}

} diff --git a/gui/src/components/RunPanel.tsx b/gui/src/components/RunPanel.tsx index 1fa1fc1..1cd818a 100644 --- a/gui/src/components/RunPanel.tsx +++ b/gui/src/components/RunPanel.tsx @@ -3,7 +3,7 @@ import { usePromise } from 'hooks/usePromise'; import React, { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next'; import { canExport, getProfile, setProfile } from 'services/config'; -import { run, useLog } from 'services/s3si'; +import { addLog, run, useLog } from 'services/s3si'; import { Checkbox } from './Checkbox'; import { Loading } from './Loading'; @@ -24,6 +24,10 @@ export const RunPanel: React.FC = () => { const onClick = async () => { setLoading(true); try { + addLog({ + level: 'log', + msg: ['Export started at', new Date().toLocaleString()], + }) const { state } = result; const newState = await run(state, { exporter: "stat.ink,file", @@ -35,14 +39,24 @@ export const RunPanel: React.FC = () => { ...result, state: newState, }) + } catch (e) { + console.error(e) + addLog({ + level: 'error', + msg: [e], + }) } finally { + addLog({ + level: 'log', + msg: ['Export ended at', new Date().toLocaleString()], + }) setLoading(false); } } const disabled = !canExport(result); return <> -
+
{t('导出对战数据')} {t('导出打工数据')}
diff --git a/gui/src/pages/Settings.tsx b/gui/src/pages/Settings.tsx index e080b6e..379e656 100644 --- a/gui/src/pages/Settings.tsx +++ b/gui/src/pages/Settings.tsx @@ -9,11 +9,15 @@ import classNames from 'classnames'; import { useLogin } from 'services/s3si'; import { STAT_INK } from 'constant'; import { Header } from 'components/Header'; +import { useSubField } from 'hooks/useSubField'; +import { useNavigate } from 'react-router-dom'; + +const STAT_INK_KEY_LENGTH = 43; const Page: React.FC<{ children?: React.ReactNode }> = ({ children }) => { const { t } = useTranslation(); return
-
+
{children}
} @@ -30,22 +34,12 @@ const Form: React.FC<{ const { login } = useLogin(); const { t } = useTranslation(); const [value, setValue] = useState(oldValue); + const { subField } = useSubField({ value, onChange: setValue }); const changed = JSON.stringify(value) !== JSON.stringify(oldValue); - const setSessionToken = (t: string) => setValue({ - ...value, - profile: { - ...value.profile, - state: { - ...value.profile.state, - loginState: { - ...value.profile.state.loginState, - sessionToken: t, - }, - } - } - }) + const sessionToken = subField('profile.state.loginState.sessionToken') + const statInkApiKey = subField('profile.state.statInkApiKey') const [onSave, { loading, error }] = usePromiseLazy(async () => { await setProfile(0, value.profile); @@ -57,9 +51,11 @@ const Form: React.FC<{ if (!result) { return; } - setSessionToken(result.sessionToken); + sessionToken.onChange(result.sessionToken); }) + const statInkKeyError = (statInkApiKey.value?.length ?? STAT_INK_KEY_LENGTH) !== STAT_INK_KEY_LENGTH; + return <>
@@ -77,8 +73,8 @@ const Form: React.FC<{ className="input input-bordered w-full" type="text" placeholder={t('请点击右上角的登录填入') ?? undefined} - value={value.profile.state.loginState?.sessionToken ?? ''} - onChange={e => setSessionToken(e.target.value)} + value={sessionToken.value ?? ''} + onChange={e => sessionToken.onChange(e.target.value)} />
@@ -90,24 +86,19 @@ const Form: React.FC<{ rel='noopener noreferrer' href={`${STAT_INK}/profile`} title={t('打开 stat.ink') ?? undefined} - >{t('stat.ink')} + >{t('查看API密钥')} - setValue({ - ...value, - profile: { - ...value.profile, - state: { - ...value.profile.state, - statInkApiKey: e.target.value, - } - } - })} - /> +
+ statInkApiKey.onChange(e.target.value)} + /> +
@@ -115,7 +106,7 @@ const Form: React.FC<{
+ })} onClick={onSave} disabled={!changed || statInkKeyError}>{t('保存')}