s3si.ts/gui/src/pages/Settings.tsx

150 lines
4.6 KiB
TypeScript
Raw Normal View History

2023-03-06 14:36:06 -05:00
import { ErrorContent } from 'components/ErrorContent';
2023-03-06 09:01:18 -05:00
import { Loading } from 'components/Loading';
2023-03-06 14:36:06 -05:00
import { usePromise, usePromiseLazy } from 'hooks/usePromise';
import React, { useState } from 'react'
2023-03-06 07:21:29 -05:00
import { useTranslation } from 'react-i18next';
import { AiOutlineLeft } from 'react-icons/ai';
import { useNavigate } from 'react-router-dom';
2023-03-06 14:36:06 -05:00
import { Config, getConfig, getProfile, Profile, setConfig, setProfile } from 'services/config';
import { composeLoadable } from 'utils/composeLoadable';
import classNames from 'classnames';
import { useLogin } from 'services/s3si';
2023-03-06 07:21:29 -05:00
2023-03-06 14:36:06 -05:00
const Page: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
const { t } = useTranslation();
2023-03-06 07:21:29 -05:00
const navigate = useNavigate();
2023-03-06 14:36:06 -05:00
return <div className='card m-2 h-full'>
<h2 className="card-title" data-tauri-drag-region><button onClick={() => navigate('/')}><AiOutlineLeft /></button>{t('配置')}</h2>
{children}
</div>
}
type FormData = {
config: Config,
profile: Profile,
}
const Form: React.FC<{
oldValue: FormData,
onSaved?: () => void,
}> = ({ oldValue, onSaved }) => {
const { login } = useLogin();
2023-03-06 07:21:29 -05:00
const { t } = useTranslation();
2023-03-06 14:36:06 -05:00
const [value, setValue] = useState(oldValue);
2023-03-06 07:21:29 -05:00
2023-03-06 14:36:06 -05:00
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,
},
}
}
})
2023-03-06 14:36:06 -05:00
const [onSave, { loading, error }] = usePromiseLazy(async () => {
await setProfile(0, value.profile);
await setConfig(value.config);
onSaved?.();
})
const [onLogin, loginState] = usePromiseLazy(async () => {
const result = await login();
if (!result) {
return;
}
setSessionToken(result.sessionToken);
})
2023-03-06 08:32:00 -05:00
2023-03-06 07:21:29 -05:00
return <>
2023-03-06 14:36:06 -05:00
<div className='card'>
<div className="form-control w-full max-w-md mb-4">
<label className="label">
<span className="label-text">{t('Nintendo Account 会话令牌')}</span>
<span className="label-text-alt"><button
className={classNames('link', {
loading: loginState.loading,
})}
onClick={onLogin}
2023-03-06 15:27:48 -05:00
disabled={loginState.loading}
>{t('网页登录')}</button></span>
</label>
<input
className="input input-bordered w-full"
type="text"
placeholder={t('请点击右上角的登录填入') ?? undefined}
value={value.profile.state.loginState?.sessionToken ?? ''}
onChange={e => setSessionToken(e.target.value)}
/>
</div>
<div className="form-control w-full max-w-md mb-4">
2023-03-06 14:36:06 -05:00
<label className="label">
<span className="label-text">{t('stat.ink API密钥')}</span>
<span className="label-text-alt"><a
className='underline'
target='_blank'
rel='noopener noreferrer'
href='https://stat.ink/profile'
title={t('打开 stat.ink') ?? undefined}
>{t('stat.ink')}</a></span>
</label>
<input
className="input input-bordered w-full"
2023-03-06 14:36:06 -05:00
type="text"
placeholder={t('长度为43') ?? undefined}
value={value.profile.state.statInkApiKey ?? ''}
onChange={e => setValue({
...value,
profile: {
...value.profile,
state: {
...value.profile.state,
statInkApiKey: e.target.value,
}
}
})}
/>
2023-03-06 07:21:29 -05:00
</div>
2023-03-06 14:36:06 -05:00
</div>
<ErrorContent error={error} />
<div className='flex gap-4 max-w-md justify-between flex-auto-all'>
<div className="tooltip" data-tip={changed ? undefined : t('没有更改')}>
<button className={classNames('btn btn-primary w-full', {
loading,
})} onClick={onSave} disabled={!changed}>{t('保存')}</button>
</div>
<button className={classNames('btn', {
2023-03-06 14:36:06 -05:00
loading,
})} onClick={() => setValue(oldValue)}>{t('重置')}</button>
2023-03-06 07:21:29 -05:00
</div>
</>
2023-03-06 14:36:06 -05:00
}
export const Settings: React.FC = () => {
let { loading, error, retry, result } = composeLoadable({
config: usePromise(getConfig),
profile: usePromise(() => getProfile(0)),
});
if (loading) {
return <Page>
<div className='h-full flex items-center justify-center'><Loading /></div>
</Page>
}
if (error) {
return <Page>
<ErrorContent error={error} retry={retry} />
</Page>
}
return <Page>
{result && <Form oldValue={result} onSaved={retry} />}
</Page>
}