@opexa/portal-components 0.0.171 → 0.0.173

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.
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as faceapi from 'face-api.js';
4
4
  import { useEffect, useRef, useState } from 'react';
5
+ import { twMerge } from 'tailwind-merge';
5
6
  import { useUploadImageFileMutation } from '../../client/hooks/useUploadImageFileMutation.js';
6
7
  import { ArrowLeftIcon } from '../../icons/ArrowLeftIcon.js';
7
8
  import { Camera01Icon } from '../../icons/Camera01Icon.js';
@@ -178,9 +179,15 @@ export function CaptureSelfie() {
178
179
  videoStream.getTracks().forEach((track) => track.stop());
179
180
  setVideoStream(null);
180
181
  }
181
- if (videoRef.current) {
182
+ if (videoRef.current && videoRef.current.srcObject) {
183
+ const stream = videoRef.current.srcObject;
184
+ stream.getTracks().forEach((track) => {
185
+ track.stop();
186
+ });
182
187
  videoRef.current.srcObject = null;
183
188
  videoRef.current.pause();
189
+ videoRef.current.currentTime = 0;
190
+ videoRef.current.load();
184
191
  }
185
192
  setCountdown(3);
186
193
  }
@@ -247,5 +254,5 @@ export function CaptureSelfie() {
247
254
  return (_jsxs("div", { className: "w-full p-4 lg:w-[640px] lg:px-12 lg:py-4xl", children: [_jsxs("button", { type: "button", className: "flex items-center gap-lg lg:absolute lg:right-5 lg:top-5 lg:p-md", onClick: () => {
248
255
  stopCamera();
249
256
  kyc.setCapturing(null);
250
- }, children: [_jsx(XIcon, { className: "text-text-senary-300 hidden size-5 lg:block" }), _jsx(ArrowLeftIcon, { className: "text-text-senary-300 size-5 lg:hidden" }), _jsx("span", { className: "text-lg font-semibold lg:hidden", children: "Back" })] }), _jsx(Dialog.Title, { className: "mt-6 text-center text-lg font-semibold lg:mt-0", children: "Take a Selfie Holding Your ID" }), _jsx(Dialog.Description, { className: "text-text-tertiary-600 mx-auto mt-sm max-w-[19rem] text-center text-sm lg:max-w-full", children: "Make sure your face and ID are clearly visible, with no glare or blur." }), _jsx("div", { id: "parent", className: "border-border-disabled mt-6 aspect-video rounded-xl border bg-black lg:mt-8", children: captureInProgress ? (_jsxs("div", { className: "relative flex h-fit w-full flex-col items-center justify-center", children: [_jsx("video", { ref: videoRef, autoPlay: true, muted: true, className: "h-[60vh] w-full rounded-lg object-cover md:aspect-video md:h-[333px]", width: "100%", height: "100%" }), _jsx("canvas", { ref: faceCanvasRef, className: "absolute inset-0 h-[60vh] w-full object-cover md:aspect-video md:h-[333px] md:object-contain", width: "100%", height: "100%" }), isCapturing && (_jsx("span", { className: "absolute inset-x-0 bottom-8 flex items-center justify-center text-5xl font-bold text-[#713B12CC] md:bottom-4", children: countdown })), !isFaceCentered && (_jsx("span", { className: "absolute inset-x-0 bottom-12 mx-auto w-1/2 text-center text-xs text-white md:bottom-3", children: "Please center your face in the camera view. Make sure your ID is clearly visible next to your face." })), _jsx(PersonOverlayDesktop, { isFaceCentered: isFaceCentered, width: videoRef.current?.clientWidth || 543, height: videoRef.current?.clientHeight || 333 })] })) : (selectedImage && (_jsx("img", { src: selectedImage, alt: "Captured Photo", className: "h-full w-full rounded-lg object-cover md:aspect-video md:h-[333px]" }))) }), selectedImage && (_jsxs("div", { className: "flex flex-col items-center justify-center gap-2 md:flex-row", children: [_jsxs(Button, { className: "disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-4 px-xl disabled:opacity-100", size: "sm", onClick: retakePhoto, type: "button", children: ["Rekate Photo", _jsx(Camera01Icon, { className: "size-5" })] }), _jsxs(Button, { className: "disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-4 px-xl disabled:opacity-100", size: "sm", onClick: usePhoto, type: "button", disabled: isPending, children: ["Use Photo", _jsx(Camera01Icon, { className: "size-5" })] })] })), !selectedImage && (_jsxs(Button, { className: "mx-auto mt-6 px-xl lg:mt-8", onClick: startCamera, disabled: !modelsLoaded, type: "button", children: ["Start Camera", _jsx(Camera01Icon, { className: "size-5" })] }))] }));
257
+ }, children: [_jsx(XIcon, { className: "text-text-senary-300 hidden size-5 lg:block" }), _jsx(ArrowLeftIcon, { className: "text-text-senary-300 size-5 lg:hidden" }), _jsx("span", { className: "text-lg font-semibold lg:hidden", children: "Back" })] }), _jsx(Dialog.Title, { className: "mt-6 text-center text-lg font-semibold lg:mt-0", children: "Take a Selfie Holding Your ID" }), _jsx(Dialog.Description, { className: "text-text-tertiary-600 mx-auto mt-sm max-w-[19rem] text-center text-sm lg:max-w-full", children: "Make sure your face and ID are clearly visible, with no glare or blur." }), _jsx("div", { id: "parent", className: "border-border-disabled mt-6 aspect-video rounded-xl border bg-black lg:mt-8", children: captureInProgress ? (_jsxs("div", { className: "relative flex h-fit w-full flex-col items-center justify-center", children: [_jsx("video", { ref: videoRef, autoPlay: true, muted: true, className: "h-[60vh] w-full rounded-lg object-cover md:aspect-video md:h-[333px]", width: "100%", height: "100%" }), _jsx("canvas", { ref: faceCanvasRef, className: "absolute inset-0 h-[60vh] w-full object-cover md:aspect-video md:h-[333px] md:object-contain", width: "100%", height: "100%" }), isCapturing && (_jsx("span", { className: "absolute inset-x-0 bottom-8 flex items-center justify-center text-5xl font-bold text-[#713B12CC] md:bottom-4", children: countdown })), !isFaceCentered && (_jsx("span", { className: "absolute inset-x-0 bottom-12 mx-auto w-1/2 text-center text-xs text-white md:bottom-3", children: "Please center your face in the camera view. Make sure your ID is clearly visible next to your face." })), _jsx(PersonOverlayDesktop, { isFaceCentered: isFaceCentered, width: videoRef.current?.clientWidth || 543, height: videoRef.current?.clientHeight || 333 })] })) : (selectedImage && (_jsx("img", { src: selectedImage, alt: "Captured Photo", className: "h-full w-full rounded-lg object-cover md:aspect-video md:h-[333px]" }))) }), selectedImage && (_jsxs("div", { className: "flex flex-col items-center justify-center gap-2 md:flex-row", children: [_jsxs(Button, { className: "disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-4 px-xl disabled:opacity-100", size: "sm", onClick: retakePhoto, type: "button", children: ["Rekate Photo", _jsx(Camera01Icon, { className: "size-5" })] }), _jsxs(Button, { className: "disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-4 px-xl disabled:opacity-100", size: "sm", onClick: usePhoto, type: "button", disabled: isPending, children: ["Use Photo", _jsx(Camera01Icon, { className: "size-5" })] })] })), !captureInProgress && (_jsxs(Button, { className: twMerge('mx-auto mt-6 px-xl lg:mt-8', selectedImage && 'hidden'), onClick: startCamera, disabled: !modelsLoaded, type: "button", children: ["Start Camera", _jsx(Camera01Icon, { className: "size-5" })] }))] }));
251
258
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Image from 'next/image';
3
3
  import Link from 'next/link';
4
- import { useEffect, useState } from 'react';
4
+ import { useState } from 'react';
5
5
  import { useInterval } from 'usehooks-ts';
6
6
  import { useShallow } from 'zustand/shallow';
7
7
  import { useDisclosure } from '../../client/hooks/useDisclosure.js';
@@ -30,6 +30,10 @@ export function MessagesPopup() {
30
30
  depositWithdrawal: ctx.depositWithdrawal,
31
31
  })));
32
32
  const markMessageAsReadMutation = useMarkMessageAsReadMutation();
33
+ const [paused, setPaused] = useState(false);
34
+ useInterval(() => {
35
+ setPaused(false);
36
+ }, paused ? 1000 * 15 : null);
33
37
  const disclosure = useDisclosure({
34
38
  onOpenChange(open) {
35
39
  if (open) {
@@ -41,13 +45,11 @@ export function MessagesPopup() {
41
45
  markMessageAsReadMutation.mutate(message.id);
42
46
  }
43
47
  else {
44
- setPaused(true);
45
48
  setIndex_(0);
49
+ setPaused(true);
46
50
  }
47
51
  },
48
52
  });
49
- const [paused, setPaused] = useState(false);
50
- useInterval(() => setPaused(false), paused ? 1000 * 30 : null);
51
53
  const messagesQuery = useMessagesQuery({
52
54
  first: 10,
53
55
  filter: {
@@ -65,7 +67,7 @@ export function MessagesPopup() {
65
67
  !globalStore.message.open &&
66
68
  !globalStore.kyc.open &&
67
69
  !paused,
68
- refetchInterval: disclosure.open ? undefined : 10000,
70
+ refetchInterval: disclosure.open ? undefined : 1000 * 10,
69
71
  });
70
72
  const messages = messagesQuery.data?.pages.flatMap((page) => page.edges.map((edge) => edge.node)) ?? [];
71
73
  const [index, setIndex_] = useState(0);
@@ -78,16 +80,16 @@ export function MessagesPopup() {
78
80
  return;
79
81
  markMessageAsReadMutation.mutate(message.id);
80
82
  };
81
- useEffect(() => {
82
- if (messages.length > 0) {
83
- disclosure.setOpen(true);
84
- }
85
- }, [disclosure, messages.length, paused]);
83
+ useInterval(() => {
84
+ if (messages.length <= 0)
85
+ return;
86
+ disclosure.setOpen(true);
87
+ }, disclosure.open || paused ? null : 2500);
86
88
  return (_jsx(Dialog.Root, { open: disclosure.open, onOpenChange: (details) => {
87
89
  disclosure.setOpen(details.open);
88
90
  }, lazyMount: true, unmountOnExit: true, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, { className: "!z-[calc(var(--z-dialog)+3)]" }), _jsx(Dialog.Positioner, { className: "!z-[calc(var(--z-dialog)+4)] flex items-center justify-center", children: _jsxs(Dialog.Content, { className: "min-h-auto mx-auto min-w-[21.438rem] max-w-[21.438rem] rounded-xl p-3xl lg:min-w-[25rem] lg:max-w-[25rem]", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsxs(Carousel.Root, { page: index, onPageChange: (details) => {
89
91
  setIndex(details.page);
90
- }, slideCount: messages.length, allowMouseDrag: true, children: [_jsx(Carousel.ItemGroup, { children: messages.map((message, index) => (_jsxs(Carousel.Item, { index: index, className: "select-none", children: [_jsx("div", { className: "bg-bg-brand-secondary text-text-brand mx-auto flex size-12 items-center justify-center rounded-full", children: _jsx(MessageIcon, { type: message.icon, className: "size-6" }) }), _jsx("h2", { className: "mt-lg text-center text-lg font-semibold xl:mt-xl", children: message.title }), message.content && (_jsx("div", { dangerouslySetInnerHTML: { __html: message.content }, className: "text-text-secondary-700 mt-xs pb-5 text-center text-sm" })), message.image && (_jsx("div", { className: "mt-5 pb-5", children: _jsx(Image, { src: message.image, alt: "", width: 400, height: 250, loading: "lazy", unoptimized: true, className: "h-auto w-full rounded-sm" }) })), message.actions.length ? (_jsx("div", { className: "mt-3xl flex flex-col gap-2 text-center lg:mt-4xl", children: message.actions.map((action, index) => (_jsx(Button, { asChild: true, children: _jsx(Link, { href: normalizeActionUrl(action.url, {
92
+ }, slideCount: messages.length, allowMouseDrag: true, autoplay: false, children: [_jsx(Carousel.ItemGroup, { children: messages.map((message, index) => (_jsxs(Carousel.Item, { index: index, className: "select-none", children: [_jsx("div", { className: "bg-bg-brand-secondary text-text-brand mx-auto flex size-12 items-center justify-center rounded-full", children: _jsx(MessageIcon, { type: message.icon, className: "size-6" }) }), _jsx("h2", { className: "mt-lg text-center text-lg font-semibold xl:mt-xl", children: message.title }), message.content && (_jsx("div", { dangerouslySetInnerHTML: { __html: message.content }, className: "text-text-secondary-700 mt-xs pb-5 text-center text-sm" })), message.image && (_jsx("div", { className: "mt-5 pb-5", children: _jsx(Image, { src: message.image, alt: "", width: 400, height: 250, loading: "lazy", unoptimized: true, className: "h-auto w-full rounded-sm" }) })), message.actions.length ? (_jsx("div", { className: "mt-3xl flex flex-col gap-2 text-center lg:mt-4xl", children: message.actions.map((action, index) => (_jsx(Button, { asChild: true, children: _jsx(Link, { href: normalizeActionUrl(action.url, {
91
93
  questsPageUrl,
92
94
  transactionsPageUrl,
93
95
  }), onClick: () => {
package/dist/handlers.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { addDays, addMinutes, subMinutes } from 'date-fns';
2
2
  import { NextResponse } from 'next/server';
3
3
  import { z } from 'zod';
4
- import { ACCESS_TOKEN_COOKIE_NAME, DOMAIN_COOKIE_NAME, DOMAIN_HEADER_KEY, PLATFORM_CODE, RECAPTCHA_HEADER_KEY, REFRESH_TOKEN_COOKIE_NAME, } from './constants/index.js';
4
+ import { ACCESS_TOKEN_COOKIE_NAME, DOMAIN_COOKIE_NAME, DOMAIN_HEADER_KEY, RECAPTCHA_HEADER_KEY, REFRESH_TOKEN_COOKIE_NAME, } from './constants/index.js';
5
5
  import { authenticate, createSession, destroySession, refreshSession, } from './services/auth.js';
6
6
  const CreateSessionDefinition = z.union([
7
7
  z.object({
@@ -163,18 +163,17 @@ export const POST = async (request, { params }) => {
163
163
  if (path === 'sessions') {
164
164
  const body = await request.json();
165
165
  const parsed = CreateSessionDefinition.safeParse(body);
166
- const recaptcha = request.headers.get(RECAPTCHA_HEADER_KEY);
167
- const domain = request.headers.get(DOMAIN_HEADER_KEY);
168
166
  if (!parsed.success) {
169
167
  return NextResponse.json({
170
168
  ok: false,
171
169
  message: parsed.error.issues[0].message,
172
170
  }, { status: 400 });
173
171
  }
172
+ const recaptcha = request.headers.get(RECAPTCHA_HEADER_KEY);
173
+ const domain = request.headers.get(DOMAIN_HEADER_KEY);
174
174
  try {
175
175
  const res = await createSession(parsed.data, {
176
176
  headers: {
177
- 'Platform-Code': PLATFORM_CODE,
178
177
  ...(recaptcha && {
179
178
  [RECAPTCHA_HEADER_KEY]: recaptcha,
180
179
  }),
@@ -218,18 +217,17 @@ export const POST = async (request, { params }) => {
218
217
  if (path === 'authenticate') {
219
218
  const body = await request.json();
220
219
  const parsed = AuthenticateDefinition.safeParse(body);
221
- const recaptcha = request.headers.get(RECAPTCHA_HEADER_KEY);
222
- const domain = request.headers.get(DOMAIN_HEADER_KEY);
223
220
  if (!parsed.success) {
224
221
  return NextResponse.json({
225
222
  ok: false,
226
223
  message: parsed.error.issues[0].message,
227
224
  }, { status: 400 });
228
225
  }
226
+ const recaptcha = request.headers.get(RECAPTCHA_HEADER_KEY);
227
+ const domain = request.headers.get(DOMAIN_HEADER_KEY);
229
228
  try {
230
229
  const res = await authenticate(parsed.data, {
231
230
  headers: {
232
- 'Platform-Code': PLATFORM_CODE,
233
231
  ...(recaptcha && {
234
232
  [RECAPTCHA_HEADER_KEY]: recaptcha,
235
233
  }),
@@ -12,13 +12,20 @@ export const middleware = async (req) => {
12
12
  const accessToken = req.cookies.get(ACCESS_TOKEN_COOKIE_NAME)?.value;
13
13
  const refreshToken = req.cookies.get(REFRESH_TOKEN_COOKIE_NAME)?.value;
14
14
  const res = NextResponse.next();
15
- if (req.nextUrl.searchParams.get('m')) {
15
+ if (req.nextUrl.searchParams.has('m')) {
16
16
  res.cookies.set(DOMAIN_COOKIE_NAME, `${req.nextUrl.origin}/${req.nextUrl.searchParams.get('m')}`, {
17
17
  expires: addDays(new Date(), 30),
18
18
  httpOnly: true,
19
19
  sameSite: 'strict',
20
20
  });
21
21
  }
22
+ if (!res.cookies.has(DOMAIN_COOKIE_NAME)) {
23
+ res.cookies.set(DOMAIN_COOKIE_NAME, req.nextUrl.origin, {
24
+ expires: addDays(new Date(), 30),
25
+ httpOnly: true,
26
+ sameSite: 'strict',
27
+ });
28
+ }
22
29
  if (!accessToken && refreshToken) {
23
30
  try {
24
31
  const data = await refreshSession({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opexa/portal-components",
3
- "version": "0.0.171",
3
+ "version": "0.0.173",
4
4
  "exports": {
5
5
  "./ui/*": {
6
6
  "types": "./dist/ui/*/index.d.ts",