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

View File

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

View File

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