refactor: improve code quality
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
import { useEvents } from '@/hooks/data/useEvents';
|
import { useEvents } from '@/hooks/data/useEvents';
|
||||||
import { Button } from '../../ui/button';
|
import { Button } from '../../ui/button';
|
||||||
import { DialogTrigger } from '../../ui/dialog';
|
import { DialogTrigger } from '../../ui/dialog';
|
||||||
@@ -10,22 +11,28 @@ import { EventGridView } from './event-grid.view';
|
|||||||
export function EventGridContainer() {
|
export function EventGridContainer() {
|
||||||
const { data, isLoading } = useEvents();
|
const { data, isLoading } = useEvents();
|
||||||
|
|
||||||
|
const events = useMemo(() => {
|
||||||
|
return data?.pages.flatMap(page => page.data ?? []).map(toEventInfo) ?? [];
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isLoading
|
isLoading
|
||||||
? <EventGridSkeleton />
|
? <EventGridSkeleton />
|
||||||
: (
|
: (
|
||||||
<EventGridView
|
<EventGridView
|
||||||
events={data.pages.flatMap(page => page.data ?? []).map(toEventInfo)}
|
events={events}
|
||||||
footer={(eventInfo) => {
|
footer={(eventInfo) => {
|
||||||
const Container = eventInfo.requireKyc ? KycDialogContainer : EventJoinDialogContainer;
|
const Container = eventInfo.requireKyc ? KycDialogContainer : EventJoinDialogContainer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container event={eventInfo}>
|
<Container event={eventInfo}>
|
||||||
{eventInfo.isJoined ? (
|
{eventInfo.isJoined
|
||||||
|
? (
|
||||||
<Button className="w-full" disabled>
|
<Button className="w-full" disabled>
|
||||||
已加入
|
已加入
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
)
|
||||||
|
: (
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button className="w-full">加入活动</Button>
|
<Button className="w-full">加入活动</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import type { TurnstileInstance } from '@marsidev/react-turnstile';
|
import type { TurnstileInstance } from '@marsidev/react-turnstile';
|
||||||
import type { AuthorizeSearchParams } from '@/routes/authorize';
|
import type { AuthorizeSearchParams } from '@/routes/authorize';
|
||||||
import { Turnstile } from '@marsidev/react-turnstile';
|
import { Turnstile } from '@marsidev/react-turnstile';
|
||||||
|
import { useForm } from '@tanstack/react-form';
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import z from 'zod';
|
||||||
import NixOSLogo from '@/assets/nixos.svg?react';
|
import NixOSLogo from '@/assets/nixos.svg?react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Field,
|
Field,
|
||||||
|
FieldError,
|
||||||
FieldGroup,
|
FieldGroup,
|
||||||
FieldLabel,
|
FieldLabel,
|
||||||
} from '@/components/ui/field';
|
} from '@/components/ui/field';
|
||||||
@@ -23,29 +26,43 @@ export function LoginForm({
|
|||||||
}: React.ComponentProps<'div'> & {
|
}: React.ComponentProps<'div'> & {
|
||||||
oauthParams: AuthorizeSearchParams;
|
oauthParams: AuthorizeSearchParams;
|
||||||
}) {
|
}) {
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
|
||||||
const turnstileRef = useRef<TurnstileInstance>(null);
|
const turnstileRef = useRef<TurnstileInstance>(null);
|
||||||
const [token, setToken] = useState<string | null>(import.meta.env.DEV ? 'turnstile_token' : null);
|
const [token, setToken] = useState<string | null>(import.meta.env.DEV ? 'turnstile_token' : null);
|
||||||
const { mutateAsync, isPending } = useGetMagicLink();
|
const { mutateAsync, isPending } = useGetMagicLink();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const form = useForm({
|
||||||
event.preventDefault();
|
defaultValues: {
|
||||||
const formData = new FormData(formRef.current!);
|
email: '',
|
||||||
const email = formData.get('email')! as string;
|
},
|
||||||
mutateAsync({ body: { email, turnstile_token: token!, ...oauthParams } }).then(() => {
|
validators: {
|
||||||
void navigate({ to: '/magicLinkSent', search: { email } });
|
onSubmit: z.object({
|
||||||
}).catch((error) => {
|
email: z.string().email('请输入有效的邮箱地址'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
onSubmit: async ({ value }) => {
|
||||||
|
try {
|
||||||
|
await mutateAsync({ body: { email: value.email, turnstile_token: token!, ...oauthParams } });
|
||||||
|
await navigate({ to: '/magicLinkSent', search: { email: value.email } });
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error('请求登录链接失败');
|
toast.error('请求登录链接失败');
|
||||||
turnstileRef.current?.reset();
|
turnstileRef.current?.reset();
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const isLoading = isPending || token === null;
|
const isLoading = isPending || token === null;
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex flex-col gap-6', className)} {...props}>
|
<div className={cn('flex flex-col gap-6', className)} {...props}>
|
||||||
<form ref={formRef} onSubmit={handleSubmit}>
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
void form.handleSubmit();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<FieldGroup>
|
<FieldGroup>
|
||||||
<div className="flex flex-col items-center gap-2 text-center">
|
<div className="flex flex-col items-center gap-2 text-center">
|
||||||
<div className="flex size-8 items-center justify-center rounded-md">
|
<div className="flex size-8 items-center justify-center rounded-md">
|
||||||
@@ -54,6 +71,8 @@ export function LoginForm({
|
|||||||
<span className="sr-only">Nix CN CMS</span>
|
<span className="sr-only">Nix CN CMS</span>
|
||||||
<h1 className="text-xl font-bold">欢迎来到 Nix CN CMS</h1>
|
<h1 className="text-xl font-bold">欢迎来到 Nix CN CMS</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<form.Field name="email">
|
||||||
|
{field => (
|
||||||
<Field>
|
<Field>
|
||||||
<FieldLabel htmlFor="email">参会登记Email</FieldLabel>
|
<FieldLabel htmlFor="email">参会登记Email</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
@@ -62,8 +81,14 @@ export function LoginForm({
|
|||||||
type="email"
|
type="email"
|
||||||
placeholder="edolstra@gmail.com"
|
placeholder="edolstra@gmail.com"
|
||||||
required
|
required
|
||||||
|
value={field.state.value}
|
||||||
|
onBlur={field.handleBlur}
|
||||||
|
onChange={e => field.handleChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<FieldError errors={field.state.meta.errors} />
|
||||||
</Field>
|
</Field>
|
||||||
|
)}
|
||||||
|
</form.Field>
|
||||||
<Field>
|
<Field>
|
||||||
<Button type="submit" disabled={isLoading}>
|
<Button type="submit" disabled={isLoading}>
|
||||||
{isLoading && <Loader2 className="animate-spin" />}
|
{isLoading && <Loader2 className="animate-spin" />}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { Query } from '@tanstack/react-query';
|
|
||||||
import type { ClassValue } from 'clsx';
|
import type { ClassValue } from 'clsx';
|
||||||
// eslint-disable-next-line unicorn/prefer-node-protocol
|
// eslint-disable-next-line unicorn/prefer-node-protocol
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
@@ -19,15 +18,6 @@ export function utf8ToBase64(utf8: string): string {
|
|||||||
return Buffer.from(utf8, 'utf-8').toString('base64');
|
return Buffer.from(utf8, 'utf-8').toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function invalidateBlurry(id: string) {
|
|
||||||
return {
|
|
||||||
predicate: (query: Query<unknown, Error, unknown, readonly unknown[]>) => {
|
|
||||||
const key = query.queryKey[0] as { _id: string };
|
|
||||||
return key?._id === id;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isInDateRange(start: Date, end: Date, target: Date = new Date()): boolean {
|
export function isInDateRange(start: Date, end: Date, target: Date = new Date()): boolean {
|
||||||
const time = target.getTime();
|
const time = target.getTime();
|
||||||
return time >= start.getTime() && time <= end.getTime();
|
return time >= start.getTime() && time <= end.getTime();
|
||||||
|
|||||||
Reference in New Issue
Block a user