@opexa/portal-components 0.0.241 → 0.0.243
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/hooks/useCamera.d.ts +6 -1
- package/dist/client/hooks/useCamera.js +57 -12
- package/dist/client/hooks/useFeatureFlag.d.ts +21 -5
- package/dist/client/hooks/useFeatureFlag.js +49 -9
- package/dist/components/Bonus/Bonus.client.js +1 -1
- package/dist/components/Cashback/Cashback.client.js +1 -1
- package/dist/components/CustomPromo/CustomPromo.js +1 -1
- package/dist/components/DepositWithdrawal/Deposit/OnlineBankDeposit/OnlineBankDepositContext.d.ts +2 -2
- package/dist/components/DepositWithdrawal/Deposit/OnlineBankDeposit/useOnlineBankDeposit.d.ts +1 -1
- package/dist/components/DepositWithdrawal/Deposit/QRPHDeposit/QRPHDepositContext.d.ts +2 -2
- package/dist/components/DepositWithdrawal/Deposit/QRPHDeposit/useQRPHDeposit.d.ts +1 -1
- package/dist/components/DepositWithdrawal/Withdrawal/InstapayWithdrawal/InstapayWithdrawal.js +2 -1
- package/dist/components/DepositWithdrawal/utils.d.ts +3 -0
- package/dist/components/DepositWithdrawal/utils.js +31 -0
- package/dist/components/GameLaunch/GameLaunch.lazy.js +1 -1
- package/dist/components/GamesSearch/GamesSearch.js +1 -1
- package/dist/components/Jackpots/JackpotsCarousel/JackpotsCarouselItem.module.css +184 -184
- package/dist/components/Jackpots/JackpotsList/JackpotsListItem.module.css +184 -184
- package/dist/components/KYC/AutoOpen.js +3 -8
- package/dist/components/KYC/utils.js +18 -18
- package/dist/components/Messages/Message.js +3 -2
- package/dist/components/Messages/MessagePopup.js +3 -2
- package/dist/components/News/News.js +1 -1
- package/dist/components/PortalProvider/FeatureFlag.d.ts +1 -0
- package/dist/components/PortalProvider/FeatureFlag.js +26 -0
- package/dist/components/PortalProvider/PortalProvider.js +2 -1
- package/dist/components/Promo/Promo.client.js +1 -1
- package/dist/components/Search/Search.lazy.js +1 -1
- package/dist/components/SignIn/MobileNumberSignIn.js +4 -2
- package/dist/components/SignIn/NameAndPasswordSignIn.js +3 -2
- package/dist/components/SignUp/SignUpForm.js +0 -4
- package/dist/components/SignUp/SignUpPagcor/CaptureIdDocument.d.ts +1 -0
- package/dist/components/SignUp/SignUpPagcor/CaptureIdDocument.js +198 -0
- package/dist/components/SignUp/SignUpPagcor/CaptureSelfie.d.ts +1 -0
- package/dist/components/SignUp/SignUpPagcor/CaptureSelfie.js +251 -0
- package/dist/components/SignUp/SignUpPagcor/ImageUploader.d.ts +10 -0
- package/dist/components/SignUp/SignUpPagcor/ImageUploader.js +41 -0
- package/dist/components/SignUp/SignUpPagcor/SignUpFormPagcor.d.ts +1 -0
- package/dist/components/SignUp/SignUpPagcor/SignUpFormPagcor.js +429 -0
- package/dist/components/SignUp/SignUpPagcor/SignUpPagcor.lazy.d.ts +13 -0
- package/dist/components/SignUp/SignUpPagcor/SignUpPagcor.lazy.js +26 -0
- package/dist/components/SignUp/SignUpPagcor/SignUpPagcorContext.d.ts +7 -0
- package/dist/components/SignUp/SignUpPagcor/SignUpPagcorContext.js +2 -0
- package/dist/components/SignUp/SignUpPagcor/useImageUploader.d.ts +11 -0
- package/dist/components/SignUp/SignUpPagcor/useImageUploader.js +20 -0
- package/dist/components/Tournaments/TournamentsCarousel/TournamentsCarouselItem.module.css +184 -184
- package/dist/components/Tournaments/TournamentsList/TournamentItem.module.css +184 -184
- package/dist/components/shared/IdDocumentField.js +18 -27
- package/dist/components/shared/SelfieField.d.ts +1 -3
- package/dist/components/shared/SelfieField.js +120 -123
- package/dist/constants/StorageKey.d.ts +1 -0
- package/dist/constants/StorageKey.js +1 -0
- package/dist/services/httpRequest.js +25 -4
- package/dist/services/queries.js +2527 -2527
- package/dist/styles/theme.css +694 -694
- package/dist/types/index.d.ts +1 -0
- package/dist/ui/AlertDialog/AlertDialog.d.ts +88 -88
- package/dist/ui/AlertDialog/alertDialog.recipe.d.ts +8 -8
- package/dist/ui/Combobox/Combobox.d.ts +42 -42
- package/dist/ui/Combobox/combobox.recipe.d.ts +3 -3
- package/dist/ui/DatePicker/DatePicker.d.ts +72 -72
- package/dist/ui/DatePicker/datePicker.recipe.d.ts +3 -3
- package/dist/ui/Dialog/Dialog.d.ts +88 -88
- package/dist/ui/Dialog/dialog.recipe.d.ts +8 -8
- package/dist/ui/Drawer/Drawer.d.ts +33 -33
- package/dist/ui/Drawer/drawer.recipe.d.ts +3 -3
- package/dist/ui/Field/Field.d.ts +21 -21
- package/dist/ui/Field/field.recipe.d.ts +3 -3
- package/dist/ui/Popover/Popover.d.ts +55 -55
- package/dist/ui/Popover/popover.recipe.d.ts +5 -5
- package/dist/ui/Select/Select.d.ts +45 -45
- package/dist/ui/Select/select.recipe.d.ts +3 -3
- package/dist/utils/resizeImageSize.d.ts +1 -0
- package/dist/utils/resizeImageSize.js +5 -0
- package/package.json +165 -169
- package/dist/client/hooks/index.d.ts +0 -65
- package/dist/client/hooks/index.js +0 -65
- package/dist/client/services/index.d.ts +0 -4
- package/dist/client/services/index.js +0 -4
- package/dist/client/utils/index.d.ts +0 -3
- package/dist/client/utils/index.js +0 -3
- package/dist/components/CashbackBonus/CashbackBonus.client.d.ts +0 -2
- package/dist/components/CashbackBonus/CashbackBonus.client.js +0 -9
- package/dist/components/CashbackBonus/CashbackBonus.d.ts +0 -10
- package/dist/components/CashbackBonus/CashbackBonus.js +0 -5
- package/dist/components/CashbackBonus/index.d.ts +0 -1
- package/dist/components/CashbackBonus/index.js +0 -1
- package/dist/components/Empty.d.ts +0 -9
- package/dist/components/Empty.js +0 -7
- package/dist/components/Games/GameContext.d.ts +0 -2
- package/dist/components/Games/GameContext.js +0 -2
- package/dist/components/InfiniteScroll.d.ts +0 -10
- package/dist/components/InfiniteScroll.js +0 -25
- package/dist/components/JackpotCounter/JackpotCounter.client.d.ts +0 -2
- package/dist/components/JackpotCounter/JackpotCounter.client.js +0 -66
- package/dist/components/JackpotCounter/JackpotCounter.d.ts +0 -17
- package/dist/components/JackpotCounter/JackpotCounter.js +0 -21
- package/dist/components/JackpotCounter/JackpotCounterItem.d.ts +0 -18
- package/dist/components/JackpotCounter/JackpotCounterItem.js +0 -109
- package/dist/components/JackpotCounter/JackpotCounterItem.module.css +0 -184
- package/dist/components/JackpotCounter/index.d.ts +0 -1
- package/dist/components/JackpotCounter/index.js +0 -1
- package/dist/components/Jackpots/JackpotDesktop.d.ts +0 -1
- package/dist/components/Jackpots/JackpotDesktop.js +0 -45
- package/dist/components/Jackpots/JackpotGameProviders.d.ts +0 -5
- package/dist/components/Jackpots/JackpotGameProviders.js +0 -41
- package/dist/components/Jackpots/JackpotItemDesktop.d.ts +0 -9
- package/dist/components/Jackpots/JackpotItemDesktop.js +0 -43
- package/dist/components/Jackpots/JackpotItemMobile.d.ts +0 -9
- package/dist/components/Jackpots/JackpotItemMobile.js +0 -44
- package/dist/components/Jackpots/JackpotMobile.d.ts +0 -1
- package/dist/components/Jackpots/JackpotMobile.js +0 -46
- package/dist/components/Jackpots/JackpotRules.d.ts +0 -5
- package/dist/components/Jackpots/JackpotRules.js +0 -11
- package/dist/components/Jackpots/Jackpots.client.d.ts +0 -2
- package/dist/components/Jackpots/Jackpots.client.js +0 -29
- package/dist/components/Jackpots/JackpotsContext.d.ts +0 -4
- package/dist/components/Jackpots/JackpotsContext.js +0 -3
- package/dist/components/Jackpots/useJackpotData.d.ts +0 -14
- package/dist/components/Jackpots/useJackpotData.js +0 -93
- package/dist/components/Jackpots/useJackpotItemData.d.ts +0 -20
- package/dist/components/Jackpots/useJackpotItemData.js +0 -87
- package/dist/components/KYC/Header.d.ts +0 -1
- package/dist/components/KYC/Header.js +0 -8
- package/dist/components/KYC/Step1.d.ts +0 -1
- package/dist/components/KYC/Step1.js +0 -13
- package/dist/components/KYC/Step2.d.ts +0 -1
- package/dist/components/KYC/Step2.js +0 -13
- package/dist/components/KYC/Step3.d.ts +0 -1
- package/dist/components/KYC/Step3.js +0 -13
- package/dist/components/KYC/Step4.d.ts +0 -1
- package/dist/components/KYC/Step4.js +0 -7
- package/dist/components/Messages/MessageItem.d.ts +0 -4
- package/dist/components/Messages/MessageItem.js +0 -34
- package/dist/components/Promo/PromoContent.d.ts +0 -2
- package/dist/components/Promo/PromoContent.js +0 -9
- package/dist/components/Promos/PromoCarousel.d.ts +0 -2
- package/dist/components/Promos/PromoCarousel.js +0 -41
- package/dist/components/Promos/PromoGrid.d.ts +0 -2
- package/dist/components/Promos/PromoGrid.js +0 -12
- package/dist/components/Promos/PromoItem.d.ts +0 -7
- package/dist/components/Promos/PromoItem.js +0 -10
- package/dist/components/TournamentCounter/TournamentCounter.client.d.ts +0 -2
- package/dist/components/TournamentCounter/TournamentCounter.client.js +0 -50
- package/dist/components/TournamentCounter/TournamentCounter.d.ts +0 -8
- package/dist/components/TournamentCounter/TournamentCounter.js +0 -21
- package/dist/components/TournamentCounter/TournamentCounterItem.d.ts +0 -8
- package/dist/components/TournamentCounter/TournamentCounterItem.js +0 -106
- package/dist/components/TournamentCounter/index.d.ts +0 -1
- package/dist/components/TournamentCounter/index.js +0 -1
- package/dist/components/Tournaments/TournamentDesktop.d.ts +0 -1
- package/dist/components/Tournaments/TournamentDesktop.js +0 -57
- package/dist/components/Tournaments/TournamentGameProviders.d.ts +0 -5
- package/dist/components/Tournaments/TournamentGameProviders.js +0 -38
- package/dist/components/Tournaments/TournamentItemDesktop.d.ts +0 -7
- package/dist/components/Tournaments/TournamentItemDesktop.js +0 -56
- package/dist/components/Tournaments/TournamentItemMobile.d.ts +0 -7
- package/dist/components/Tournaments/TournamentItemMobile.js +0 -54
- package/dist/components/Tournaments/TournamentMobile.d.ts +0 -1
- package/dist/components/Tournaments/TournamentMobile.js +0 -55
- package/dist/components/Tournaments/TournamentRules.d.ts +0 -5
- package/dist/components/Tournaments/TournamentRules.js +0 -11
- package/dist/components/Tournaments/Tournaments.client.d.ts +0 -2
- package/dist/components/Tournaments/Tournaments.client.js +0 -20
- package/dist/components/Tournaments/TournamentsContext.d.ts +0 -4
- package/dist/components/Tournaments/TournamentsContext.js +0 -3
- package/dist/components/Tournaments/useTournamentData.d.ts +0 -10
- package/dist/components/Tournaments/useTournamentData.js +0 -72
- package/dist/components/Tournaments/useTournamentItemData.d.ts +0 -16
- package/dist/components/Tournaments/useTournamentItemData.js +0 -64
- package/dist/components/index.d.ts +0 -33
- package/dist/components/index.js +0 -33
- package/dist/constants/endpoints.d.ts +0 -10
- package/dist/constants/endpoints.js +0 -41
- package/dist/constants/envVars.d.ts +0 -5
- package/dist/constants/envVars.js +0 -28
- package/dist/constants/gameProviders.d.ts +0 -76
- package/dist/constants/gameProviders.js +0 -361
- package/dist/constants/gameTypes.d.ts +0 -3
- package/dist/constants/gameTypes.js +0 -64
- package/dist/constants/headerKeys.d.ts +0 -2
- package/dist/constants/headerKeys.js +0 -2
- package/dist/constants/secretQuestions.d.ts +0 -3
- package/dist/constants/secretQuestions.js +0 -24
- package/dist/constants/storageKeys.d.ts +0 -2
- package/dist/constants/storageKeys.js +0 -2
- package/dist/icons/index.d.ts +0 -65
- package/dist/icons/index.js +0 -65
- package/dist/images/hacksaw-gaming.png +0 -0
- package/dist/server/services/index.d.ts +0 -4
- package/dist/server/services/index.js +0 -4
- package/dist/server/utils/index.d.ts +0 -29
- package/dist/server/utils/index.js +0 -29
- package/dist/ui/Presence.d.ts +0 -2
- package/dist/ui/Presence.js +0 -7
- package/dist/ui/Prose.d.ts +0 -2
- package/dist/ui/Prose.js +0 -5
- package/dist/ui/index.d.ts +0 -26
- package/dist/ui/index.js +0 -26
- package/dist/utils/createErrorFromCode.d.ts +0 -2
- package/dist/utils/createErrorFromCode.js +0 -11
- package/dist/utils/index.d.ts +0 -17
- package/dist/utils/index.js +0 -17
- package/dist/utils/isMobile.d.ts +0 -5
- package/dist/utils/isMobile.js +0 -9
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
3
|
-
import '@mediapipe/face_detection';
|
|
4
|
-
import * as faceDetection from '@tensorflow-models/face-detection';
|
|
5
|
-
import '@tensorflow/tfjs-backend-webgl';
|
|
6
|
-
import '@tensorflow/tfjs-core';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
3
|
import { useFieldContext } from '@ark-ui/react';
|
|
4
|
+
import * as faceapi from 'face-api.js';
|
|
8
5
|
import Image from 'next/image';
|
|
9
|
-
import {
|
|
6
|
+
import { cache, useEffect, useId, useRef, useState, } from 'react';
|
|
10
7
|
import { twMerge } from 'tailwind-merge';
|
|
11
8
|
import { useInterval } from 'usehooks-ts';
|
|
12
9
|
import { useCamera } from '../../client/hooks/useCamera.js';
|
|
@@ -34,17 +31,7 @@ export function SelfieField(props) {
|
|
|
34
31
|
const file = e.dataTransfer.files?.[0];
|
|
35
32
|
if (!file)
|
|
36
33
|
return;
|
|
37
|
-
const
|
|
38
|
-
const error = validateFile(file, {
|
|
39
|
-
customValidator() {
|
|
40
|
-
if (!face) {
|
|
41
|
-
return {
|
|
42
|
-
name: 'FaceNotFoundError',
|
|
43
|
-
message: 'Please ensure your face is clearly visible in the photo.',
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
});
|
|
34
|
+
const error = validateFile(file);
|
|
48
35
|
if (error)
|
|
49
36
|
return props.onError?.(error);
|
|
50
37
|
context.mutation.mutate({ file });
|
|
@@ -55,13 +42,6 @@ export function SelfieField(props) {
|
|
|
55
42
|
const error = validateFile(file);
|
|
56
43
|
if (error)
|
|
57
44
|
return props.onError?.(error);
|
|
58
|
-
const face = await getFaceFromFile(file);
|
|
59
|
-
if (!face) {
|
|
60
|
-
return props.onError?.({
|
|
61
|
-
name: 'FaceNotFoundError',
|
|
62
|
-
message: 'Please ensure your face is clearly visible in the photo.',
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
45
|
context.mutation.mutate({ file });
|
|
66
46
|
}, disabled: context.field?.disabled ||
|
|
67
47
|
context.query.isLoading ||
|
|
@@ -93,41 +73,31 @@ function Camera() {
|
|
|
93
73
|
const context = useSelfieFieldContext();
|
|
94
74
|
return (_jsx(Dialog.Root, { open: context.disclosure.open, onOpenChange: (details) => {
|
|
95
75
|
context.disclosure.setOpen(details.open);
|
|
96
|
-
}, onExitComplete: context.camera.close, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, { className: "!z-[calc(var(--z-dialog)+1)]" }), _jsx(Dialog.Positioner, { className: "!z-[calc(var(--z-dialog)+2)] flex items-center justify-center overflow-y-auto py-4", children: _jsxs(Dialog.Content, { className: "bg-bg-primary-alt mx-auto w-[calc(100dvw-1rem)] max-w-[calc(100dvw-1rem)] overflow-y-auto rounded-lg px-4 py-5 lg:w-[747px] lg:max-w-[747px] lg:px-3xl lg:py-4xl", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Dialog.Title, { className: "text-center font-semibold lg:text-xl", children: "Take a Selfie Holding Your ID" }), _jsx(Dialog.Description, { className: "text-text-tertiary-600 mt-md text-center text-xs lg:text-base", children: "Make sure your face and ID are clearly visible, with no glare\u00A0or\u00A0blur." }), _jsxs("div", { className: "relative mt-5 lg:mt-10 lg:px-3xl", children: [_jsxs("div", { className:
|
|
97
|
-
!context.camera.error &&
|
|
98
|
-
!context.camera.loading
|
|
99
|
-
? 'block'
|
|
100
|
-
: 'hidden'), children: [_jsx("video", { ...context.camera.videoProps }), _jsx(Mask, {})] }), context.camera.error && (_jsxs("div", { className: "border-border-disabled flex aspect-[85.60/53.98] flex-col items-center justify-center rounded-md border bg-black px-4 lg:rounded-lg", children: [_jsx(CameraOffIcon, { className: "text-text-placeholder-subtle size-10 text-center lg:size-12" }), _jsx("h2", { className: "mt-3 text-sm font-semibold lg:mt-4 lg:text-base", children: context.camera.error.name }), _jsx("p", { className: "text-text-tertiary-600 mt-0.5 text-center text-xs lg:mt-1 lg:text-sm", children: context.camera.error.message }), _jsx(Button, { size: "xs", variant: "outline", colorScheme: "gray", fullWidth: false, className: "mt-4 lg:mt-5", onClick: () => {
|
|
76
|
+
}, closeOnEscape: false, closeOnInteractOutside: false, onExitComplete: context.camera.close, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, { className: "!z-[calc(var(--z-dialog)+1)]" }), _jsx(Dialog.Positioner, { className: "!z-[calc(var(--z-dialog)+2)] flex items-center justify-center overflow-y-auto py-4", children: _jsxs(Dialog.Content, { className: "bg-bg-primary-alt mx-auto w-[calc(100dvw-1rem)] max-w-[calc(100dvw-1rem)] overflow-y-auto rounded-lg px-4 py-5 lg:w-[747px] lg:max-w-[747px] lg:px-3xl lg:py-4xl", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Dialog.Title, { className: "text-center font-semibold lg:text-xl", children: "Take a Selfie Holding Your ID" }), _jsx(Dialog.Description, { className: "text-text-tertiary-600 mt-md text-center text-xs lg:text-base", children: "Make sure your face and ID are clearly visible, with no glare\u00A0or\u00A0blur." }), _jsxs("div", { className: "relative mt-5 lg:mt-10 lg:px-3xl", children: [_jsx(Video, {}), context.camera.error && (_jsxs("div", { className: "border-border-disabled flex aspect-[4/3] flex-col items-center justify-center rounded-md border bg-black px-4 lg:aspect-video", children: [_jsx(CameraOffIcon, { className: "text-text-placeholder-subtle size-10 text-center lg:size-12" }), _jsx("h2", { className: "mt-3 text-sm font-semibold lg:mt-4 lg:text-base", children: context.camera.error.name }), _jsx("p", { className: "text-text-tertiary-600 mt-0.5 text-center text-xs lg:mt-1 lg:text-sm", children: context.camera.error.message }), _jsx(Button, { size: "xs", variant: "outline", colorScheme: "gray", fullWidth: false, className: "mt-4 lg:mt-5", onClick: () => {
|
|
101
77
|
context.camera.reopen();
|
|
102
|
-
}, children: "Try again" })] })), context.camera.loading && (_jsx("div", { className: "border-border-disabled flex aspect-[
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
x: rect.x,
|
|
111
|
-
width: rect.width,
|
|
112
|
-
height: rect.height,
|
|
113
|
-
};
|
|
114
|
-
context.camera.snap({ crop });
|
|
115
|
-
}, disabled: !context.faceFound, children: [_jsx(Camera01Icon, { className: "size-5" }), "Capture"] }) })), context.camera.data && (_jsxs("div", { className: "mt-5 flex gap-3 lg:mt-10", children: [_jsxs(Button, { onClick: () => {
|
|
78
|
+
}, children: "Try again" })] })), context.camera.loading && (_jsx("div", { className: "border-border-disabled flex aspect-[4/3] flex-col items-center justify-center rounded-md border bg-black px-4 lg:aspect-video", children: _jsx(Spinner02Icon, { className: "text-text-placeholder size-8" }) })), context.camera.data && (_jsx("div", { className: "border-border-disabled aspect-[4/3] w-full overflow-hidden rounded-md border bg-black lg:aspect-video", children: _jsx(Image, { src: context.camera.data.url, alt: "", width: 600, height: 600, unoptimized: true, className: "mx-auto mt-2 block h-[calc(100%-1rem)] w-auto" }) }))] }), !context.camera.data && (_jsx("div", { className: "mt-5 lg:mt-10", children: _jsxs(Button, { className: "disabled:bg-bg-disabled disabled:text-text-disabled mx-auto w-full px-xl py-lg lg:w-[10rem]", onClick: () => {
|
|
79
|
+
context.camera.snap({
|
|
80
|
+
crop: context.guideRef.current?.getBoundingClientRect(),
|
|
81
|
+
});
|
|
82
|
+
}, disabled: !context.faceFound ||
|
|
83
|
+
context.camera.loading ||
|
|
84
|
+
context.camera.snapping ||
|
|
85
|
+
!!context.camera.error, children: [context.camera.snapping ? (_jsx(Spinner02Icon, { className: "size-5" })) : (_jsx(Camera01Icon, { className: "size-5" })), "Capture"] }) })), context.camera.data && (_jsxs("div", { className: "mt-5 flex gap-3 lg:mt-10 lg:justify-center", children: [_jsxs(Button, { onClick: () => {
|
|
116
86
|
const file = context.camera.data?.file;
|
|
117
87
|
if (!file)
|
|
118
88
|
return;
|
|
119
89
|
context.mutation.mutate({ file });
|
|
120
90
|
context.disclosure.setOpen(false);
|
|
121
|
-
}, children: [_jsx(CheckCircleIcon, { className: "size-5" }), "Use Photo"] }), _jsxs(Button, { variant: "outline", colorScheme: "gray", onClick: () => {
|
|
91
|
+
}, className: "w-full lg:w-[10rem]", children: [_jsx(CheckCircleIcon, { className: "size-5" }), "Use Photo"] }), _jsxs(Button, { variant: "outline", colorScheme: "gray", onClick: () => {
|
|
122
92
|
context.camera.reset();
|
|
123
|
-
}, children: [_jsx(RefreshCcw01Icon, { className: "size-5" }), " Retake"] })] }))] }) })] }) }));
|
|
93
|
+
}, className: "w-full lg:w-[10rem]", children: [_jsx(RefreshCcw01Icon, { className: "size-5" }), " Retake"] })] }))] }) })] }) }));
|
|
124
94
|
}
|
|
125
95
|
/*
|
|
126
96
|
* -------------------------------------
|
|
127
|
-
*
|
|
97
|
+
* Video
|
|
128
98
|
* -------------------------------------
|
|
129
99
|
*/
|
|
130
|
-
function
|
|
100
|
+
function Video() {
|
|
131
101
|
const id = useId();
|
|
132
102
|
const context = useSelfieFieldContext();
|
|
133
103
|
const [maskBox, setMaskBox] = useState({
|
|
@@ -136,7 +106,7 @@ function Mask() {
|
|
|
136
106
|
width: 0,
|
|
137
107
|
height: 0,
|
|
138
108
|
});
|
|
139
|
-
|
|
109
|
+
useEffect(() => {
|
|
140
110
|
const update = () => {
|
|
141
111
|
const guide = context.guideRef.current;
|
|
142
112
|
const mask = context.maskRef.current;
|
|
@@ -165,12 +135,43 @@ function Mask() {
|
|
|
165
135
|
requestAnimationFrame(update);
|
|
166
136
|
return () => observer.disconnect();
|
|
167
137
|
}, [context.guideRef, context.maskRef]);
|
|
168
|
-
|
|
138
|
+
const [videoSize, setVideoSize] = useState({
|
|
139
|
+
width: 0,
|
|
140
|
+
height: 0,
|
|
141
|
+
});
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
const video = context.camera.videoRef.current;
|
|
144
|
+
if (!video)
|
|
145
|
+
return;
|
|
146
|
+
const updateVideoWidth = () => {
|
|
147
|
+
const rect = video.getBoundingClientRect();
|
|
148
|
+
setVideoSize({
|
|
149
|
+
width: rect.width,
|
|
150
|
+
height: rect.height,
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
video.addEventListener('loadedmetadata', updateVideoWidth);
|
|
154
|
+
video.addEventListener('resize', updateVideoWidth);
|
|
155
|
+
return () => {
|
|
156
|
+
video.removeEventListener('loadedmetadata', updateVideoWidth);
|
|
157
|
+
video.removeEventListener('resize', updateVideoWidth);
|
|
158
|
+
};
|
|
159
|
+
}, [context.camera.videoRef]);
|
|
160
|
+
const guideWidth = videoSize.width <= 0 || videoSize.height <= 0
|
|
161
|
+
? 0
|
|
162
|
+
: videoSize.width > videoSize.height
|
|
163
|
+
? videoSize.height
|
|
164
|
+
: videoSize.width;
|
|
165
|
+
return (_jsxs("div", { className: twMerge('relative h-auto w-full overflow-hidden', !context.camera.data && !context.camera.error && !context.camera.loading
|
|
166
|
+
? 'block'
|
|
167
|
+
: 'hidden'), children: [_jsx("video", { ...context.camera.videoProps }), _jsx("svg", { className: "pointer-events-none absolute -inset-4 size-[200%]", children: _jsx("defs", { children: _jsxs("mask", { id: id, maskUnits: "userSpaceOnUse", children: [_jsx("rect", { x: "0", y: "0", width: "200%", height: "200%", fill: "white" }), _jsx("rect", { x: maskBox.x, y: maskBox.y, width: maskBox.width, height: maskBox.height, fill: "black", rx: "8", ry: "8" })] }) }) }), _jsx("div", { ref: context.maskRef, className: "absolute -inset-4 size-[200%] bg-black/60 backdrop-blur-sm", style: {
|
|
169
168
|
mask: `url(#${id})`,
|
|
170
169
|
WebkitMask: `url(#${id})`,
|
|
171
170
|
maskRepeat: 'no-repeat',
|
|
172
171
|
WebkitMaskRepeat: 'no-repeat',
|
|
173
|
-
} }), _jsx("div", { ref: context.guideRef, className: twMerge('border-utility-warning-700 absolute left-1/2 top-1/2 z-20 aspect-square
|
|
172
|
+
} }), _jsx("div", { ref: context.guideRef, className: twMerge('border-utility-warning-700 absolute left-1/2 top-1/2 z-20 aspect-square -translate-x-1/2 -translate-y-1/2 rounded-md border-2 border-dashed', context.faceFound && 'border-utility-success-700', guideWidth <= 0 ? 'hidden' : 'block'), style: {
|
|
173
|
+
width: `calc(${guideWidth}px - 2rem)`,
|
|
174
|
+
} })] }));
|
|
174
175
|
}
|
|
175
176
|
function useSelfieField(props) {
|
|
176
177
|
const field = useFieldContext();
|
|
@@ -208,17 +209,18 @@ function useSelfieField(props) {
|
|
|
208
209
|
const guideRef = useRef(null);
|
|
209
210
|
const camera = useCamera({
|
|
210
211
|
async validate(data) {
|
|
211
|
-
const face = await getFaceFromFile(data.file);
|
|
212
|
-
console.log(face);
|
|
213
212
|
/*
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
name: 'FaceNotFoundError',
|
|
218
|
-
message: 'Please ensure your face is clearly visible in the photo.',
|
|
219
|
-
};
|
|
220
|
-
}
|
|
213
|
+
* 💁♂️
|
|
214
|
+
* Check if we still have the face.
|
|
215
|
+
* User could have moved away from the camera
|
|
221
216
|
*/
|
|
217
|
+
const ok = await validateFaceFromImage(data.image);
|
|
218
|
+
if (!ok) {
|
|
219
|
+
return {
|
|
220
|
+
name: 'FaceNotFoundError',
|
|
221
|
+
message: 'Please ensure your face is clearly visible in the photo.',
|
|
222
|
+
};
|
|
223
|
+
}
|
|
222
224
|
},
|
|
223
225
|
});
|
|
224
226
|
const disclosure = useDisclosure({
|
|
@@ -230,32 +232,10 @@ function useSelfieField(props) {
|
|
|
230
232
|
});
|
|
231
233
|
const [faceFound, setFaceFound] = useState(false);
|
|
232
234
|
useInterval(async () => {
|
|
233
|
-
if (!camera.videoRef.current)
|
|
235
|
+
if (!camera.videoRef.current || !guideRef.current) {
|
|
234
236
|
return setFaceFound(false);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const guide = guideRef.current;
|
|
238
|
-
const video = camera.videoRef.current;
|
|
239
|
-
const guideRect = guide.getBoundingClientRect();
|
|
240
|
-
const videoRect = video.getBoundingClientRect();
|
|
241
|
-
const face = await getFace(video);
|
|
242
|
-
if (!face)
|
|
243
|
-
return setFaceFound(false);
|
|
244
|
-
/* ⛏️ FIXME */
|
|
245
|
-
const scaleX = videoRect.width / video.videoWidth;
|
|
246
|
-
const scaleY = videoRect.height / video.videoHeight;
|
|
247
|
-
const offsetX = videoRect.left;
|
|
248
|
-
const offsetY = videoRect.top;
|
|
249
|
-
const faceBoxX = offsetX + face.box.xMin * video.videoWidth * scaleX;
|
|
250
|
-
const faceBoxY = offsetY + face.box.yMin * video.videoHeight * scaleY;
|
|
251
|
-
const faceBoxWidth = (face.box.xMax - face.box.xMin) * video.videoWidth * scaleX;
|
|
252
|
-
const faceBoxHeight = (face.box.yMax - face.box.yMin) * video.videoHeight * scaleY;
|
|
253
|
-
const faceIsInsideGuide = faceBoxX >= guideRect.left &&
|
|
254
|
-
faceBoxY >= guideRect.top &&
|
|
255
|
-
faceBoxX + faceBoxWidth <= guideRect.right &&
|
|
256
|
-
faceBoxY + faceBoxHeight <= guideRect.bottom;
|
|
257
|
-
console.log({ faceIsInsideGuide });
|
|
258
|
-
setFaceFound(true);
|
|
237
|
+
}
|
|
238
|
+
setFaceFound(await validateFace(camera.videoRef.current, guideRef.current));
|
|
259
239
|
}, disclosure.open && !camera.data && !camera.error && !camera.loading
|
|
260
240
|
? 1000
|
|
261
241
|
: null);
|
|
@@ -279,44 +259,61 @@ const [SelfieFieldProvider, useSelfieFieldContext] = createContext();
|
|
|
279
259
|
* Utils
|
|
280
260
|
* -------------------------------------
|
|
281
261
|
*/
|
|
282
|
-
let
|
|
283
|
-
async function
|
|
284
|
-
if (
|
|
285
|
-
return
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
async function getFace(video) {
|
|
293
|
-
if (!video || video.readyState < 2)
|
|
294
|
-
return null;
|
|
295
|
-
try {
|
|
296
|
-
const detector = await getFaceDetector();
|
|
297
|
-
const faces = await detector.estimateFaces(video);
|
|
298
|
-
return faces.at(0) ?? null;
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
262
|
+
let __faceapi_models_loaded__ = false;
|
|
263
|
+
async function loadFaceApiModels() {
|
|
264
|
+
if (__faceapi_models_loaded__)
|
|
265
|
+
return;
|
|
266
|
+
await Promise.all([
|
|
267
|
+
faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
|
|
268
|
+
faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
|
|
269
|
+
faceapi.nets.faceRecognitionNet.loadFromUri('/models'),
|
|
270
|
+
]);
|
|
271
|
+
__faceapi_models_loaded__ = true;
|
|
303
272
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return
|
|
321
|
-
}
|
|
273
|
+
const validateFace = cache(async (video, guide) => {
|
|
274
|
+
await loadFaceApiModels();
|
|
275
|
+
const videoRect = video.getBoundingClientRect();
|
|
276
|
+
const guideRect = guide.getBoundingClientRect();
|
|
277
|
+
const canvas = document.createElement('canvas');
|
|
278
|
+
canvas.width = videoRect.width;
|
|
279
|
+
canvas.height = videoRect.height;
|
|
280
|
+
const ctx = canvas.getContext('2d');
|
|
281
|
+
if (!ctx)
|
|
282
|
+
return false;
|
|
283
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
284
|
+
const detection = await faceapi.detectSingleFace(canvas, new faceapi.TinyFaceDetectorOptions({
|
|
285
|
+
inputSize: 224,
|
|
286
|
+
scoreThreshold: 0.75,
|
|
287
|
+
}));
|
|
288
|
+
if (!detection)
|
|
289
|
+
return false;
|
|
290
|
+
const { x, y, width, height } = detection.box;
|
|
291
|
+
const faceBox = {
|
|
292
|
+
left: x,
|
|
293
|
+
right: x + width,
|
|
294
|
+
top: y,
|
|
295
|
+
bottom: y + height,
|
|
296
|
+
};
|
|
297
|
+
const guideBox = {
|
|
298
|
+
left: guideRect.left - videoRect.left,
|
|
299
|
+
right: guideRect.right - videoRect.left,
|
|
300
|
+
top: guideRect.top - videoRect.top,
|
|
301
|
+
bottom: guideRect.bottom - videoRect.top,
|
|
302
|
+
};
|
|
303
|
+
const overlapWidth = Math.max(0, Math.min(faceBox.right, guideBox.right) -
|
|
304
|
+
Math.max(faceBox.left, guideBox.left));
|
|
305
|
+
const overlapHeight = Math.max(0, Math.min(faceBox.bottom, guideBox.bottom) -
|
|
306
|
+
Math.max(faceBox.top, guideBox.top));
|
|
307
|
+
const overlapArea = overlapWidth * overlapHeight;
|
|
308
|
+
const faceArea = width * height;
|
|
309
|
+
const overlapRatio = overlapArea / faceArea;
|
|
310
|
+
return overlapRatio >= 0.6;
|
|
311
|
+
});
|
|
312
|
+
export async function validateFaceFromImage(img) {
|
|
313
|
+
await loadFaceApiModels();
|
|
314
|
+
const detection = await faceapi.detectSingleFace(img, new faceapi.TinyFaceDetectorOptions({
|
|
315
|
+
inputSize: 224,
|
|
316
|
+
scoreThreshold: 0.5,
|
|
317
|
+
}));
|
|
318
|
+
return detection != null;
|
|
322
319
|
}
|
|
@@ -72,16 +72,35 @@ const KnownErrorSchema = z
|
|
|
72
72
|
.object({
|
|
73
73
|
code: z.string().optional(),
|
|
74
74
|
message: z.string().optional(),
|
|
75
|
+
__error: z
|
|
76
|
+
.object({
|
|
77
|
+
name: z
|
|
78
|
+
.union([z.string(), z.object({ code: z.string().optional() })])
|
|
79
|
+
.optional(),
|
|
80
|
+
message: z.string().optional(),
|
|
81
|
+
})
|
|
82
|
+
.optional(),
|
|
75
83
|
})
|
|
76
84
|
.nullable()
|
|
77
85
|
.catch(null)
|
|
78
|
-
.transform((v) =>
|
|
86
|
+
.transform((v) => {
|
|
87
|
+
if (v?.__error) {
|
|
88
|
+
return ((typeof v.__error.name === 'string'
|
|
89
|
+
? v.__error.name
|
|
90
|
+
: v.__error.name?.code) ??
|
|
91
|
+
v.__error.message ??
|
|
92
|
+
v.code ??
|
|
93
|
+
v.message ??
|
|
94
|
+
null);
|
|
95
|
+
}
|
|
96
|
+
return v?.code ?? v?.message ?? null;
|
|
97
|
+
});
|
|
79
98
|
function getErrorMessage(code) {
|
|
80
99
|
switch (code) {
|
|
81
|
-
case '
|
|
82
|
-
return '
|
|
100
|
+
case 'MEMBER_ACCOUNT_BLACKLISTED':
|
|
101
|
+
return 'Your account has been blacklisted due to a serious violation of our policies. For more information or to appeal, please contac Chat Support team.';
|
|
83
102
|
case 'MEMBER_ACCOUNT_SUSPENDED':
|
|
84
|
-
return '
|
|
103
|
+
return 'Your account has been temporarily suspended. Please reach out to the Chat Support team to learn more and get help with resolving the issue.';
|
|
85
104
|
case 'INVALID_RECAPTCHA_RESPONSE':
|
|
86
105
|
return 'Invalid reCAPTCHA response';
|
|
87
106
|
case 'MEMBER_NOT_FOUND':
|
|
@@ -90,6 +109,8 @@ function getErrorMessage(code) {
|
|
|
90
109
|
return 'Rate limit exceeded';
|
|
91
110
|
case 'NOT_READY_TO_SEND_VERIFICATION_ERROR':
|
|
92
111
|
return 'Not ready to send verification code';
|
|
112
|
+
case 'Forbidden':
|
|
113
|
+
return 'Invalid username or password. Please try again.';
|
|
93
114
|
default:
|
|
94
115
|
return 'Something went wrong. Please try again later.';
|
|
95
116
|
}
|