fix(app): application create flow + duplicate sudo (#47)

This commit is contained in:
hampus-fluxer 2026-01-06 03:56:16 +01:00 committed by GitHub
parent 30869f8c3d
commit ec8601b9d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 31 additions and 35 deletions

View File

@ -23,12 +23,14 @@ import React from 'react';
import {useForm} from 'react-hook-form'; import {useForm} from 'react-hook-form';
import * as ModalActionCreators from '~/actions/ModalActionCreators'; import * as ModalActionCreators from '~/actions/ModalActionCreators';
import {Input} from '~/components/form/Input'; import {Input} from '~/components/form/Input';
import {Form} from '~/components/form/Form';
import * as Modal from '~/components/modals/Modal'; import * as Modal from '~/components/modals/Modal';
import styles from '~/components/modals/tabs/ApplicationsTab/ApplicationsTab.module.css'; import styles from '~/components/modals/tabs/ApplicationsTab/ApplicationsTab.module.css';
import {Button} from '~/components/uikit/Button/Button'; import {Button} from '~/components/uikit/Button/Button';
import {Endpoints} from '~/Endpoints'; import {Endpoints} from '~/Endpoints';
import HttpClient from '~/lib/HttpClient'; import HttpClient from '~/lib/HttpClient';
import type {DeveloperApplication} from '~/records/DeveloperApplicationRecord'; import type {DeveloperApplication} from '~/records/DeveloperApplicationRecord';
import {useFormSubmit} from '~/hooks/useFormSubmit';
interface ApplicationCreateModalProps { interface ApplicationCreateModalProps {
onCreated: (application: DeveloperApplication) => void; onCreated: (application: DeveloperApplication) => void;
@ -40,50 +42,41 @@ interface CreateFormValues {
export const ApplicationCreateModal: React.FC<ApplicationCreateModalProps> = observer(({onCreated}) => { export const ApplicationCreateModal: React.FC<ApplicationCreateModalProps> = observer(({onCreated}) => {
const {t} = useLingui(); const {t} = useLingui();
const { const form = useForm<CreateFormValues>({
register,
handleSubmit,
formState: {errors},
reset,
} = useForm<CreateFormValues>({
defaultValues: { defaultValues: {
name: '', name: '',
}, },
}); });
const nameField = form.register('name', {required: true, maxLength: 100});
const nameField = register('name', {required: true, maxLength: 100});
const nameInputRef = React.useRef<HTMLInputElement | null>(null); const nameInputRef = React.useRef<HTMLInputElement | null>(null);
const [creating, setCreating] = React.useState(false); const handleCancel = React.useCallback(() => {
const [createError, setCreateError] = React.useState<string | null>(null); form.reset();
form.clearErrors();
const handleCancel = () => {
reset();
setCreateError(null);
ModalActionCreators.pop(); ModalActionCreators.pop();
}; }, [form]);
const onSubmit = handleSubmit(async (data) => { const onSubmit = React.useCallback(
setCreateError(null); async (data: CreateFormValues) => {
setCreating(true);
try {
const response = await HttpClient.post<DeveloperApplication>(Endpoints.OAUTH_APPLICATIONS, { const response = await HttpClient.post<DeveloperApplication>(Endpoints.OAUTH_APPLICATIONS, {
name: data.name.trim(), name: data.name.trim(),
redirect_uris: [], redirect_uris: [],
}); });
onCreated(response.body); onCreated(response.body);
reset(); form.reset();
ModalActionCreators.pop(); ModalActionCreators.pop();
} catch (err) { },
console.error('[ApplicationCreateModal] Failed to create application:', err); [form, onCreated],
setCreateError(t`Failed to create application. Please check your inputs and try again.`); );
} finally {
setCreating(false); const {handleSubmit, isSubmitting} = useFormSubmit({
} form,
onSubmit,
defaultErrorField: 'name',
}); });
return ( return (
<Modal.Root size="small" centered initialFocusRef={nameInputRef}> <Modal.Root size="small" centered initialFocusRef={nameInputRef}>
<form onSubmit={onSubmit}> <Form form={form} onSubmit={handleSubmit}>
<Modal.Header title={t`Create Application`} /> <Modal.Header title={t`Create Application`} />
<Modal.Content className={styles.createForm}> <Modal.Content className={styles.createForm}>
<Input <Input
@ -97,22 +90,20 @@ export const ApplicationCreateModal: React.FC<ApplicationCreateModalProps> = obs
placeholder={t`My Application`} placeholder={t`My Application`}
maxLength={100} maxLength={100}
required required
disabled={creating} disabled={isSubmitting}
autoFocus autoFocus
error={form.formState.errors.name?.message}
/> />
{errors.name && <div className={styles.error}>{t`Application name is required`}</div>}
{createError && <div className={styles.error}>{createError}</div>}
</Modal.Content> </Modal.Content>
<Modal.Footer> <Modal.Footer>
<Button type="button" variant="secondary" onClick={handleCancel} disabled={creating}> <Button type="button" variant="secondary" onClick={handleCancel} disabled={isSubmitting}>
{t`Cancel`} {t`Cancel`}
</Button> </Button>
<Button type="submit" variant="primary" submitting={creating}> <Button type="submit" variant="primary" submitting={isSubmitting}>
{t`Create`} {t`Create`}
</Button> </Button>
</Modal.Footer> </Modal.Footer>
</form> </Form>
</Modal.Root> </Modal.Root>
); );
}); });

View File

@ -417,6 +417,7 @@ export const ApplicationDetail: React.FC<ApplicationDetailProps> = observer(
} else { } else {
setBotToken(res.body.token ?? null); setBotToken(res.body.token ?? null);
} }
sudo.finalize();
ToastActionCreators.createToast({ ToastActionCreators.createToast({
type: 'success', type: 'success',
children: children:

View File

@ -29,5 +29,9 @@ export const useSudo = () => {
return await SudoPromptStore.requestVerification(); return await SudoPromptStore.requestVerification();
}, []); }, []);
return {require}; const finalize = useCallback(() => {
SudoPromptStore.handleTokenReceived(null);
}, []);
return {require, finalize};
}; };