strimertul/frontend/src/ui/pages/twitch/TwitchSettings/TwitchAPISettings.tsx

196 lines
5.7 KiB
TypeScript

import { useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useModule, useStatus } from '~/lib/react';
import { useAppDispatch } from '~/store';
import apiReducer, { modules } from '~/store/api/reducer';
import { checkTwitchKeys } from '~/lib/twitch';
import BrowserLink from '../../../components/BrowserLink';
import DefinitionTable from '../../../components/DefinitionTable';
import RevealLink from '../../../components/utils/RevealLink';
import SaveButton from '../../../components/forms/SaveButton';
import {
Button,
ButtonGroup,
Field,
InputBox,
Label,
PasswordInputBox,
SectionHeader,
styled,
TextBlock,
} from '../../../theme';
import AlertContent from '../../../components/AlertContent';
import { Alert } from '../../../theme/alert';
const StepList = styled('ul', {
lineHeight: '1.5',
listStyleType: 'none',
listStylePosition: 'outside',
});
const Step = styled('li', {
marginBottom: '0.5rem',
paddingLeft: '1rem',
'&::marker': {
color: '$teal11',
content: '▧',
display: 'inline-block',
marginLeft: '-0.5rem',
},
});
type TestResult = { open: boolean; error?: Error };
export default function TwitchAPISettings() {
const { t } = useTranslation();
const [httpConfig] = useModule(modules.httpConfig);
const [twitchConfig, setTwitchConfig, loadStatus] = useModule(
modules.twitchConfig,
);
const status = useStatus(loadStatus.save);
const dispatch = useAppDispatch();
const [revealClientSecret, setRevealClientSecret] = useState(false);
const [testing, setTesting] = useState(false);
const [testResult, setTestResult] = useState<TestResult>({
open: false,
});
const checkCredentials = async () => {
setTesting(true);
if (twitchConfig) {
try {
await checkTwitchKeys(
twitchConfig.api_client_id,
twitchConfig.api_client_secret,
);
setTestResult({ open: true });
} catch (e: unknown) {
setTestResult({ open: true, error: e as Error });
}
}
setTesting(false);
};
return (
<form
onSubmit={(ev) => {
void dispatch(setTwitchConfig(twitchConfig));
ev.preventDefault();
}}
>
<SectionHeader spacing={'none'}>
{t('pages.twitch-settings.api-subheader')}
</SectionHeader>
<TextBlock>{t('pages.twitch-settings.apiguide-1')}</TextBlock>
<StepList>
<Step>
<Trans i18nKey="pages.twitch-settings.apiguide-2">
{' '}
<BrowserLink href="https://dev.twitch.tv/console/apps/create">
https://dev.twitch.tv/console/apps/create
</BrowserLink>
</Trans>
</Step>
<Step>
{t('pages.twitch-settings.apiguide-3')}
<DefinitionTable
entries={{
[t('pages.twitch-settings.app-oauth-redirect-url')]: `http://${
httpConfig?.bind.indexOf(':') > 0
? httpConfig.bind
: `localhost${httpConfig?.bind ?? ':4337'}`
}/twitch/callback`,
[t('pages.twitch-settings.app-category')]: 'Broadcasting Suite',
}}
/>
</Step>
<Step>
<Trans i18nKey="pages.twitch-settings.apiguide-4">
{'str1 '}
<b>str2</b>
</Trans>
</Step>
</StepList>
<Field size="fullWidth" css={{ marginTop: '2rem' }}>
<Label htmlFor="clientid">
{t('pages.twitch-settings.app-client-id')}
</Label>
<InputBox
type="text"
id="clientid"
placeholder={t('pages.twitch-settings.app-client-id')}
required={true}
value={twitchConfig?.api_client_id ?? ''}
onChange={(ev) =>
dispatch(
apiReducer.actions.twitchConfigChanged({
...twitchConfig,
api_client_id: ev.target.value,
}),
)
}
/>
</Field>
<Field size="fullWidth">
<Label htmlFor="clientsecret">
{t('pages.twitch-settings.app-client-secret')}
<RevealLink
value={revealClientSecret}
setter={setRevealClientSecret}
/>
</Label>
<PasswordInputBox
reveal={revealClientSecret}
id="clientsecret"
placeholder={t('pages.twitch-settings.app-client-secret')}
required={true}
value={twitchConfig?.api_client_secret ?? ''}
onChange={(ev) =>
dispatch(
apiReducer.actions.twitchConfigChanged({
...twitchConfig,
api_client_secret: ev.target.value,
}),
)
}
/>
</Field>
<ButtonGroup>
<SaveButton status={status} />
<Button
type="button"
onClick={() => {
void checkCredentials();
}}
disabled={testing}
>
{t('pages.twitch-settings.test-button')}
</Button>
</ButtonGroup>
<Alert
defaultOpen={false}
open={testResult.open}
onOpenChange={(val: boolean) => {
setTestResult({ ...testResult, open: val });
}}
>
<AlertContent
variation={testResult.error ? 'danger' : 'default'}
description={
testResult.error
? t('pages.twitch-settings.test-failed', {
error: testResult.error.message,
})
: t('pages.twitch-settings.test-succeeded')
}
actionText={t('form-actions.ok')}
onAction={() => {
setTestResult({ ...testResult, open: false });
}}
/>
</Alert>
</form>
);
}