feat(ui): add loading spinners to async buttons in dialogs and forms
All checks were successful
Client CMS Check Build (NixCN CMS) TeamCity build finished
Backend Check Build (NixCN CMS) TeamCity build finished

Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
2026-02-11 23:27:31 +08:00
parent cdd25236e4
commit 550254b844
7 changed files with 32 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
import type { ServiceUserUserInfoData } from '@/client';
import { useForm } from '@tanstack/react-form';
import { Loader2 } from 'lucide-react';
import {
useEffect,
useState,
@@ -164,9 +165,11 @@ export function EditProfileDialogView({ user, updateProfile }: { user: ServiceUs
<Button variant="outline"></Button>
</DialogClose>
<form.Subscribe
selector={state => [state.canSubmit]}
children={([canSubmit]) => (
<Button type="submit" disabled={!canSubmit}></Button>
selector={state => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<Button type="submit" disabled={!canSubmit}>
{isSubmitting ? <Loader2 className="animate-spin" /> : '保存'}
</Button>
)}
/>
</DialogFooter>

View File

@@ -6,7 +6,7 @@ import {
isEmpty,
isNil,
} from 'lodash-es';
import { Mail, Pencil } from 'lucide-react';
import { Loader2, Mail, Pencil } from 'lucide-react';
import { useMemo, useState } from 'react';
import Markdown from 'react-markdown';
import { toast } from 'sonner';
@@ -18,6 +18,7 @@ import { EditProfileDialogContainer } from './edit-profile.dialog.container';
export function ProfileView({ user, onSaveBio }: { user: ServiceUserUserInfoData; onSaveBio: (bio: string) => Promise<void> }) {
const [bio, setBio] = useState<string | undefined>(() => base64ToUtf8(user.bio ?? ''));
const [enableBioEdit, setEnableBioEdit] = useState(false);
const [isSubmittingBio, setIsSubmittingBio] = useState(false);
const IdentIcon = useMemo(() => {
const avatar = createAvatar(identicon, {
@@ -53,12 +54,12 @@ export function ProfileView({ user, onSaveBio }: { user: ServiceUserUserInfoData
{/* Bio */}
{enableBioEdit
? (
<MDEditor
value={bio}
onChange={setBio}
height="100%"
/>
)
<MDEditor
value={bio}
onChange={setBio}
height="100%"
/>
)
: <div className="p-6 prose dark:prose-invert"><Markdown>{bio}</Markdown></div>}
<Button
className="absolute bottom-4 right-4"
@@ -70,6 +71,7 @@ export function ProfileView({ user, onSaveBio }: { user: ServiceUserUserInfoData
else {
if (!isNil(bio)) {
try {
setIsSubmittingBio(true);
await onSaveBio(bio);
setEnableBioEdit(false);
}
@@ -77,13 +79,17 @@ export function ProfileView({ user, onSaveBio }: { user: ServiceUserUserInfoData
console.error(error);
toast.error('个人简介更新失败');
}
finally {
setIsSubmittingBio(false);
}
}
}
}}
size="icon-sm"
variant={enableBioEdit ? 'default' : 'outline'}
disabled={isSubmittingBio}
>
<Pencil />
{isSubmittingBio ? <Loader2 className="animate-spin" /> : <Pencil />}
</Button>
</section>
</div>