feat(ui): add loading spinners to async buttons in dialogs and forms
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user