@opexa/portal-components 0.0.242 → 0.0.244

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.
Files changed (70) hide show
  1. package/dist/client/hooks/useCamera.d.ts +6 -1
  2. package/dist/client/hooks/useCamera.js +70 -16
  3. package/dist/client/hooks/useFeatureFlag.d.ts +18 -5
  4. package/dist/client/hooks/useFeatureFlag.js +44 -11
  5. package/dist/components/KYC/AutoOpen.js +8 -3
  6. package/dist/components/PortalProvider/FeatureFlag.d.ts +1 -0
  7. package/dist/components/PortalProvider/FeatureFlag.js +34 -0
  8. package/dist/components/PortalProvider/PortalProvider.js +2 -1
  9. package/dist/components/SignIn/MobileNumberSignIn.js +4 -2
  10. package/dist/components/SignIn/NameAndPasswordSignIn.js +3 -2
  11. package/dist/components/SignUp/SignUpForm.js +1 -2
  12. package/dist/components/shared/IdDocumentField.js +18 -27
  13. package/dist/components/shared/SelfieField.d.ts +1 -3
  14. package/dist/components/shared/SelfieField.js +119 -123
  15. package/dist/constants/StorageKey.d.ts +1 -0
  16. package/dist/constants/StorageKey.js +1 -0
  17. package/dist/services/httpRequest.js +25 -4
  18. package/dist/ui/DatePicker/DatePicker.d.ts +72 -72
  19. package/dist/ui/DatePicker/datePicker.recipe.d.ts +3 -3
  20. package/package.json +9 -13
  21. package/dist/components/KYC/backup/Header.d.ts +0 -1
  22. package/dist/components/KYC/backup/Header.js +0 -8
  23. package/dist/components/KYC/backup/Indicator.d.ts +0 -1
  24. package/dist/components/KYC/backup/Indicator.js +0 -9
  25. package/dist/components/KYC/backup/KYC.d.ts +0 -1
  26. package/dist/components/KYC/backup/KYC.js +0 -14
  27. package/dist/components/KYC/backup/KYC.lazy.d.ts +0 -1
  28. package/dist/components/KYC/backup/KYC.lazy.js +0 -26
  29. package/dist/components/KYC/backup/KYCContext.d.ts +0 -6
  30. package/dist/components/KYC/backup/KYCContext.js +0 -2
  31. package/dist/components/KYC/backup/Step1.d.ts +0 -1
  32. package/dist/components/KYC/backup/Step1.js +0 -13
  33. package/dist/components/KYC/backup/Step2.d.ts +0 -1
  34. package/dist/components/KYC/backup/Step2.js +0 -13
  35. package/dist/components/KYC/backup/Step3.d.ts +0 -1
  36. package/dist/components/KYC/backup/Step3.js +0 -13
  37. package/dist/components/KYC/backup/Step4.d.ts +0 -1
  38. package/dist/components/KYC/backup/Step4.js +0 -7
  39. package/dist/components/KYC/backup/useKYC.d.ts +0 -10
  40. package/dist/components/KYC/backup/useKYC.js +0 -8
  41. package/dist/components/KYC/loadModels.d.ts +0 -1
  42. package/dist/components/KYC/loadModels.js +0 -9
  43. package/dist/components/Messages/Message.d.ts +0 -1
  44. package/dist/components/Messages/Message.js +0 -35
  45. package/dist/components/Messages/MessageContext.d.ts +0 -6
  46. package/dist/components/Messages/MessageContext.js +0 -2
  47. package/dist/components/Messages/MessagePopup.d.ts +0 -1
  48. package/dist/components/Messages/MessagePopup.js +0 -20
  49. package/dist/components/Messages/MessageTrigger.d.ts +0 -8
  50. package/dist/components/Messages/MessageTrigger.js +0 -19
  51. package/dist/components/Quests/CountdownTimer.d.ts +0 -15
  52. package/dist/components/Quests/CountdownTimer.js +0 -33
  53. package/dist/components/Quests/DailyCheckInQuest/DailyCheckInQuest.d.ts +0 -4
  54. package/dist/components/Quests/DailyCheckInQuest/DailyCheckInQuest.js +0 -78
  55. package/dist/components/Quests/DailyCheckInQuest/DailyCheckInQuestModal.d.ts +0 -8
  56. package/dist/components/Quests/DailyCheckInQuest/DailyCheckInQuestModal.js +0 -9
  57. package/dist/components/Quests/OnboardingQuest/OnboardingQuest.d.ts +0 -4
  58. package/dist/components/Quests/OnboardingQuest/OnboardingQuest.js +0 -4
  59. package/dist/components/Quests/WageringQuest/WageringQuest.d.ts +0 -4
  60. package/dist/components/Quests/WageringQuest/WageringQuest.js +0 -20
  61. package/dist/components/Quests/WageringQuest/WageringQuestModal.d.ts +0 -9
  62. package/dist/components/Quests/WageringQuest/WageringQuestModal.js +0 -9
  63. package/dist/constants/BranchCode.d.ts +0 -4
  64. package/dist/constants/BranchCode.js +0 -42
  65. package/dist/icons/BellRingIcon.d.ts +0 -2
  66. package/dist/icons/BellRingIcon.js +0 -4
  67. package/dist/utils/dataUrlToBlob.d.ts +0 -1
  68. package/dist/utils/dataUrlToBlob.js +0 -11
  69. package/dist/utils/resizeImageSize.d.ts +0 -2
  70. package/dist/utils/resizeImageSize.js +0 -11
@@ -7,6 +7,7 @@ export interface CameraError<T extends string = never> {
7
7
  export interface CameraData {
8
8
  url: string;
9
9
  file: File;
10
+ image: HTMLImageElement;
10
11
  }
11
12
  export interface CameraCropPoints {
12
13
  x: number;
@@ -29,11 +30,15 @@ export interface UseCameraReturn<T extends string = never> {
29
30
  data: CameraData | null;
30
31
  error: CameraError<T> | null;
31
32
  loading: boolean;
33
+ snapping: boolean;
32
34
  videoRef: React.RefObject<HTMLVideoElement | null>;
33
35
  videoProps: ComponentPropsWithRef<'video'>;
34
36
  }
37
+ export interface CameraTransformData extends CameraData {
38
+ canvas: HTMLCanvasElement;
39
+ }
35
40
  export interface UseCameraOptions<T extends string = never> extends MediaStreamConstraints {
36
- transform?(data: CameraData): CameraData | Promise<CameraData>;
41
+ transform?(data: CameraTransformData): CameraData | Promise<CameraData>;
37
42
  validate?(data: CameraData): CameraError<T> | void | null | undefined | Promise<CameraError<T> | void | null | undefined>;
38
43
  }
39
44
  export declare function useCamera<T extends string = never>(options?: UseCameraOptions<T>): UseCameraReturn<T>;
@@ -1,13 +1,16 @@
1
1
  import { isBoolean, isFunction, isObject } from 'lodash-es';
2
- import { useCallback, useMemo, useRef, useState, } from 'react';
2
+ import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
3
3
  import invariant from 'tiny-invariant';
4
+ import { useMediaQuery } from 'usehooks-ts';
4
5
  export function useCamera(options = {}) {
5
6
  const { transform, validate, ...constraints } = options;
6
7
  const videoRef = useRef(null);
7
- const stream = useRef(null);
8
+ const streamRef = useRef(null);
8
9
  const [data, setData] = useState(null);
9
10
  const [error, setError] = useState(null);
10
11
  const [loading, setLoading] = useState(false);
12
+ const [snapping, setSnapping] = useState(false);
13
+ const desktop = useMediaQuery('(min-width: 1024px)');
11
14
  const [mirror, setMirror] = useState(false);
12
15
  const open = useCallback(async () => {
13
16
  setData(null);
@@ -31,16 +34,24 @@ export function useCamera(options = {}) {
31
34
  video: {
32
35
  facingMode: 'user',
33
36
  noiseSuppression: true,
37
+ width: {
38
+ ideal: 9999,
39
+ },
40
+ height: {
41
+ ideal: 9999,
42
+ },
43
+ aspectRatio: {
44
+ exact: desktop ? 16 / 9 : 4 / 3,
45
+ },
34
46
  frameRate: {
35
- min: 15,
36
- max: 60,
37
- ideal: 30,
47
+ max: 120,
48
+ ideal: 90,
38
49
  },
39
50
  ...(isBoolean(constraints.video) ? {} : constraints.video),
40
51
  },
41
52
  });
42
53
  node.srcObject = result;
43
- stream.current = result;
54
+ streamRef.current = result;
44
55
  setMirror(!isObject(constraints.video)
45
56
  ? true
46
57
  : constraints.video.facingMode === 'environment'
@@ -74,12 +85,14 @@ export function useCamera(options = {}) {
74
85
  finally {
75
86
  setLoading(false);
76
87
  }
77
- }, [constraints, setData, setError]);
88
+ }, [constraints, desktop]);
78
89
  const close = useCallback(() => {
79
90
  setData(null);
80
91
  setError(null);
92
+ setLoading(false);
93
+ setSnapping(false);
81
94
  return new Promise((resolve) => {
82
- stream.current?.getTracks().forEach((track) => track.stop());
95
+ streamRef.current?.getTracks().forEach((track) => track.stop());
83
96
  videoRef.current?.pause();
84
97
  resolve();
85
98
  });
@@ -87,6 +100,7 @@ export function useCamera(options = {}) {
87
100
  const snap = useCallback(async (opts = {}) => {
88
101
  setData(null);
89
102
  setError(null);
103
+ setSnapping(true);
90
104
  const video = videoRef.current;
91
105
  const canvas = document.createElement('canvas');
92
106
  const context = canvas.getContext('2d');
@@ -98,12 +112,18 @@ export function useCamera(options = {}) {
98
112
  const hdResolution = video.videoWidth >= 1280 && video.videoHeight >= 720;
99
113
  const highResolution = video.videoWidth >= 1920 && video.videoHeight >= 1080;
100
114
  const scaleFactor = lowResolution
101
- ? (opts.scale ?? 2)
115
+ ? (opts.scale ?? (opts.crop ? 2.15 : 2))
102
116
  : highResolution
103
- ? 1
117
+ ? opts.crop
118
+ ? 1.25
119
+ : 1.15
104
120
  : hdResolution
105
- ? 1.15
106
- : 1.25;
121
+ ? opts.crop
122
+ ? 1.5
123
+ : 1.25
124
+ : opts.crop
125
+ ? 1.5
126
+ : 2;
107
127
  let x = 0;
108
128
  let y = 0;
109
129
  let w = video.videoWidth;
@@ -127,6 +147,7 @@ export function useCamera(options = {}) {
127
147
  context.drawImage(video, x, y, w, h, 0, 0, w * scaleFactor, h * scaleFactor);
128
148
  canvas.toBlob(async (blob) => {
129
149
  if (!blob) {
150
+ setSnapping(false);
130
151
  return setError({
131
152
  name: 'CameraError',
132
153
  message: "'canvas.toBlob' failed to create blob",
@@ -138,21 +159,46 @@ export function useCamera(options = {}) {
138
159
  endings: 'native',
139
160
  lastModified: Date.now(),
140
161
  });
141
- const data = { url, file };
162
+ const image = new Image();
163
+ image.src = url;
164
+ image.alt = '';
165
+ image.width = canvas.width;
166
+ image.height = canvas.height;
167
+ if (!image.complete || image.naturalWidth === 0) {
168
+ await new Promise((resolve, reject) => {
169
+ image.onload = () => resolve();
170
+ image.onerror = () => reject();
171
+ });
172
+ }
173
+ const data = isFunction(transform)
174
+ ? await transform({
175
+ url,
176
+ file,
177
+ image,
178
+ canvas,
179
+ })
180
+ : {
181
+ url,
182
+ file,
183
+ image,
184
+ };
142
185
  const error = await validate?.(data);
143
186
  if (error)
144
187
  setError(error);
145
188
  else
146
- setData(isFunction(transform) ? await transform(data) : data);
189
+ setData(data);
190
+ setSnapping(false);
147
191
  }, 'image/jpeg', 1);
148
192
  }, [mirror, transform, validate]);
149
193
  const reset = useCallback(() => {
150
194
  setData(null);
151
195
  setError(null);
152
196
  setLoading(false);
197
+ setSnapping(false);
153
198
  return Promise.resolve();
154
199
  }, [setData, setError]);
155
200
  const reopen = useCallback(async () => {
201
+ setSnapping(false);
156
202
  setLoading(true);
157
203
  close();
158
204
  await sleep();
@@ -171,11 +217,18 @@ export function useCamera(options = {}) {
171
217
  position: 'relative',
172
218
  boxSizing: 'border-box',
173
219
  background: 'transparent',
174
- objectFit: 'cover',
175
- objectPosition: 'center',
176
220
  transform: mirror ? 'scaleX(-1)' : 'none',
221
+ pointerEvents: 'none',
177
222
  },
178
223
  }), [mirror]);
224
+ useEffect(() => {
225
+ return () => {
226
+ setData(null);
227
+ setError(null);
228
+ setLoading(false);
229
+ setSnapping(false);
230
+ };
231
+ }, []);
179
232
  return {
180
233
  snap,
181
234
  open,
@@ -185,6 +238,7 @@ export function useCamera(options = {}) {
185
238
  data,
186
239
  error,
187
240
  loading,
241
+ snapping,
188
242
  videoRef,
189
243
  videoProps,
190
244
  };
@@ -1,5 +1,18 @@
1
- export declare const FEATURE_FLAG_KEY = "FEATURE_FLAG_ENABLED";
2
- export declare const useFeatureFlag: () => {
3
- featureFlagEnabled: boolean;
4
- setFeatureFlagEnabled: import("react").Dispatch<import("react").SetStateAction<boolean>>;
5
- };
1
+ export interface UseFeatureFlagReturn {
2
+ enabled: boolean;
3
+ enable(): Promise<void>;
4
+ disable(): Promise<void>;
5
+ }
6
+ export declare function useFeatureFlag(): UseFeatureFlagReturn;
7
+ interface EventsMap {
8
+ 'web-portal::feature-flag-enabled': CustomEvent;
9
+ 'web-portal::feature-flag-disabled': CustomEvent;
10
+ }
11
+ type Listener<K extends keyof EventsMap> = (this: Window, event: EventsMap[K]) => void;
12
+ declare global {
13
+ interface Window {
14
+ addEventListener<T extends keyof EventsMap>(type: T, listener: Listener<T>): void;
15
+ removeEventListener<K extends keyof EventsMap>(type: K, listener: Listener<K>): void;
16
+ }
17
+ }
18
+ export {};
@@ -1,16 +1,49 @@
1
- 'use client';
2
- import { useEffect, useState } from 'react';
3
- export const FEATURE_FLAG_KEY = 'FEATURE_FLAG_ENABLED';
4
- export const useFeatureFlag = () => {
5
- const [featureFlagEnabled, setFeatureFlagEnabled] = useState(false);
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { FEATURE_FLAG_LOCAL_STORAGE_KEY } from '../../constants/index.js';
3
+ export function useFeatureFlag() {
4
+ const [enabled, setEnabled] = useState(false);
5
+ const enable = useCallback(() => {
6
+ return new Promise((resolve) => {
7
+ localStorage.setItem(FEATURE_FLAG_LOCAL_STORAGE_KEY, '');
8
+ window.dispatchEvent(FEATURE_FLAG_ENABLED_EVENT);
9
+ resolve();
10
+ });
11
+ }, []);
12
+ const disable = useCallback(() => {
13
+ return new Promise((resolve) => {
14
+ localStorage.removeItem(FEATURE_FLAG_LOCAL_STORAGE_KEY);
15
+ window.dispatchEvent(FEATURE_FLAG_DISABLED_EVENT);
16
+ resolve();
17
+ });
18
+ }, []);
6
19
  useEffect(() => {
7
- const storedFlag = localStorage.getItem(FEATURE_FLAG_KEY);
8
- if (storedFlag !== null) {
9
- setFeatureFlagEnabled(storedFlag === 'true');
20
+ setEnabled(localStorage.getItem(FEATURE_FLAG_LOCAL_STORAGE_KEY) != null);
21
+ }, []);
22
+ useEffect(() => {
23
+ function handleEnabled() {
24
+ setEnabled(true);
25
+ }
26
+ function handleDisabled() {
27
+ setEnabled(false);
10
28
  }
29
+ window.addEventListener('web-portal::feature-flag-enabled', handleEnabled);
30
+ window.addEventListener('web-portal::feature-flag-disabled', handleDisabled);
31
+ return () => {
32
+ window.removeEventListener('web-portal::feature-flag-enabled', handleEnabled);
33
+ window.removeEventListener('web-portal::feature-flag-disabled', handleDisabled);
34
+ };
11
35
  }, []);
12
36
  return {
13
- featureFlagEnabled,
14
- setFeatureFlagEnabled,
37
+ enabled,
38
+ enable,
39
+ disable,
15
40
  };
16
- };
41
+ }
42
+ const FEATURE_FLAG_DISABLED_EVENT = new CustomEvent('web-portal::feature-flag-disabled', {
43
+ bubbles: true,
44
+ cancelable: false,
45
+ });
46
+ const FEATURE_FLAG_ENABLED_EVENT = new CustomEvent('web-portal::feature-flag-enabled', {
47
+ bubbles: true,
48
+ cancelable: false,
49
+ });
@@ -22,12 +22,17 @@ export function AutoOpen() {
22
22
  : isUploadCompleted
23
23
  ? 3
24
24
  : 2;
25
- const shouldOpen = !isBasicInfoCompleted || !isUploadCompleted || isRejected;
25
+ const shouldOpen = !account?.realName ||
26
+ !account?.birthDay ||
27
+ !verification ||
28
+ verification.status === 'REJECTED' ||
29
+ verification.status === 'UNVERIFIED';
26
30
  useEffect(() => {
27
31
  if (session?.status == 'authenticated' &&
32
+ !kycStore['~touched'] &&
28
33
  shouldOpen &&
29
- localStorage.getItem(KYC_PROMPT_LOCALSTORAGE_KEY) === null) {
30
- kycStore.setKycReminder(true);
34
+ localStorage.getItem(KYC_PROMPT_LOCALSTORAGE_KEY) == null) {
35
+ kycStore.setOpen(true);
31
36
  return;
32
37
  }
33
38
  }, [kycStore, session?.status, shouldOpen]);
@@ -0,0 +1 @@
1
+ export declare function FeatureFlag(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { usePathname, useRouter, useSearchParams } from 'next/navigation';
4
+ import { useCallback, useEffect } from 'react';
5
+ import { useFeatureFlag } from '../../client/hooks/useFeatureFlag.js';
6
+ import { Portal } from '../../ui/Portal/index.js';
7
+ import { Presence } from '../../ui/Presence/index.js';
8
+ export function FeatureFlag() {
9
+ const router = useRouter();
10
+ const pathname = usePathname();
11
+ const searchParams = useSearchParams();
12
+ const featureFlag = useFeatureFlag();
13
+ useEffect(() => {
14
+ if (searchParams.has('future')) {
15
+ featureFlag.enable();
16
+ }
17
+ }, [featureFlag, searchParams]);
18
+ const removeFutureParam = useCallback(() => {
19
+ return new Promise((resolve) => {
20
+ if (!searchParams.has('future'))
21
+ return resolve();
22
+ const newSearchParams = new URLSearchParams(searchParams);
23
+ newSearchParams.delete('future');
24
+ router.replace(`${pathname}?${newSearchParams.toString()}`, {
25
+ scroll: false,
26
+ });
27
+ setTimeout(resolve, 250);
28
+ });
29
+ }, [pathname, router, searchParams]);
30
+ return (_jsx(Portal, { children: _jsx(Presence, { present: featureFlag.enabled, asChild: true, children: _jsx("div", { className: "fixed bottom-3 flex w-full flex-col items-center justify-center", children: _jsxs("div", { className: "rounded-full bg-black/50 px-2 py-1 text-sm leading-none backdrop-blur-md", children: ["Experimental features are enabled.", ' ', _jsx("button", { type: "button", className: "cursor-pointer underline underline-offset-2", onClick: async () => {
31
+ await removeFutureParam();
32
+ await featureFlag.disable();
33
+ }, children: "Disable" })] }) }) }) }));
34
+ }
@@ -9,6 +9,7 @@ import { prefetchSessionQuery } from '../../server/utils/prefetchSessionQuery.js
9
9
  import { prefetchWalletQuery } from '../../server/utils/prefetchWalletQuery.js';
10
10
  import { FacebookPixel, GoogleRecaptcha } from '../../third-parties/index.js';
11
11
  import { getQueryClient } from '../../utils/getQueryClient.js';
12
+ import { FeatureFlag } from './FeatureFlag.js';
12
13
  import { Providers } from './Providers.js';
13
14
  import { SetupDomain } from './SetupDomain.js';
14
15
  import { Snackbar } from './Snackbar.js';
@@ -21,5 +22,5 @@ export async function PortalProvider({ children, facebookPixelId, googleAnalytic
21
22
  prefetchAccountQuery(),
22
23
  prefetchWalletQuery(),
23
24
  ]);
24
- return (_jsxs(_Fragment, { children: [INTERNALS__REPORT_WEB_VITALS && _jsx(WebVitals, {}), _jsx(Providers, { children: _jsxs(HydrationBoundary, { state: dehydrate(queryClient), children: [children, _jsx(Suspense, { fallback: null, children: _jsx(SetupDomain, {}) })] }) }), _jsx(Snackbar, {}), facebookPixelId && _jsx(FacebookPixel, { fbId: facebookPixelId }), googleAnalyticsId && _jsx(GoogleAnalytics, { gaId: googleAnalyticsId }), googleTagManagerId && _jsx(GoogleTagManager, { gtmId: googleTagManagerId }), googleRecaptchaSiteKey && (_jsx(GoogleRecaptcha, { siteKey: googleRecaptchaSiteKey }))] }));
25
+ return (_jsxs(_Fragment, { children: [INTERNALS__REPORT_WEB_VITALS && _jsx(WebVitals, {}), _jsx(Providers, { children: _jsxs(HydrationBoundary, { state: dehydrate(queryClient), children: [children, _jsx(Suspense, { fallback: null, children: _jsx(SetupDomain, {}) })] }) }), _jsx(Snackbar, {}), _jsx(Suspense, { fallback: null, children: _jsx(FeatureFlag, {}) }), facebookPixelId && _jsx(FacebookPixel, { fbId: facebookPixelId }), googleAnalyticsId && _jsx(GoogleAnalytics, { gaId: googleAnalyticsId }), googleTagManagerId && _jsx(GoogleTagManager, { gtmId: googleTagManagerId }), googleRecaptchaSiteKey && (_jsx(GoogleRecaptcha, { siteKey: googleRecaptchaSiteKey }))] }));
25
26
  }
@@ -121,9 +121,11 @@ export function MobileNumberSignIn() {
121
121
  globalStore.signIn.setOpen(false);
122
122
  globalStore.kycReminder.setOpen(true);
123
123
  }
124
- catch {
124
+ catch (err) {
125
125
  toaster.error({
126
- description: 'Invalid Verification Code',
126
+ description: err instanceof Error
127
+ ? err.message
128
+ : 'Invalid Verification Code',
127
129
  });
128
130
  }
129
131
  }), children: [_jsx(Controller, { name: "verificationCode", control: step2Form.control, render: (o) => (_jsxs(Field.Root, { invalid: o.fieldState.invalid, children: [_jsxs(PinInput.Root, { placeholder: "0", value: o.field.value, onValueChange: (details) => {
@@ -41,9 +41,10 @@ export function NameAndPasswordSignIn() {
41
41
  form.reset();
42
42
  }
43
43
  },
44
- onError() {
44
+ onError(err) {
45
45
  toaster.error({
46
- description: 'Invalid username or password',
46
+ description: err.message ||
47
+ 'An error occurred while signing in. Please try again.',
47
48
  });
48
49
  },
49
50
  });
@@ -192,7 +192,6 @@ export function SignUpForm() {
192
192
  mobileNumber: mobileNumberParser.format(mobileNumber),
193
193
  verificationCode: verificationCode.join(''),
194
194
  referralCode: search.get('referralCode') ?? undefined,
195
- domain: search.get('domain') ?? undefined,
196
195
  btag: search.get('btag') ?? undefined,
197
196
  });
198
197
  const name = mobileNumberParser.format(mobileNumber);
@@ -252,7 +251,7 @@ export function SignUpForm() {
252
251
  o.field.onChange(details.value);
253
252
  }, onValueComplete: () => {
254
253
  form2Ref.current?.requestSubmit();
255
- }, children: [_jsxs(PinInput.Control, { className: "grid-cols-[1fr_1fr_1fr_auto_1fr_1fr_1fr] items-center gap-md", children: [_jsx(PinInput.Input, { index: 0 }), _jsx(PinInput.Input, { index: 1 }), _jsx(PinInput.Input, { index: 2 }), _jsx("span", { className: "text-text-placeholder-subtle text-2xl font-medium", children: "\u2013" }), _jsx(PinInput.Input, { index: 3 }), _jsx(PinInput.Input, { index: 4 }), _jsx(PinInput.Input, { index: 5 })] }), _jsx(PinInput.HiddenInput, {})] }), _jsx(Field.ErrorText, { children: o.formState.errors.verificationCode?.message })] })) }), _jsx(Button, { type: "submit", className: "mt-4xl", disabled: step2Form.formState.isSubmitting, children: "Verify" }), _jsxs("div", { className: "mt-3 flex w-full items-center justify-center gap-xs text-sm", children: [_jsx("span", { className: "text-text-secondary-700", children: "Didn't recieve the code?" }), _jsx("button", { type: "button", className: "text-button-secondary-fg font-semibold disabled:cursor-not-allowed disabled:opacity-75", disabled: cooldown.cooling, onClick: async () => {
254
+ }, blurOnComplete: true, readOnly: step2Form.formState.isSubmitting, type: "numeric", children: [_jsxs(PinInput.Control, { className: "grid-cols-[1fr_1fr_1fr_auto_1fr_1fr_1fr] items-center gap-md", children: [_jsx(PinInput.Input, { index: 0 }), _jsx(PinInput.Input, { index: 1 }), _jsx(PinInput.Input, { index: 2 }), _jsx("span", { className: "text-text-placeholder-subtle text-2xl font-medium", children: "\u2013" }), _jsx(PinInput.Input, { index: 3 }), _jsx(PinInput.Input, { index: 4 }), _jsx(PinInput.Input, { index: 5 })] }), _jsx(PinInput.HiddenInput, {})] }), _jsx(Field.ErrorText, { children: o.formState.errors.verificationCode?.message })] })) }), _jsx(Button, { type: "submit", className: "mt-4xl", disabled: step2Form.formState.isSubmitting, children: "Verify" }), _jsxs("div", { className: "mt-3 flex w-full items-center justify-center gap-xs text-sm", children: [_jsx("span", { className: "text-text-secondary-700", children: "Didn't recieve the code?" }), _jsx("button", { type: "button", className: "text-button-secondary-fg font-semibold disabled:cursor-not-allowed disabled:opacity-75", disabled: cooldown.cooling, onClick: async () => {
256
255
  await sendVerificationCodeMutation.mutateAsync({
257
256
  channel: 'SMS',
258
257
  recipient: mobileNumberParser.format(step1Form.getValues('mobileNumber')),
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useFieldContext } from '@ark-ui/react';
4
4
  import Image from 'next/image';
5
- import { useId, useLayoutEffect, useRef, useState, } from 'react';
5
+ import { useEffect, useId, useRef, useState, } from 'react';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { useMediaQuery } from 'usehooks-ts';
8
8
  import { useCamera } from '../../client/hooks/useCamera.js';
@@ -72,41 +72,30 @@ function Camera() {
72
72
  const context = useIdDocumentFieldContext();
73
73
  return (_jsx(Dialog.Root, { open: context.disclosure.open, onOpenChange: (details) => {
74
74
  context.disclosure.setOpen(details.open);
75
- }, 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 Picture of Your Front ID" }), _jsxs(Dialog.Description, { className: "text-text-tertiary-600 mt-md text-center text-xs lg:text-base", children: ["Make sure your ID is clearly visible, well-lit, and not blurry.", ' ', _jsx("br", { className: "hidden lg:block" }), "Avoid glare or reflections, and ensure all corners are within\u00A0the\u00A0frame."] }), _jsxs("div", { className: "relative mt-5 lg:mt-10 lg:px-3xl", children: [_jsxs("div", { className: twMerge('border-border-disabled relative w-full overflow-hidden rounded-md border bg-black lg:rounded-lg', !context.camera.data &&
76
- !context.camera.error &&
77
- !context.camera.loading
78
- ? 'block'
79
- : '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: () => {
75
+ }, 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 Picture of Your Front ID" }), _jsxs(Dialog.Description, { className: "text-text-tertiary-600 mt-md text-center text-xs lg:text-base", children: ["Make sure your ID is clearly visible, well-lit, and not blurry.", ' ', _jsx("br", { className: "hidden lg:block" }), "Avoid glare or reflections, and ensure all corners are within\u00A0the\u00A0frame."] }), _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: () => {
80
76
  context.camera.reopen();
81
- }, children: "Try again" })] })), context.camera.loading && (_jsx("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(Spinner02Icon, { className: "text-text-placeholder size-8" }) })), context.camera.data && (_jsx("div", { className: "border-border-disabled h-auto w-full overflow-hidden rounded-md border bg-black lg:rounded-lg", children: _jsx(Image, { src: context.camera.data.url, alt: "", width: 600, height: 378.08, unoptimized: true, className: "block h-auto w-full" }) }))] }), !context.camera.data &&
82
- !context.camera.error &&
83
- !context.camera.loading && (_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 lg:w-auto", onClick: () => {
84
- const rect = context.guideRef.current?.getBoundingClientRect();
85
- const crop = !rect
86
- ? undefined
87
- : {
88
- y: rect.y,
89
- x: rect.x,
90
- width: rect.width,
91
- height: rect.height,
92
- };
93
- context.camera.snap({ crop });
94
- }, 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: () => {
77
+ }, 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: 378.08, 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: () => {
78
+ context.camera.snap({
79
+ crop: context.guideRef.current?.getBoundingClientRect(),
80
+ });
81
+ }, disabled: context.camera.snapping ||
82
+ context.camera.loading ||
83
+ !!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: () => {
95
84
  const file = context.camera.data?.file;
96
85
  if (!file)
97
86
  return;
98
87
  context.mutation.mutate({ file });
99
88
  context.disclosure.setOpen(false);
100
- }, children: [_jsx(CheckCircleIcon, { className: "size-5" }), "Use Photo"] }), _jsxs(Button, { variant: "outline", colorScheme: "gray", onClick: () => {
89
+ }, className: "w-full lg:w-[10rem]", children: [_jsx(CheckCircleIcon, { className: "size-5" }), "Use Photo"] }), _jsxs(Button, { variant: "outline", colorScheme: "gray", onClick: () => {
101
90
  context.camera.reset();
102
- }, children: [_jsx(RefreshCcw01Icon, { className: "size-5" }), " Retake"] })] }))] }) })] }) }));
91
+ }, className: "w-full lg:w-[10rem]", children: [_jsx(RefreshCcw01Icon, { className: "size-5" }), " Retake"] })] }))] }) })] }) }));
103
92
  }
104
93
  /**
105
94
  * -------------------------------------
106
- * Mask
95
+ * Video
107
96
  * -------------------------------------
108
97
  */
109
- function Mask() {
98
+ function Video() {
110
99
  const id = useId();
111
100
  const context = useIdDocumentFieldContext();
112
101
  const [maskBox, setMaskBox] = useState({
@@ -115,7 +104,7 @@ function Mask() {
115
104
  width: 0,
116
105
  height: 0,
117
106
  });
118
- useLayoutEffect(() => {
107
+ useEffect(() => {
119
108
  const update = () => {
120
109
  const guide = context.guideRef.current;
121
110
  const mask = context.maskRef.current;
@@ -144,7 +133,9 @@ function Mask() {
144
133
  requestAnimationFrame(update);
145
134
  return () => observer.disconnect();
146
135
  }, [context.guideRef, context.maskRef]);
147
- return (_jsxs(_Fragment, { children: [_jsx("svg", { className: "pointer-events-none absolute h-full w-full", children: _jsx("defs", { children: _jsxs("mask", { id: id, maskUnits: "userSpaceOnUse", children: [_jsx("rect", { x: "0", y: "0", width: "100%", height: "100%", 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-0 bg-black/60 backdrop-blur-sm", style: {
136
+ return (_jsxs("div", { className: twMerge('relative h-auto w-full overflow-hidden', !context.camera.data && !context.camera.error && !context.camera.loading
137
+ ? 'block'
138
+ : '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: {
148
139
  mask: `url(#${id})`,
149
140
  WebkitMask: `url(#${id})`,
150
141
  maskRepeat: 'no-repeat',
@@ -1,6 +1,3 @@
1
- import '@mediapipe/face_detection';
2
- import '@tensorflow/tfjs-backend-webgl';
3
- import '@tensorflow/tfjs-core';
4
1
  import { type CSSProperties, type RefObject } from 'react';
5
2
  import { type ValidateFileError } from '../../utils/validateFile';
6
3
  export interface SelfieFieldProps extends UseSelfieFieldProps {
@@ -20,4 +17,5 @@ interface UseSelfieFieldProps {
20
17
  onChange?: (value: string) => void;
21
18
  defaultValue?: string;
22
19
  }
20
+ export declare function validateFaceFromImage(img: HTMLImageElement): Promise<boolean>;
23
21
  export {};