feat(client): add KYC for event joining
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
129
client/cms/src/components/events/kyc/kyc.dialog.container.tsx
Normal file
129
client/cms/src/components/events/kyc/kyc.dialog.container.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import type { KycSubmission } from './kyc.types';
|
||||
import { Dialog } from '@radix-ui/react-dialog';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useStore } from 'zustand';
|
||||
import { postEventJoin, postKycQuery } from '@/client';
|
||||
import { getEventListInfiniteQueryKey } from '@/client/@tanstack/react-query.gen';
|
||||
import { useCreateKycSession } from '@/hooks/data/useCreateKycSession';
|
||||
import { ver } from '@/lib/apiVersion';
|
||||
import { KycFailedDialogView } from './kyc-failed.dialog.view';
|
||||
import { KycMethodSelectionDialogView } from './kyc-method-selection.dialog.view';
|
||||
import { KycPendingDialogView } from './kyc-pending.dialog.view';
|
||||
import { KycPromptDialogView } from './kyc-prompt.dialog.view';
|
||||
import { KycSuccessDialogView } from './kyc-success.dialog.view';
|
||||
import { createKycStore } from './kyc.state';
|
||||
|
||||
export function KycDialogContainer({ eventIdToJoin, children }: { eventIdToJoin: string; children: React.ReactNode }) {
|
||||
const [store] = useState(() => createKycStore(eventIdToJoin));
|
||||
const isDialogOpen = useStore(store, s => s.isDialogOpen);
|
||||
const setIsDialogOpen = useStore(store, s => s.setIsDialogOpen);
|
||||
const stage = useStore(store, s => s.stage);
|
||||
const setStage = useStore(store, s => s.setStage);
|
||||
const setKycId = useStore(store, s => s.setKycId);
|
||||
|
||||
const { mutateAsync } = useCreateKycSession();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const joinEvent = useCallback(async (eventId: string, kycId: string, abortSignal?: AbortSignal) => {
|
||||
try {
|
||||
await postEventJoin({
|
||||
signal: abortSignal,
|
||||
body: { event_id: eventId, kyc_id: kycId },
|
||||
headers: ver('20260205'),
|
||||
});
|
||||
setStage('success');
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Error joining event:', e);
|
||||
setStage('failed');
|
||||
}
|
||||
}, [setStage]);
|
||||
|
||||
const onKycSessionCreate = useCallback(async (submission: KycSubmission) => {
|
||||
try {
|
||||
const { data } = await mutateAsync(submission);
|
||||
setKycId(data!.kyc_id!);
|
||||
if (data!.status === 'success') {
|
||||
await joinEvent(eventIdToJoin, data!.kyc_id!, undefined);
|
||||
}
|
||||
else if (data!.status === 'processing') {
|
||||
window.open(data!.redirect_uri, '_blank');
|
||||
setStage('pending');
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
setStage('failed');
|
||||
}
|
||||
}, [eventIdToJoin, joinEvent, mutateAsync, setKycId, setStage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (stage !== 'pending' || !isDialogOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
let timer: NodeJS.Timeout;
|
||||
|
||||
const poll = async () => {
|
||||
try {
|
||||
const { data } = await postKycQuery({
|
||||
signal: controller.signal,
|
||||
body: { kyc_id: store.getState().kycId! },
|
||||
headers: ver('20260205'),
|
||||
});
|
||||
|
||||
const status = data?.data?.status;
|
||||
|
||||
if (status === 'success') {
|
||||
void joinEvent(eventIdToJoin, store.getState().kycId!, controller.signal);
|
||||
}
|
||||
else if (status === 'failed') {
|
||||
setStage('failed');
|
||||
}
|
||||
else if (status === 'pending') {
|
||||
timer = setTimeout(() => void poll(), 1000);
|
||||
}
|
||||
else {
|
||||
// What the fuck?
|
||||
setStage('failed');
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if ((e as Error).name === 'AbortError')
|
||||
return;
|
||||
console.error('Error fetching KYC status:', e);
|
||||
setStage('failed');
|
||||
}
|
||||
};
|
||||
|
||||
void poll();
|
||||
|
||||
return () => {
|
||||
controller.abort();
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [stage, store, setStage, isDialogOpen, joinEvent, eventIdToJoin]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: getEventListInfiniteQueryKey({ query: {}, headers: ver('20260205') }),
|
||||
});
|
||||
}
|
||||
setIsDialogOpen(open);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
{stage === 'prompt' && <KycPromptDialogView next={() => setStage('methodSelection')} />}
|
||||
{stage === 'methodSelection' && <KycMethodSelectionDialogView onSubmit={onKycSessionCreate} />}
|
||||
{stage === 'pending' && <KycPendingDialogView />}
|
||||
{stage === 'success' && <KycSuccessDialogView />}
|
||||
{stage === 'failed' && <KycFailedDialogView />}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user