@swan-admin/swan-web-component 1.0.76 → 1.0.77
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/faceScan.js +1 -1
- package/dist/faceScan.js.map +1 -1
- package/dist/faceScan.mjs +1 -1
- package/dist/faceScan.mjs.map +1 -1
- package/package.json +1 -1
package/dist/faceScan.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react/jsx-runtime"),s=require("./Header-BiYtGF6l.js"),
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),s=require("./Header-BiYtGF6l.js"),l=require("./arrow-right-CC1_17IM.js"),n=require("./LoadingScreen-D7OUJo45.js");function i({resetScanState:n,loading:i,config:t}){return e.jsx("div",{id:"faceScanErrorSuccess",className:"fixed top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full",style:{background:t?.style?.base?.backgroundColor},children:e.jsxs("div",{className:"flex flex-col w-full items-center p-[1rem] rounded-lg ",children:[e.jsx(s.Header,{noTitle:!0,resolvedConfig:t}),e.jsxs("div",{className:"flex flex-col items-center w-full",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-800 ",style:{fontFamily:t?.style?.heading?.headingFontFamily||"SeriouslyNostalgic Fn",fontSize:t?.style?.heading?.headingFontSize||"32px",color:t?.style?.heading?.headingColor||"#000",fontWeight:t?.style?.heading?.headingFontWeight||"normal"},children:"Your Scan Failed"}),e.jsx("p",{className:"mb-[1.5rem]",style:{fontFamily:t?.style?.subheading?.subheadingFontFamily||"'Inter', sans-serif",fontSize:t?.style?.subheading?.subheadingFontSize||"14px",color:t?.style?.subheading?.subheadingColor||"#4b5563",fontWeight:t?.style?.subheading?.subheadingFontWeight||"normal"},children:"Please click below to reset your scan."}),e.jsx(s.SpecificButton,{disabled:i,className:"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]",buttonText:"Reset Scan",postfixIcon:e.jsx(l.ArrowRight,{}),buttonFunc:()=>n?.(),resolvedConfig:t})]})]})})}require("react");exports.FaceScan=({userDetails:l,onComplete:t,onScanError:r,onRetry:o,config:a,isError:c,isSuccess:d})=>{const u=s.useLocalConfig(a);return c?e.jsx(i,{config:u}):d?e.jsx("div",{className:"fixed z-[9] w-full h-full",style:{background:u?.style?.base?.primaryColor},children:e.jsx(n.LoadingScreen,{url:u?.loader,loaderType:"default"})}):void 0};
|
|
2
2
|
//# sourceMappingURL=faceScan.js.map
|
package/dist/faceScan.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faceScan.js","sources":["../src/components/faceScan/FaceScanErrorScreen.tsx","../src/components/faceScan/FaceScan.tsx"],"sourcesContent":["import { ArrowRight } from \"lucide-react\";\nimport Header from \"../Header\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\n\nfunction FaceScanErrorScreen({ resetScanState, loading, config }: { resetScanState?: () => void; loading?: boolean; config?: any }) {\n\treturn (\n\t\t<div id=\"faceScanErrorSuccess\" className=\"fixed top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full\" style={{ background: config?.style?.base?.backgroundColor }}>\n\t\t\t<div className=\"flex flex-col w-full items-center p-[1rem] rounded-lg \">\n\t\t\t\t<Header noTitle resolvedConfig={config} />\n\t\t\t\t<div className=\"flex flex-col items-center w-full\">\n\t\t\t\t\t<h2\n\t\t\t\t\t\tclassName=\"text-xl font-semibold text-gray-800 \"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\tfontSize: config?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\tcolor: config?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tYour Scan Failed\n\t\t\t\t\t</h2>\n\t\t\t\t\t<p\n\t\t\t\t\t\tclassName=\"mb-[1.5rem]\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.subheading?.subheadingFontFamily || \"'Inter', sans-serif\",\n\t\t\t\t\t\t\tfontSize: config?.style?.subheading?.subheadingFontSize || \"14px\",\n\t\t\t\t\t\t\tcolor: config?.style?.subheading?.subheadingColor || \"#4b5563\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.subheading?.subheadingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tPlease click below to reset your scan.\n\t\t\t\t\t</p>\n\t\t\t\t\t<SpecificButton\n\t\t\t\t\t\tdisabled={loading}\n\t\t\t\t\t\tclassName=\"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]\"\n\t\t\t\t\t\tbuttonText=\"Reset Scan\"\n\t\t\t\t\t\tpostfixIcon={<ArrowRight />}\n\t\t\t\t\t\tbuttonFunc={() => resetScanState?.()}\n\t\t\t\t\t\tresolvedConfig={config}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default FaceScanErrorScreen;\n","\"use client\";\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ArrowRight } from \"lucide-react\";\nimport { Drawer } from \"@mui/material\";\nimport FaceScanGuide from \"./FaceScanGuide\";\nimport FaceScanErrorScreen from \"./FaceScanErrorScreen\";\nimport posthog from \"posthog-js\";\nimport { FaceScanProps } from \"../../types/interfaces\";\nimport { generateUuid, handleScanTimeCapture, handleWebSocketCapture } from \"../../utils/utils\";\nimport { useLocalConfig } from \"../../config/useLocalConfig\";\nimport { posthogPublicHost, posthogPublicKey, videoConstraints, videoTypes, voiceOverAssetsPath } from \"../../utils/constants\";\nimport swan from \"../../utils/service/swanService\";\nimport speechService from \"../../utils/service/speechService\";\nimport useFaceScan from \"../../customHooks/useFaceScan\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport LoadingScreen from \"../LoadingScreen\";\n\nexport const FaceScan: React.FC<FaceScanProps> = ({ userDetails, onComplete, onScanError, onRetry, config,isError,isSuccess }) => {\n\t// const webcamRef = useRef<HTMLVideoElement | null>(null);\n\t// const canvasRef = useRef<HTMLCanvasElement | null>(null);\n\t// const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n\t// const recordedBlobsRef = useRef<Blob[] | null>([]);\n\t// const streamRef = useRef<MediaStream | null>(null);\n\t// const [showLoader, setShowLoader] = useState(false);\n\t// const [modelReady, setModelReady] = useState(false);\n\t// const [isScanning, setIsScanning] = useState(false);\n\t// const [showGuideCard, setShowGuideCard] = useState(true);\n\t// const [videoLoading, setVideoLoading] = useState(false);\n\t// const [videoError, setVideoError] = useState(false);\n\t// const [hasError, setHasError] = useState(false);\n\t// const [faceScanId, setFaceScanId] = useState(generateUuid());\n\tconst resolvedConfig = useLocalConfig(config);\n\t// const { email, gender, deviceFocalLength, shopDomain, callbackUrl } = userDetails;\n\t// const supportedTypes = useMemo(() => videoTypes.filter((type) => MediaRecorder.isTypeSupported(type)), []);\n\n\t// const stopRecording = useCallback(() => {\n\t// \tconsole.log(\"Stopping recording...\");\n\t// \tif (mediaRecorderRef.current && mediaRecorderRef.current.state === \"recording\") {\n\t// \t\tmediaRecorderRef.current.stop();\n\t// \t}\n\t// \tmediaRecorderRef.current = null;\n\t// }, []);\n\n\t// const uploadFinalVideo = async () => {\n\t// \tsetShowLoader(true);\n\t// \tconsole.log(\"upload final video\");\n\t// \tif (recordedBlobsRef.current) {\n\t// \t\tif (recordedBlobsRef.current.length === 0) {\n\t// \t\t\tconsole.error(\"No video data recorded\");\n\t// \t\t\tsetHasError(true);\n\t// \t\t\tsetShowLoader(false);\n\n\t// \t\t\treturn;\n\t// \t\t}\n\n\t\t\t// const videoFile = new File(recordedBlobsRef.current, `${faceScanId}.webm`, {\n\t\t\t// \ttype: \"video/webm\",\n\t\t\t// });\n\t\t\t// const fileSize = videoFile.size;\n\t\t\t// const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2);\n\t\t\t// const estimatedDuration = Math.round(fileSize / 10000);\n\t\t\t// const videoData = {\n\t\t\t// \tvideo_size_mb: parseFloat(fileSizeMB),\n\t\t\t// \tvideo_size_bytes: fileSize,\n\t\t\t// \tblob_count: recordedBlobsRef.current.length,\n\t\t\t// \testimated_duration_seconds: estimatedDuration,\n\t\t\t// };\n\n\t\t\t// const metaData = [\n\t\t\t// \t{ gender: gender },\n\t\t\t// \t{ face_scan_id: faceScanId },\n\t\t\t// \t{\n\t\t\t// \t\tfocal_length: `${deviceFocalLength}`,\n\t\t\t// \t},\n\t\t\t// \t{ customer_store_url: shopDomain },\n\t\t\t// \t{ scan_type: \"face_scan\" },\n\t\t\t// \t{ callback_url: callbackUrl },\n\t\t\t// ];\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_meta_data`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tdata: JSON.stringify({ metaData, videoData }),\n\t\t\t// });\n\n\t\t\t// const uploadStartTime = Date.now();\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_upload_start`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tstartTime: uploadStartTime,\n\t\t\t// });\n\n\t// \t\ttry {\n\t// \t\t\tconst res = await swan.fileUpload.faceScanFileUploader({\n\t// \t\t\t\tfile: videoFile,\n\t// \t\t\t\tobjectKey: faceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tarrayMetaData: metaData,\n\t// \t\t\t\tcontentType: videoFile.type,\n\t// \t\t\t});\n\n\t// \t\t\tconst uploadEndTime = Date.now();\n\t// \t\t\tconst uploadDuration = uploadEndTime - uploadStartTime;\n\n\t// \t\t\thandleScanTimeCapture({\n\t// \t\t\t\teventName: `${shopDomain}/face_scan_upload_complete`,\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tcompletionTime: uploadEndTime,\n\t// \t\t\t\tuploadDuration,\n\t// \t\t\t});\n\n\t// \t\t\tconsole.log(\"✅ Upload successful\", res);\n\t// \t\t\tswan.measurement.handlFaceScaneSocket({\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\tonOpen: () => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"open\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tconsole.log(\"websocket connect open\");\n\t// \t\t\t\t},\n\t// \t\t\t\tonError: (err) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"error\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonError(err);\n\t// \t\t\t\t},\n\t// \t\t\t\tonSuccess: (data) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"success\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonSuccess(data);\n\t// \t\t\t\t},\n\t// \t\t\t\tonClose: () => {\n\t// \t\t\t\t\tconsole.log(\"websocket connect close\");\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"close\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t},\n\t// \t\t\t});\n\t// \t\t} catch (error) {\n\t// \t\t\tonError(error);\n\t// \t\t}\n\t// \t}\n\t// };\n\n\t// const startRecording = useCallback(() => {\n\t// \tconsole.log(\"Starting recording for stages 1-3...\");\n\t// \tconst stream = webcamRef.current?.srcObject as MediaStream | null;\n\t// \tif (!stream) return;\n\n\t// \t// Create a canvas to normalize orientation\n\t// \tconst videoTrack = stream.getVideoTracks()[0];\n\t// \tconst settings = videoTrack.getSettings();\n\t// \tconst { width = videoConstraints.width, height = videoConstraints.height } = settings;\n\n\t// \tconst canvas = document.createElement(\"canvas\");\n\t// \tconst ctx = canvas.getContext(\"2d\");\n\n\t// \t// Always force portrait (swap width/height if landscape)\n\t// \tif (width > height) {\n\t// \t\tcanvas.width = height;\n\t// \t\tcanvas.height = width;\n\t// \t} else {\n\t// \t\tcanvas.width = width;\n\t// \t\tcanvas.height = height;\n\t// \t}\n\n\t// \tconst videoEl = document.createElement(\"video\");\n\t// \tvideoEl.srcObject = stream;\n\t// \tvideoEl.muted = true;\n\t// \tvideoEl.playsInline = true;\n\t// \tvideoEl.play();\n\n\t// \t// Draw video frames onto canvas\n\t// \tconst drawFrame = () => {\n\t// \t\t// Rotate if camera gives landscape frames\n\t// \t\tif (width > height) {\n\t// \t\t\tctx?.save();\n\t// \t\t\tctx?.translate(canvas.width, 0);\n\t// \t\t\tctx?.rotate(Math.PI / 2);\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, height, width);\n\t// \t\t\tctx?.restore();\n\t// \t\t} else {\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, width, height);\n\t// \t\t}\n\t// \t\trequestAnimationFrame(drawFrame);\n\t// \t};\n\t// \tdrawFrame();\n\n\t\t// Capture portrait-normalized stream\n\t\t// const canvasStream = canvas.captureStream(30); // 30fps\n\t\t// const mediaRecorderOptions = supportedTypes.length > 0 ? { mimeType: supportedTypes[0] } : {};\n\t\t// let mediaRecorder;\n\n\t\t// try {\n\t\t// \tmediaRecorder = new MediaRecorder(canvasStream, mediaRecorderOptions);\n\t\t// } catch (e) {\n\t\t// \tconsole.error(\"MediaRecorder init failed:\", e);\n\t\t// \tsetHasError(true);\n\t\t// \tsetShowLoader(false);\n\t\t// \treturn;\n\t\t// }\n\n\t// \trecordedBlobsRef.current = [];\n\t// \tmediaRecorder.ondataavailable = (event) => {\n\t// \t\tif (event?.data && event.data.size > 0 && recordedBlobsRef.current) {\n\t// \t\t\trecordedBlobsRef.current.push(event.data);\n\t// \t\t}\n\t// \t};\n\t// \tmediaRecorder.onstop = () => {\n\t// \t\tconsole.log(\"Recording stopped, total blobs:\", recordedBlobsRef?.current?.length);\n\t// \t\tuploadFinalVideo();\n\t// \t};\n\n\t// \tmediaRecorder.start(100); // 100ms chunks\n\t// \tmediaRecorderRef.current = mediaRecorder;\n\t// \tconsole.log(\"Recording started successfully (portrait normalized)\");\n\t// }, [supportedTypes, uploadFinalVideo]);\n\n\t// const { faceScanDetector, scanStage, setScanStage, resetScan, isModelLoaded, startScanSequence } = useFaceScan({\n\t// \tfaceScanId,\n\t// \tshopDomain,\n\t// \tonScanComplete: () => {\n\t// \t\tstopRecording();\n\t// \t},\n\t// \tonModelReady: () => {\n\t// \t\tsetModelReady(true);\n\t// \t},\n\t// \tonStartRecording: () => {\n\t// \t\tconsole.log(\"Stage 0 completed - starting recording for stages 1-3\");\n\t// \t\tstartRecording();\n\t// \t},\n\t// });\n\n\t// const startScan = useCallback(() => {\n\t// \tif (!isModelLoaded) return;\n\t// \tsetScanStage(0);\n\t// \tspeechService.playAudio(`${voiceOverAssetsPath}face-scan-vos/Face-forward.mp3`);\n\t// \tsetTimeout(() => {\n\t// \t\tsetIsScanning(true);\n\t// \t\tsetTimeout(() => {\n\t// \t\t\tstartScanSequence();\n\t// \t\t}, 200);\n\t// // \t}, 200);\n\t// // }, [setScanStage, setIsScanning, startScanSequence, isModelLoaded]);\n\n\t// const resetScanState = useCallback(() => {\n\t// \tsetIsScanning(false);\n\t// \tsetShowGuideCard(true);\n\t// \tstopRecording();\n\t// \tresetScan();\n\t// \tonRetry?.();\n\t// \trecordedBlobsRef.current = [];\n\t// \tif (mediaRecorderRef.current) {\n\t// \t\tmediaRecorderRef.current = null;\n\t// \t}\n\t// \tsetHasError(false);\n\t// \tsetScanStage(-1);\n\t// \tsetFaceScanId(generateUuid());\n\t// \tif (webcamRef.current && streamRef.current) {\n\t// \t\twebcamRef.current.srcObject = streamRef.current;\n\t// \t\tconsole.log(\"Camera stream restored after reset\");\n\t// \t}\n\t// }, [setScanStage, setIsScanning, setShowGuideCard, stopRecording, resetScan, setFaceScanId]);\n\n\t// const onError = (data: any) => {\n\t// \tconsole.log(data, \"ws error\");\n\t// \tonScanError?.(data);\n\t// \tsetHasError(true);\n\t// \tsetShowLoader(false);\n\t// \tsetShowGuideCard(false);\n\t// \thandleScanTimeCapture({\n\t// \t\teventName: `${shopDomain}/faceScan`,\n\t// \t\tfaceScanId,\n\t// \t\tstatus: \"failed\",\n\t// \t\temail,\n\t// \t\tdata: JSON.stringify(data),\n\t// \t});\n\t// };\n\n\t// const onSuccess = (data: any) => {\n\t// \tconsole.log(data, \"ws success\");\n\t// \tif (data && data?.resultType === \"intermediate\") {\n\t// \t\thandleScanTimeCapture({\n\t// \t\t\teventName: `${shopDomain}/faceScan_success/intermediate`,\n\t// \t\t\tfaceScanId,\n\t// \t\t\tstatus: \"success\",\n\t// \t\t\temail,\n\t// \t\t\tdata: JSON.stringify(data),\n\t// \t\t});\n\t// \t}\n\t// \tonComplete?.(data);\n\t// };\n\n\t// Initialize webcam\n\t// useEffect(() => {\n\t// \tif (isError || isSuccess) return;\n\t// \tif (navigator.mediaDevices.getUserMedia) {\n\t// \t\tnavigator.mediaDevices\n\t// \t\t\t.getUserMedia({ video: videoConstraints })\n\t// \t\t\t.then((stream) => {\n\t// \t\t\t\tstreamRef.current = stream;\n\t// \t\t\t\tif (webcamRef.current) {\n\t// \t\t\t\t\twebcamRef.current.srcObject = stream;\n\t// \t\t\t\t\tconsole.log(\"Webcam initialized\");\n\t// \t\t\t\t}\n\t// \t\t\t})\n\t// \t\t\t.catch((err) => console.error(\"Error accessing webcam:\", err));\n\t// \t}\n\n\t\t// Cleanup function\n\t// \treturn () => {\n\t// \t\tif (streamRef.current) {\n\t// \t\t\tstreamRef.current.getTracks().forEach((track) => track.stop());\n\t// \t\t}\n\t// \t};\n\t// }, [isError, isSuccess]);\n\n\t// // Face detection interval - only run when scanning is active\n\t// useEffect(() => {\n\t// \tif (!modelReady || !isScanning) return;\n\n\t// \tconst intervalId = setInterval(() => {\n\t// \t\tfaceScanDetector(webcamRef, canvasRef);\n\t// \t}, 500);\n\n\t// \t// eslint-disable-next-line consistent-return\n\t// \treturn () => clearInterval(intervalId);\n\t// }, [faceScanDetector, modelReady, isScanning]);\n\n\t// const getButtonText = () => {\n\t// \tif (isScanning) return \"Reset\";\n\t// \tif (!isModelLoaded) return \"Loading...\";\n\t// \treturn \"Start\";\n\t// };\n\n\t// console.log(\"Model ready:\", modelReady, \"Has error:\", hasError, \"Is scanning:\", isScanning, \"showLoader\", showLoader);\n\n\t// useEffect(() => {\n\t// \tsetScanStage(-1);\n\t// \tif (isError || isSuccess) return;\n\t// \tposthog.init(posthogPublicKey, { api_host: posthogPublicHost });\n\t// \tposthog.capture(\"$pageview\");\n\t// }, [isError, isSuccess]);\n\n\tif (isError) {\n\t\treturn <FaceScanErrorScreen config={resolvedConfig} />;\n\t}\n\n\tif (isSuccess) {\n\t\treturn (\n\t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t\t\t\t{/* <Asset genderType={gender} /> */}\n\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// return (\n\t// \t<>\n\t// \t\t<audio id=\"audioElement\" crossOrigin=\"anonymous\" preload=\"auto\" style={{ position: \"absolute\", zIndex: -99999 }} src=\"\" />\n\n\t// \t\t{showLoader && !hasError && (\n\t// \t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t// \t\t\t\t{/* <Asset genderType={gender} /> */}\n\t// \t\t\t\t<LoadingScreen url={resolvedConfig?.loader} />\n\t// \t\t\t</div>\n\t// \t\t)}\n\n\t\t\t{/* Always show camera view */}\n\t\t\t// <div className=\"h-full flex-col relative w-full flex justify-center items-center text-center rounded-t-[20px]\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t// \t<div className=\"flex-1 w-full max-w-md overflow-hidden\">\n\t\t\t// \t\t<div className=\"w-full h-full\">\n\t\t\t// \t\t\t<video\n\t\t\t// \t\t\t\tref={webcamRef}\n\t\t\t// \t\t\t\tautoPlay\n\t\t\t// \t\t\t\tplaysInline\n\t\t\t// \t\t\t\tmuted\n\t\t\t// \t\t\t\twidth={videoConstraints.width}\n\t\t\t// \t\t\t\theight={videoConstraints.height}\n\t\t\t// \t\t\t\tclassName=\"w-full h-full object-cover fixed left-0 top-0 z-0\"\n\t\t\t// \t\t\t\tstyle={{ transform: \"scaleX(-1)\" }}\n\t\t\t// \t\t\t/>\n\t\t\t// \t\t\t<canvas ref={canvasRef} width={videoConstraints.width} height={videoConstraints.height} style={{ transform: \"scaleX(-1)\", opacity: \"0\" }} />\n\t\t\t// \t\t</div>\n\t\t\t// \t</div>\n\t\t\t// </div>\n\n\t\t\t{/* Error overlay */}\n\t\t\t// {!showLoader && hasError && <FaceScanErrorScreen loading={showLoader} resetScanState={resetScanState} config={resolvedConfig} />}\n\n\t\t\t{/* Scan guide drawer - only show when scanning */}\n\t\t// \t{showGuideCard && !hasError && !showLoader && (\n\t\t// \t\t<Drawer\n\t\t// \t\t\topen\n\t\t// \t\t\tclassName=\"face-scan-small camera-draw\"\n\t\t// \t\t\tanchor=\"bottom\"\n\t\t// \t\t\tonClose={(event, reason) => {\n\t\t// \t\t\t\tif (reason === \"backdropClick\") {\n\t\t// \t\t\t\t\treturn;\n\t\t// \t\t\t\t}\n\t\t// \t\t\t}}\n\t\t// \t\t\thideBackdrop\n\t\t// \t\t>\n\t\t// \t\t\t<FaceScanGuide stage={scanStage} videoLoading={videoLoading} setVideoLoading={setVideoLoading} videoError={videoError} setVideoError={setVideoError} gender={gender} config={resolvedConfig}>\n\t\t// \t\t\t\t<div className=\"flex justify-center w-full p-[1rem]\">\n\t\t// \t\t\t\t\t<SpecificButton\n\t\t// \t\t\t\t\t\tdisabled={showLoader || !isModelLoaded}\n\t\t// \t\t\t\t\t\tclassName=\"!w-[60px] !h-[35px] !py-0 !px-0\"\n\t\t// \t\t\t\t\t\tbuttonText={getButtonText()}\n\t\t// \t\t\t\t\t\tpostfixIcon={isScanning ? <ArrowRight /> : \"\"}\n\t\t// \t\t\t\t\t\tbuttonFunc={isScanning ? resetScanState : startScan}\n\t\t// \t\t\t\t\t\tresolvedConfig={resolvedConfig}\n\t\t// \t\t\t\t\t/>\n\t\t// \t\t\t\t</div>\n\t\t// \t\t\t</FaceScanGuide>\n\t\t// \t\t</Drawer>\n\t\t// \t)}\n\t\t// </>\n\t// );\n};\n"],"names":["FaceScanErrorScreen","resetScanState","loading","config","_jsx","id","className","style","background","base","backgroundColor","children","_jsxs","Header","noTitle","resolvedConfig","fontFamily","heading","headingFontFamily","fontSize","headingFontSize","color","headingColor","fontWeight","headingFontWeight","subheading","subheadingFontFamily","subheadingFontSize","subheadingColor","subheadingFontWeight","SpecificButton","disabled","buttonText","postfixIcon","ArrowRight","buttonFunc","userDetails","onComplete","onScanError","onRetry","isError","isSuccess","useLocalConfig","primaryColor","LoadingScreen","url","loader"],"mappings":"kKAIA,SAASA,GAAoBC,eAAEA,EAAcC,QAAEA,EAAOC,OAAEA,IACvD,OACCC,EAAAA,WAAKC,GAAG,uBAAuBC,UAAU,kFAAkFC,MAAO,CAAEC,WAAYL,GAAQI,OAAOE,MAAMC,iBAAiBC,SACrLC,OAAA,MAAA,CAAKN,UAAU,oEACdF,EAAAA,IAACS,EAAAA,QAAOC,SAAO,EAACC,eAAgBZ,IAChCS,OAAA,MAAA,CAAKN,UAAU,oCAAmCK,SAAA,CACjDP,EAAAA,IAAA,KAAA,CACCE,UAAU,uCACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOU,SAASC,mBAAqB,wBACzDC,SAAUhB,GAAQI,OAAOU,SAASG,iBAAmB,OACrDC,MAAOlB,GAAQI,OAAOU,SAASK,cAAgB,OAC/CC,WAAYpB,GAAQI,OAAOU,SAASO,mBAAqB,UACzDb,SAAA,qBAIFP,EAAAA,IAAA,IAAA,CACCE,UAAU,cACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOkB,YAAYC,sBAAwB,sBAC/DP,SAAUhB,GAAQI,OAAOkB,YAAYE,oBAAsB,OAC3DN,MAAOlB,GAAQI,OAAOkB,YAAYG,iBAAmB,UACrDL,WAAYpB,GAAQI,OAAOkB,YAAYI,sBAAwB,UAC/DlB,SAAA,2CAIFP,EAAAA,IAAC0B,EAAAA,eAAc,CACdC,SAAU7B,EACVI,UAAU,iEACV0B,WAAW,aACXC,YAAa7B,EAAAA,IAAC8B,EAAAA,WAAU,CAAA,GACxBC,WAAY,IAAMlC,MAClBc,eAAgBZ,WAMtB,mCC3BiD,EAAGiC,cAAaC,aAAYC,cAAaC,UAASpC,SAAOqC,UAAQC,gBAcjH,MAAM1B,EAAiB2B,EAAAA,eAAevC,GA4UtC,OAAIqC,EACIpC,EAAAA,IAACJ,EAAmB,CAACG,OAAQY,IAGjC0B,EAEFrC,EAAAA,IAAA,MAAA,CAAKE,UAAU,6BAA6BC,MAAO,CAAEC,WAAYO,GAAgBR,OAAOE,MAAMkC,cAAchC,SAE3GP,EAAAA,IAACwC,EAAAA,cAAa,CAACC,IAAK9B,GAAgB+B,gBAJvC"}
|
|
1
|
+
{"version":3,"file":"faceScan.js","sources":["../src/components/faceScan/FaceScanErrorScreen.tsx","../src/components/faceScan/FaceScan.tsx"],"sourcesContent":["import { ArrowRight } from \"lucide-react\";\nimport Header from \"../Header\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\n\nfunction FaceScanErrorScreen({ resetScanState, loading, config }: { resetScanState?: () => void; loading?: boolean; config?: any }) {\n\treturn (\n\t\t<div id=\"faceScanErrorSuccess\" className=\"fixed top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full\" style={{ background: config?.style?.base?.backgroundColor }}>\n\t\t\t<div className=\"flex flex-col w-full items-center p-[1rem] rounded-lg \">\n\t\t\t\t<Header noTitle resolvedConfig={config} />\n\t\t\t\t<div className=\"flex flex-col items-center w-full\">\n\t\t\t\t\t<h2\n\t\t\t\t\t\tclassName=\"text-xl font-semibold text-gray-800 \"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\tfontSize: config?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\tcolor: config?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tYour Scan Failed\n\t\t\t\t\t</h2>\n\t\t\t\t\t<p\n\t\t\t\t\t\tclassName=\"mb-[1.5rem]\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.subheading?.subheadingFontFamily || \"'Inter', sans-serif\",\n\t\t\t\t\t\t\tfontSize: config?.style?.subheading?.subheadingFontSize || \"14px\",\n\t\t\t\t\t\t\tcolor: config?.style?.subheading?.subheadingColor || \"#4b5563\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.subheading?.subheadingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tPlease click below to reset your scan.\n\t\t\t\t\t</p>\n\t\t\t\t\t<SpecificButton\n\t\t\t\t\t\tdisabled={loading}\n\t\t\t\t\t\tclassName=\"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]\"\n\t\t\t\t\t\tbuttonText=\"Reset Scan\"\n\t\t\t\t\t\tpostfixIcon={<ArrowRight />}\n\t\t\t\t\t\tbuttonFunc={() => resetScanState?.()}\n\t\t\t\t\t\tresolvedConfig={config}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default FaceScanErrorScreen;\n","\"use client\";\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ArrowRight } from \"lucide-react\";\nimport { Drawer } from \"@mui/material\";\nimport FaceScanGuide from \"./FaceScanGuide\";\nimport FaceScanErrorScreen from \"./FaceScanErrorScreen\";\nimport posthog from \"posthog-js\";\nimport { FaceScanProps } from \"../../types/interfaces\";\nimport { generateUuid, handleScanTimeCapture, handleWebSocketCapture } from \"../../utils/utils\";\nimport { useLocalConfig } from \"../../config/useLocalConfig\";\nimport { posthogPublicHost, posthogPublicKey, videoConstraints, videoTypes, voiceOverAssetsPath } from \"../../utils/constants\";\nimport swan from \"../../utils/service/swanService\";\nimport speechService from \"../../utils/service/speechService\";\nimport useFaceScan from \"../../customHooks/useFaceScan\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport LoadingScreen from \"../LoadingScreen\";\n\nexport const FaceScan: React.FC<FaceScanProps> = ({ userDetails, onComplete, onScanError, onRetry, config,isError,isSuccess }) => {\n\t// const webcamRef = useRef<HTMLVideoElement | null>(null);\n\t// const canvasRef = useRef<HTMLCanvasElement | null>(null);\n\t// const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n\t// const recordedBlobsRef = useRef<Blob[] | null>([]);\n\t// const streamRef = useRef<MediaStream | null>(null);\n\t// const [showLoader, setShowLoader] = useState(false);\n\t// const [modelReady, setModelReady] = useState(false);\n\t// const [isScanning, setIsScanning] = useState(false);\n\t// const [showGuideCard, setShowGuideCard] = useState(true);\n\t// const [videoLoading, setVideoLoading] = useState(false);\n\t// const [videoError, setVideoError] = useState(false);\n\t// const [hasError, setHasError] = useState(false);\n\t// const [faceScanId, setFaceScanId] = useState(generateUuid());\n\tconst resolvedConfig = useLocalConfig(config);\n\t// const { email, gender, deviceFocalLength, shopDomain, callbackUrl } = userDetails;\n\t// const supportedTypes = useMemo(() => videoTypes.filter((type) => MediaRecorder.isTypeSupported(type)), []);\n\n\t// const stopRecording = useCallback(() => {\n\t// \tconsole.log(\"Stopping recording...\");\n\t// \tif (mediaRecorderRef.current && mediaRecorderRef.current.state === \"recording\") {\n\t// \t\tmediaRecorderRef.current.stop();\n\t// \t}\n\t// \tmediaRecorderRef.current = null;\n\t// }, []);\n\n\t// const uploadFinalVideo = async () => {\n\t// \tsetShowLoader(true);\n\t// \tconsole.log(\"upload final video\");\n\t// \tif (recordedBlobsRef.current) {\n\t// \t\tif (recordedBlobsRef.current.length === 0) {\n\t// \t\t\tconsole.error(\"No video data recorded\");\n\t// \t\t\tsetHasError(true);\n\t// \t\t\tsetShowLoader(false);\n\n\t// \t\t\treturn;\n\t// \t\t}\n\n\t\t\t// const videoFile = new File(recordedBlobsRef.current, `${faceScanId}.webm`, {\n\t\t\t// \ttype: \"video/webm\",\n\t\t\t// });\n\t\t\t// const fileSize = videoFile.size;\n\t\t\t// const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2);\n\t\t\t// const estimatedDuration = Math.round(fileSize / 10000);\n\t\t\t// const videoData = {\n\t\t\t// \tvideo_size_mb: parseFloat(fileSizeMB),\n\t\t\t// \tvideo_size_bytes: fileSize,\n\t\t\t// \tblob_count: recordedBlobsRef.current.length,\n\t\t\t// \testimated_duration_seconds: estimatedDuration,\n\t\t\t// };\n\n\t\t\t// const metaData = [\n\t\t\t// \t{ gender: gender },\n\t\t\t// \t{ face_scan_id: faceScanId },\n\t\t\t// \t{\n\t\t\t// \t\tfocal_length: `${deviceFocalLength}`,\n\t\t\t// \t},\n\t\t\t// \t{ customer_store_url: shopDomain },\n\t\t\t// \t{ scan_type: \"face_scan\" },\n\t\t\t// \t{ callback_url: callbackUrl },\n\t\t\t// ];\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_meta_data`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tdata: JSON.stringify({ metaData, videoData }),\n\t\t\t// });\n\n\t\t\t// const uploadStartTime = Date.now();\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_upload_start`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tstartTime: uploadStartTime,\n\t\t\t// });\n\n\t// \t\ttry {\n\t// \t\t\tconst res = await swan.fileUpload.faceScanFileUploader({\n\t// \t\t\t\tfile: videoFile,\n\t// \t\t\t\tobjectKey: faceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tarrayMetaData: metaData,\n\t// \t\t\t\tcontentType: videoFile.type,\n\t// \t\t\t});\n\n\t// \t\t\tconst uploadEndTime = Date.now();\n\t// \t\t\tconst uploadDuration = uploadEndTime - uploadStartTime;\n\n\t// \t\t\thandleScanTimeCapture({\n\t// \t\t\t\teventName: `${shopDomain}/face_scan_upload_complete`,\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tcompletionTime: uploadEndTime,\n\t// \t\t\t\tuploadDuration,\n\t// \t\t\t});\n\n\t// \t\t\tconsole.log(\"✅ Upload successful\", res);\n\t// \t\t\tswan.measurement.handlFaceScaneSocket({\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\tonOpen: () => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"open\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tconsole.log(\"websocket connect open\");\n\t// \t\t\t\t},\n\t// \t\t\t\tonError: (err) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"error\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonError(err);\n\t// \t\t\t\t},\n\t// \t\t\t\tonSuccess: (data) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"success\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonSuccess(data);\n\t// \t\t\t\t},\n\t// \t\t\t\tonClose: () => {\n\t// \t\t\t\t\tconsole.log(\"websocket connect close\");\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"close\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t},\n\t// \t\t\t});\n\t// \t\t} catch (error) {\n\t// \t\t\tonError(error);\n\t// \t\t}\n\t// \t}\n\t// };\n\n\t// const startRecording = useCallback(() => {\n\t// \tconsole.log(\"Starting recording for stages 1-3...\");\n\t// \tconst stream = webcamRef.current?.srcObject as MediaStream | null;\n\t// \tif (!stream) return;\n\n\t// \t// Create a canvas to normalize orientation\n\t// \tconst videoTrack = stream.getVideoTracks()[0];\n\t// \tconst settings = videoTrack.getSettings();\n\t// \tconst { width = videoConstraints.width, height = videoConstraints.height } = settings;\n\n\t// \tconst canvas = document.createElement(\"canvas\");\n\t// \tconst ctx = canvas.getContext(\"2d\");\n\n\t// \t// Always force portrait (swap width/height if landscape)\n\t// \tif (width > height) {\n\t// \t\tcanvas.width = height;\n\t// \t\tcanvas.height = width;\n\t// \t} else {\n\t// \t\tcanvas.width = width;\n\t// \t\tcanvas.height = height;\n\t// \t}\n\n\t// \tconst videoEl = document.createElement(\"video\");\n\t// \tvideoEl.srcObject = stream;\n\t// \tvideoEl.muted = true;\n\t// \tvideoEl.playsInline = true;\n\t// \tvideoEl.play();\n\n\t// \t// Draw video frames onto canvas\n\t// \tconst drawFrame = () => {\n\t// \t\t// Rotate if camera gives landscape frames\n\t// \t\tif (width > height) {\n\t// \t\t\tctx?.save();\n\t// \t\t\tctx?.translate(canvas.width, 0);\n\t// \t\t\tctx?.rotate(Math.PI / 2);\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, height, width);\n\t// \t\t\tctx?.restore();\n\t// \t\t} else {\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, width, height);\n\t// \t\t}\n\t// \t\trequestAnimationFrame(drawFrame);\n\t// \t};\n\t// \tdrawFrame();\n\n\t\t// Capture portrait-normalized stream\n\t\t// const canvasStream = canvas.captureStream(30); // 30fps\n\t\t// const mediaRecorderOptions = supportedTypes.length > 0 ? { mimeType: supportedTypes[0] } : {};\n\t\t// let mediaRecorder;\n\n\t\t// try {\n\t\t// \tmediaRecorder = new MediaRecorder(canvasStream, mediaRecorderOptions);\n\t\t// } catch (e) {\n\t\t// \tconsole.error(\"MediaRecorder init failed:\", e);\n\t\t// \tsetHasError(true);\n\t\t// \tsetShowLoader(false);\n\t\t// \treturn;\n\t\t// }\n\n\t// \trecordedBlobsRef.current = [];\n\t// \tmediaRecorder.ondataavailable = (event) => {\n\t// \t\tif (event?.data && event.data.size > 0 && recordedBlobsRef.current) {\n\t// \t\t\trecordedBlobsRef.current.push(event.data);\n\t// \t\t}\n\t// \t};\n\t// \tmediaRecorder.onstop = () => {\n\t// \t\tconsole.log(\"Recording stopped, total blobs:\", recordedBlobsRef?.current?.length);\n\t// \t\tuploadFinalVideo();\n\t// \t};\n\n\t// \tmediaRecorder.start(100); // 100ms chunks\n\t// \tmediaRecorderRef.current = mediaRecorder;\n\t// \tconsole.log(\"Recording started successfully (portrait normalized)\");\n\t// }, [supportedTypes, uploadFinalVideo]);\n\n\t// const { faceScanDetector, scanStage, setScanStage, resetScan, isModelLoaded, startScanSequence } = useFaceScan({\n\t// \tfaceScanId,\n\t// \tshopDomain,\n\t// \tonScanComplete: () => {\n\t// \t\tstopRecording();\n\t// \t},\n\t// \tonModelReady: () => {\n\t// \t\tsetModelReady(true);\n\t// \t},\n\t// \tonStartRecording: () => {\n\t// \t\tconsole.log(\"Stage 0 completed - starting recording for stages 1-3\");\n\t// \t\tstartRecording();\n\t// \t},\n\t// });\n\n\t// const startScan = useCallback(() => {\n\t// \tif (!isModelLoaded) return;\n\t// \tsetScanStage(0);\n\t// \tspeechService.playAudio(`${voiceOverAssetsPath}face-scan-vos/Face-forward.mp3`);\n\t// \tsetTimeout(() => {\n\t// \t\tsetIsScanning(true);\n\t// \t\tsetTimeout(() => {\n\t// \t\t\tstartScanSequence();\n\t// \t\t}, 200);\n\t// // \t}, 200);\n\t// // }, [setScanStage, setIsScanning, startScanSequence, isModelLoaded]);\n\n\t// const resetScanState = useCallback(() => {\n\t// \tsetIsScanning(false);\n\t// \tsetShowGuideCard(true);\n\t// \tstopRecording();\n\t// \tresetScan();\n\t// \tonRetry?.();\n\t// \trecordedBlobsRef.current = [];\n\t// \tif (mediaRecorderRef.current) {\n\t// \t\tmediaRecorderRef.current = null;\n\t// \t}\n\t// \tsetHasError(false);\n\t// \tsetScanStage(-1);\n\t// \tsetFaceScanId(generateUuid());\n\t// \tif (webcamRef.current && streamRef.current) {\n\t// \t\twebcamRef.current.srcObject = streamRef.current;\n\t// \t\tconsole.log(\"Camera stream restored after reset\");\n\t// \t}\n\t// }, [setScanStage, setIsScanning, setShowGuideCard, stopRecording, resetScan, setFaceScanId]);\n\n\t// const onError = (data: any) => {\n\t// \tconsole.log(data, \"ws error\");\n\t// \tonScanError?.(data);\n\t// \tsetHasError(true);\n\t// \tsetShowLoader(false);\n\t// \tsetShowGuideCard(false);\n\t// \thandleScanTimeCapture({\n\t// \t\teventName: `${shopDomain}/faceScan`,\n\t// \t\tfaceScanId,\n\t// \t\tstatus: \"failed\",\n\t// \t\temail,\n\t// \t\tdata: JSON.stringify(data),\n\t// \t});\n\t// };\n\n\t// const onSuccess = (data: any) => {\n\t// \tconsole.log(data, \"ws success\");\n\t// \tif (data && data?.resultType === \"intermediate\") {\n\t// \t\thandleScanTimeCapture({\n\t// \t\t\teventName: `${shopDomain}/faceScan_success/intermediate`,\n\t// \t\t\tfaceScanId,\n\t// \t\t\tstatus: \"success\",\n\t// \t\t\temail,\n\t// \t\t\tdata: JSON.stringify(data),\n\t// \t\t});\n\t// \t}\n\t// \tonComplete?.(data);\n\t// };\n\n\t// Initialize webcam\n\t// useEffect(() => {\n\t// \tif (isError || isSuccess) return;\n\t// \tif (navigator.mediaDevices.getUserMedia) {\n\t// \t\tnavigator.mediaDevices\n\t// \t\t\t.getUserMedia({ video: videoConstraints })\n\t// \t\t\t.then((stream) => {\n\t// \t\t\t\tstreamRef.current = stream;\n\t// \t\t\t\tif (webcamRef.current) {\n\t// \t\t\t\t\twebcamRef.current.srcObject = stream;\n\t// \t\t\t\t\tconsole.log(\"Webcam initialized\");\n\t// \t\t\t\t}\n\t// \t\t\t})\n\t// \t\t\t.catch((err) => console.error(\"Error accessing webcam:\", err));\n\t// \t}\n\n\t\t// Cleanup function\n\t// \treturn () => {\n\t// \t\tif (streamRef.current) {\n\t// \t\t\tstreamRef.current.getTracks().forEach((track) => track.stop());\n\t// \t\t}\n\t// \t};\n\t// }, [isError, isSuccess]);\n\n\t// // Face detection interval - only run when scanning is active\n\t// useEffect(() => {\n\t// \tif (!modelReady || !isScanning) return;\n\n\t// \tconst intervalId = setInterval(() => {\n\t// \t\tfaceScanDetector(webcamRef, canvasRef);\n\t// \t}, 500);\n\n\t// \t// eslint-disable-next-line consistent-return\n\t// \treturn () => clearInterval(intervalId);\n\t// }, [faceScanDetector, modelReady, isScanning]);\n\n\t// const getButtonText = () => {\n\t// \tif (isScanning) return \"Reset\";\n\t// \tif (!isModelLoaded) return \"Loading...\";\n\t// \treturn \"Start\";\n\t// };\n\n\t// console.log(\"Model ready:\", modelReady, \"Has error:\", hasError, \"Is scanning:\", isScanning, \"showLoader\", showLoader);\n\n\t// useEffect(() => {\n\t// \tsetScanStage(-1);\n\t// \tif (isError || isSuccess) return;\n\t// \tposthog.init(posthogPublicKey, { api_host: posthogPublicHost });\n\t// \tposthog.capture(\"$pageview\");\n\t// }, [isError, isSuccess]);\n\n\tif (isError) {\n\t\treturn <FaceScanErrorScreen config={resolvedConfig} />;\n\t}\n\n\tif (isSuccess) {\n\t\treturn (\n\t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t\t\t\t{/* <Asset genderType={gender} /> */}\n\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} loaderType={\"default\"} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// return (\n\t// \t<>\n\t// \t\t<audio id=\"audioElement\" crossOrigin=\"anonymous\" preload=\"auto\" style={{ position: \"absolute\", zIndex: -99999 }} src=\"\" />\n\n\t// \t\t{showLoader && !hasError && (\n\t// \t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t// \t\t\t\t{/* <Asset genderType={gender} /> */}\n\t// \t\t\t\t<LoadingScreen url={resolvedConfig?.loader} />\n\t// \t\t\t</div>\n\t// \t\t)}\n\n\t\t\t{/* Always show camera view */}\n\t\t\t// <div className=\"h-full flex-col relative w-full flex justify-center items-center text-center rounded-t-[20px]\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t// \t<div className=\"flex-1 w-full max-w-md overflow-hidden\">\n\t\t\t// \t\t<div className=\"w-full h-full\">\n\t\t\t// \t\t\t<video\n\t\t\t// \t\t\t\tref={webcamRef}\n\t\t\t// \t\t\t\tautoPlay\n\t\t\t// \t\t\t\tplaysInline\n\t\t\t// \t\t\t\tmuted\n\t\t\t// \t\t\t\twidth={videoConstraints.width}\n\t\t\t// \t\t\t\theight={videoConstraints.height}\n\t\t\t// \t\t\t\tclassName=\"w-full h-full object-cover fixed left-0 top-0 z-0\"\n\t\t\t// \t\t\t\tstyle={{ transform: \"scaleX(-1)\" }}\n\t\t\t// \t\t\t/>\n\t\t\t// \t\t\t<canvas ref={canvasRef} width={videoConstraints.width} height={videoConstraints.height} style={{ transform: \"scaleX(-1)\", opacity: \"0\" }} />\n\t\t\t// \t\t</div>\n\t\t\t// \t</div>\n\t\t\t// </div>\n\n\t\t\t{/* Error overlay */}\n\t\t\t// {!showLoader && hasError && <FaceScanErrorScreen loading={showLoader} resetScanState={resetScanState} config={resolvedConfig} />}\n\n\t\t\t{/* Scan guide drawer - only show when scanning */}\n\t\t// \t{showGuideCard && !hasError && !showLoader && (\n\t\t// \t\t<Drawer\n\t\t// \t\t\topen\n\t\t// \t\t\tclassName=\"face-scan-small camera-draw\"\n\t\t// \t\t\tanchor=\"bottom\"\n\t\t// \t\t\tonClose={(event, reason) => {\n\t\t// \t\t\t\tif (reason === \"backdropClick\") {\n\t\t// \t\t\t\t\treturn;\n\t\t// \t\t\t\t}\n\t\t// \t\t\t}}\n\t\t// \t\t\thideBackdrop\n\t\t// \t\t>\n\t\t// \t\t\t<FaceScanGuide stage={scanStage} videoLoading={videoLoading} setVideoLoading={setVideoLoading} videoError={videoError} setVideoError={setVideoError} gender={gender} config={resolvedConfig}>\n\t\t// \t\t\t\t<div className=\"flex justify-center w-full p-[1rem]\">\n\t\t// \t\t\t\t\t<SpecificButton\n\t\t// \t\t\t\t\t\tdisabled={showLoader || !isModelLoaded}\n\t\t// \t\t\t\t\t\tclassName=\"!w-[60px] !h-[35px] !py-0 !px-0\"\n\t\t// \t\t\t\t\t\tbuttonText={getButtonText()}\n\t\t// \t\t\t\t\t\tpostfixIcon={isScanning ? <ArrowRight /> : \"\"}\n\t\t// \t\t\t\t\t\tbuttonFunc={isScanning ? resetScanState : startScan}\n\t\t// \t\t\t\t\t\tresolvedConfig={resolvedConfig}\n\t\t// \t\t\t\t\t/>\n\t\t// \t\t\t\t</div>\n\t\t// \t\t\t</FaceScanGuide>\n\t\t// \t\t</Drawer>\n\t\t// \t)}\n\t\t// </>\n\t// );\n};\n"],"names":["FaceScanErrorScreen","resetScanState","loading","config","_jsx","id","className","style","background","base","backgroundColor","children","_jsxs","Header","noTitle","resolvedConfig","fontFamily","heading","headingFontFamily","fontSize","headingFontSize","color","headingColor","fontWeight","headingFontWeight","subheading","subheadingFontFamily","subheadingFontSize","subheadingColor","subheadingFontWeight","SpecificButton","disabled","buttonText","postfixIcon","ArrowRight","buttonFunc","userDetails","onComplete","onScanError","onRetry","isError","isSuccess","useLocalConfig","primaryColor","LoadingScreen","url","loader","loaderType"],"mappings":"kKAIA,SAASA,GAAoBC,eAAEA,EAAcC,QAAEA,EAAOC,OAAEA,IACvD,OACCC,EAAAA,WAAKC,GAAG,uBAAuBC,UAAU,kFAAkFC,MAAO,CAAEC,WAAYL,GAAQI,OAAOE,MAAMC,iBAAiBC,SACrLC,OAAA,MAAA,CAAKN,UAAU,oEACdF,EAAAA,IAACS,EAAAA,QAAOC,SAAO,EAACC,eAAgBZ,IAChCS,OAAA,MAAA,CAAKN,UAAU,oCAAmCK,SAAA,CACjDP,EAAAA,IAAA,KAAA,CACCE,UAAU,uCACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOU,SAASC,mBAAqB,wBACzDC,SAAUhB,GAAQI,OAAOU,SAASG,iBAAmB,OACrDC,MAAOlB,GAAQI,OAAOU,SAASK,cAAgB,OAC/CC,WAAYpB,GAAQI,OAAOU,SAASO,mBAAqB,UACzDb,SAAA,qBAIFP,EAAAA,IAAA,IAAA,CACCE,UAAU,cACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOkB,YAAYC,sBAAwB,sBAC/DP,SAAUhB,GAAQI,OAAOkB,YAAYE,oBAAsB,OAC3DN,MAAOlB,GAAQI,OAAOkB,YAAYG,iBAAmB,UACrDL,WAAYpB,GAAQI,OAAOkB,YAAYI,sBAAwB,UAC/DlB,SAAA,2CAIFP,EAAAA,IAAC0B,EAAAA,eAAc,CACdC,SAAU7B,EACVI,UAAU,iEACV0B,WAAW,aACXC,YAAa7B,EAAAA,IAAC8B,EAAAA,WAAU,CAAA,GACxBC,WAAY,IAAMlC,MAClBc,eAAgBZ,WAMtB,mCC3BiD,EAAGiC,cAAaC,aAAYC,cAAaC,UAASpC,SAAOqC,UAAQC,gBAcjH,MAAM1B,EAAiB2B,EAAAA,eAAevC,GA4UtC,OAAIqC,EACIpC,EAAAA,IAACJ,EAAmB,CAACG,OAAQY,IAGjC0B,EAEFrC,EAAAA,IAAA,MAAA,CAAKE,UAAU,6BAA6BC,MAAO,CAAEC,WAAYO,GAAgBR,OAAOE,MAAMkC,uBAE7FvC,EAAAA,IAACwC,EAAAA,cAAa,CAACC,IAAK9B,GAAgB+B,OAAQC,WAAY,mBAJ3D"}
|
package/dist/faceScan.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as e,jsxs as
|
|
1
|
+
import{jsx as e,jsxs as l}from"react/jsx-runtime";import{H as t,S as o,u as n}from"./Header-BhrDARhH.js";import{A as s}from"./arrow-right-DXQbS3wL.js";import{L as i}from"./LoadingScreen-BXF96hkf.js";import"react";function a({resetScanState:n,loading:i,config:a}){return e("div",{id:"faceScanErrorSuccess",className:"fixed top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full",style:{background:a?.style?.base?.backgroundColor},children:l("div",{className:"flex flex-col w-full items-center p-[1rem] rounded-lg ",children:[e(t,{noTitle:!0,resolvedConfig:a}),l("div",{className:"flex flex-col items-center w-full",children:[e("h2",{className:"text-xl font-semibold text-gray-800 ",style:{fontFamily:a?.style?.heading?.headingFontFamily||"SeriouslyNostalgic Fn",fontSize:a?.style?.heading?.headingFontSize||"32px",color:a?.style?.heading?.headingColor||"#000",fontWeight:a?.style?.heading?.headingFontWeight||"normal"},children:"Your Scan Failed"}),e("p",{className:"mb-[1.5rem]",style:{fontFamily:a?.style?.subheading?.subheadingFontFamily||"'Inter', sans-serif",fontSize:a?.style?.subheading?.subheadingFontSize||"14px",color:a?.style?.subheading?.subheadingColor||"#4b5563",fontWeight:a?.style?.subheading?.subheadingFontWeight||"normal"},children:"Please click below to reset your scan."}),e(o,{disabled:i,className:"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]",buttonText:"Reset Scan",postfixIcon:e(s,{}),buttonFunc:()=>n?.(),resolvedConfig:a})]})]})})}const r=({userDetails:l,onComplete:t,onScanError:o,onRetry:s,config:r,isError:c,isSuccess:d})=>{const f=n(r);return c?e(a,{config:f}):d?e("div",{className:"fixed z-[9] w-full h-full",style:{background:f?.style?.base?.primaryColor},children:e(i,{url:f?.loader,loaderType:"default"})}):void 0};export{r as FaceScan};
|
|
2
2
|
//# sourceMappingURL=faceScan.mjs.map
|
package/dist/faceScan.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faceScan.mjs","sources":["../src/components/faceScan/FaceScanErrorScreen.tsx","../src/components/faceScan/FaceScan.tsx"],"sourcesContent":["import { ArrowRight } from \"lucide-react\";\nimport Header from \"../Header\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\n\nfunction FaceScanErrorScreen({ resetScanState, loading, config }: { resetScanState?: () => void; loading?: boolean; config?: any }) {\n\treturn (\n\t\t<div id=\"faceScanErrorSuccess\" className=\"fixed top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full\" style={{ background: config?.style?.base?.backgroundColor }}>\n\t\t\t<div className=\"flex flex-col w-full items-center p-[1rem] rounded-lg \">\n\t\t\t\t<Header noTitle resolvedConfig={config} />\n\t\t\t\t<div className=\"flex flex-col items-center w-full\">\n\t\t\t\t\t<h2\n\t\t\t\t\t\tclassName=\"text-xl font-semibold text-gray-800 \"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\tfontSize: config?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\tcolor: config?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tYour Scan Failed\n\t\t\t\t\t</h2>\n\t\t\t\t\t<p\n\t\t\t\t\t\tclassName=\"mb-[1.5rem]\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.subheading?.subheadingFontFamily || \"'Inter', sans-serif\",\n\t\t\t\t\t\t\tfontSize: config?.style?.subheading?.subheadingFontSize || \"14px\",\n\t\t\t\t\t\t\tcolor: config?.style?.subheading?.subheadingColor || \"#4b5563\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.subheading?.subheadingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tPlease click below to reset your scan.\n\t\t\t\t\t</p>\n\t\t\t\t\t<SpecificButton\n\t\t\t\t\t\tdisabled={loading}\n\t\t\t\t\t\tclassName=\"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]\"\n\t\t\t\t\t\tbuttonText=\"Reset Scan\"\n\t\t\t\t\t\tpostfixIcon={<ArrowRight />}\n\t\t\t\t\t\tbuttonFunc={() => resetScanState?.()}\n\t\t\t\t\t\tresolvedConfig={config}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default FaceScanErrorScreen;\n","\"use client\";\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ArrowRight } from \"lucide-react\";\nimport { Drawer } from \"@mui/material\";\nimport FaceScanGuide from \"./FaceScanGuide\";\nimport FaceScanErrorScreen from \"./FaceScanErrorScreen\";\nimport posthog from \"posthog-js\";\nimport { FaceScanProps } from \"../../types/interfaces\";\nimport { generateUuid, handleScanTimeCapture, handleWebSocketCapture } from \"../../utils/utils\";\nimport { useLocalConfig } from \"../../config/useLocalConfig\";\nimport { posthogPublicHost, posthogPublicKey, videoConstraints, videoTypes, voiceOverAssetsPath } from \"../../utils/constants\";\nimport swan from \"../../utils/service/swanService\";\nimport speechService from \"../../utils/service/speechService\";\nimport useFaceScan from \"../../customHooks/useFaceScan\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport LoadingScreen from \"../LoadingScreen\";\n\nexport const FaceScan: React.FC<FaceScanProps> = ({ userDetails, onComplete, onScanError, onRetry, config,isError,isSuccess }) => {\n\t// const webcamRef = useRef<HTMLVideoElement | null>(null);\n\t// const canvasRef = useRef<HTMLCanvasElement | null>(null);\n\t// const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n\t// const recordedBlobsRef = useRef<Blob[] | null>([]);\n\t// const streamRef = useRef<MediaStream | null>(null);\n\t// const [showLoader, setShowLoader] = useState(false);\n\t// const [modelReady, setModelReady] = useState(false);\n\t// const [isScanning, setIsScanning] = useState(false);\n\t// const [showGuideCard, setShowGuideCard] = useState(true);\n\t// const [videoLoading, setVideoLoading] = useState(false);\n\t// const [videoError, setVideoError] = useState(false);\n\t// const [hasError, setHasError] = useState(false);\n\t// const [faceScanId, setFaceScanId] = useState(generateUuid());\n\tconst resolvedConfig = useLocalConfig(config);\n\t// const { email, gender, deviceFocalLength, shopDomain, callbackUrl } = userDetails;\n\t// const supportedTypes = useMemo(() => videoTypes.filter((type) => MediaRecorder.isTypeSupported(type)), []);\n\n\t// const stopRecording = useCallback(() => {\n\t// \tconsole.log(\"Stopping recording...\");\n\t// \tif (mediaRecorderRef.current && mediaRecorderRef.current.state === \"recording\") {\n\t// \t\tmediaRecorderRef.current.stop();\n\t// \t}\n\t// \tmediaRecorderRef.current = null;\n\t// }, []);\n\n\t// const uploadFinalVideo = async () => {\n\t// \tsetShowLoader(true);\n\t// \tconsole.log(\"upload final video\");\n\t// \tif (recordedBlobsRef.current) {\n\t// \t\tif (recordedBlobsRef.current.length === 0) {\n\t// \t\t\tconsole.error(\"No video data recorded\");\n\t// \t\t\tsetHasError(true);\n\t// \t\t\tsetShowLoader(false);\n\n\t// \t\t\treturn;\n\t// \t\t}\n\n\t\t\t// const videoFile = new File(recordedBlobsRef.current, `${faceScanId}.webm`, {\n\t\t\t// \ttype: \"video/webm\",\n\t\t\t// });\n\t\t\t// const fileSize = videoFile.size;\n\t\t\t// const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2);\n\t\t\t// const estimatedDuration = Math.round(fileSize / 10000);\n\t\t\t// const videoData = {\n\t\t\t// \tvideo_size_mb: parseFloat(fileSizeMB),\n\t\t\t// \tvideo_size_bytes: fileSize,\n\t\t\t// \tblob_count: recordedBlobsRef.current.length,\n\t\t\t// \testimated_duration_seconds: estimatedDuration,\n\t\t\t// };\n\n\t\t\t// const metaData = [\n\t\t\t// \t{ gender: gender },\n\t\t\t// \t{ face_scan_id: faceScanId },\n\t\t\t// \t{\n\t\t\t// \t\tfocal_length: `${deviceFocalLength}`,\n\t\t\t// \t},\n\t\t\t// \t{ customer_store_url: shopDomain },\n\t\t\t// \t{ scan_type: \"face_scan\" },\n\t\t\t// \t{ callback_url: callbackUrl },\n\t\t\t// ];\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_meta_data`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tdata: JSON.stringify({ metaData, videoData }),\n\t\t\t// });\n\n\t\t\t// const uploadStartTime = Date.now();\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_upload_start`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tstartTime: uploadStartTime,\n\t\t\t// });\n\n\t// \t\ttry {\n\t// \t\t\tconst res = await swan.fileUpload.faceScanFileUploader({\n\t// \t\t\t\tfile: videoFile,\n\t// \t\t\t\tobjectKey: faceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tarrayMetaData: metaData,\n\t// \t\t\t\tcontentType: videoFile.type,\n\t// \t\t\t});\n\n\t// \t\t\tconst uploadEndTime = Date.now();\n\t// \t\t\tconst uploadDuration = uploadEndTime - uploadStartTime;\n\n\t// \t\t\thandleScanTimeCapture({\n\t// \t\t\t\teventName: `${shopDomain}/face_scan_upload_complete`,\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tcompletionTime: uploadEndTime,\n\t// \t\t\t\tuploadDuration,\n\t// \t\t\t});\n\n\t// \t\t\tconsole.log(\"✅ Upload successful\", res);\n\t// \t\t\tswan.measurement.handlFaceScaneSocket({\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\tonOpen: () => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"open\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tconsole.log(\"websocket connect open\");\n\t// \t\t\t\t},\n\t// \t\t\t\tonError: (err) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"error\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonError(err);\n\t// \t\t\t\t},\n\t// \t\t\t\tonSuccess: (data) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"success\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonSuccess(data);\n\t// \t\t\t\t},\n\t// \t\t\t\tonClose: () => {\n\t// \t\t\t\t\tconsole.log(\"websocket connect close\");\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"close\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t},\n\t// \t\t\t});\n\t// \t\t} catch (error) {\n\t// \t\t\tonError(error);\n\t// \t\t}\n\t// \t}\n\t// };\n\n\t// const startRecording = useCallback(() => {\n\t// \tconsole.log(\"Starting recording for stages 1-3...\");\n\t// \tconst stream = webcamRef.current?.srcObject as MediaStream | null;\n\t// \tif (!stream) return;\n\n\t// \t// Create a canvas to normalize orientation\n\t// \tconst videoTrack = stream.getVideoTracks()[0];\n\t// \tconst settings = videoTrack.getSettings();\n\t// \tconst { width = videoConstraints.width, height = videoConstraints.height } = settings;\n\n\t// \tconst canvas = document.createElement(\"canvas\");\n\t// \tconst ctx = canvas.getContext(\"2d\");\n\n\t// \t// Always force portrait (swap width/height if landscape)\n\t// \tif (width > height) {\n\t// \t\tcanvas.width = height;\n\t// \t\tcanvas.height = width;\n\t// \t} else {\n\t// \t\tcanvas.width = width;\n\t// \t\tcanvas.height = height;\n\t// \t}\n\n\t// \tconst videoEl = document.createElement(\"video\");\n\t// \tvideoEl.srcObject = stream;\n\t// \tvideoEl.muted = true;\n\t// \tvideoEl.playsInline = true;\n\t// \tvideoEl.play();\n\n\t// \t// Draw video frames onto canvas\n\t// \tconst drawFrame = () => {\n\t// \t\t// Rotate if camera gives landscape frames\n\t// \t\tif (width > height) {\n\t// \t\t\tctx?.save();\n\t// \t\t\tctx?.translate(canvas.width, 0);\n\t// \t\t\tctx?.rotate(Math.PI / 2);\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, height, width);\n\t// \t\t\tctx?.restore();\n\t// \t\t} else {\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, width, height);\n\t// \t\t}\n\t// \t\trequestAnimationFrame(drawFrame);\n\t// \t};\n\t// \tdrawFrame();\n\n\t\t// Capture portrait-normalized stream\n\t\t// const canvasStream = canvas.captureStream(30); // 30fps\n\t\t// const mediaRecorderOptions = supportedTypes.length > 0 ? { mimeType: supportedTypes[0] } : {};\n\t\t// let mediaRecorder;\n\n\t\t// try {\n\t\t// \tmediaRecorder = new MediaRecorder(canvasStream, mediaRecorderOptions);\n\t\t// } catch (e) {\n\t\t// \tconsole.error(\"MediaRecorder init failed:\", e);\n\t\t// \tsetHasError(true);\n\t\t// \tsetShowLoader(false);\n\t\t// \treturn;\n\t\t// }\n\n\t// \trecordedBlobsRef.current = [];\n\t// \tmediaRecorder.ondataavailable = (event) => {\n\t// \t\tif (event?.data && event.data.size > 0 && recordedBlobsRef.current) {\n\t// \t\t\trecordedBlobsRef.current.push(event.data);\n\t// \t\t}\n\t// \t};\n\t// \tmediaRecorder.onstop = () => {\n\t// \t\tconsole.log(\"Recording stopped, total blobs:\", recordedBlobsRef?.current?.length);\n\t// \t\tuploadFinalVideo();\n\t// \t};\n\n\t// \tmediaRecorder.start(100); // 100ms chunks\n\t// \tmediaRecorderRef.current = mediaRecorder;\n\t// \tconsole.log(\"Recording started successfully (portrait normalized)\");\n\t// }, [supportedTypes, uploadFinalVideo]);\n\n\t// const { faceScanDetector, scanStage, setScanStage, resetScan, isModelLoaded, startScanSequence } = useFaceScan({\n\t// \tfaceScanId,\n\t// \tshopDomain,\n\t// \tonScanComplete: () => {\n\t// \t\tstopRecording();\n\t// \t},\n\t// \tonModelReady: () => {\n\t// \t\tsetModelReady(true);\n\t// \t},\n\t// \tonStartRecording: () => {\n\t// \t\tconsole.log(\"Stage 0 completed - starting recording for stages 1-3\");\n\t// \t\tstartRecording();\n\t// \t},\n\t// });\n\n\t// const startScan = useCallback(() => {\n\t// \tif (!isModelLoaded) return;\n\t// \tsetScanStage(0);\n\t// \tspeechService.playAudio(`${voiceOverAssetsPath}face-scan-vos/Face-forward.mp3`);\n\t// \tsetTimeout(() => {\n\t// \t\tsetIsScanning(true);\n\t// \t\tsetTimeout(() => {\n\t// \t\t\tstartScanSequence();\n\t// \t\t}, 200);\n\t// // \t}, 200);\n\t// // }, [setScanStage, setIsScanning, startScanSequence, isModelLoaded]);\n\n\t// const resetScanState = useCallback(() => {\n\t// \tsetIsScanning(false);\n\t// \tsetShowGuideCard(true);\n\t// \tstopRecording();\n\t// \tresetScan();\n\t// \tonRetry?.();\n\t// \trecordedBlobsRef.current = [];\n\t// \tif (mediaRecorderRef.current) {\n\t// \t\tmediaRecorderRef.current = null;\n\t// \t}\n\t// \tsetHasError(false);\n\t// \tsetScanStage(-1);\n\t// \tsetFaceScanId(generateUuid());\n\t// \tif (webcamRef.current && streamRef.current) {\n\t// \t\twebcamRef.current.srcObject = streamRef.current;\n\t// \t\tconsole.log(\"Camera stream restored after reset\");\n\t// \t}\n\t// }, [setScanStage, setIsScanning, setShowGuideCard, stopRecording, resetScan, setFaceScanId]);\n\n\t// const onError = (data: any) => {\n\t// \tconsole.log(data, \"ws error\");\n\t// \tonScanError?.(data);\n\t// \tsetHasError(true);\n\t// \tsetShowLoader(false);\n\t// \tsetShowGuideCard(false);\n\t// \thandleScanTimeCapture({\n\t// \t\teventName: `${shopDomain}/faceScan`,\n\t// \t\tfaceScanId,\n\t// \t\tstatus: \"failed\",\n\t// \t\temail,\n\t// \t\tdata: JSON.stringify(data),\n\t// \t});\n\t// };\n\n\t// const onSuccess = (data: any) => {\n\t// \tconsole.log(data, \"ws success\");\n\t// \tif (data && data?.resultType === \"intermediate\") {\n\t// \t\thandleScanTimeCapture({\n\t// \t\t\teventName: `${shopDomain}/faceScan_success/intermediate`,\n\t// \t\t\tfaceScanId,\n\t// \t\t\tstatus: \"success\",\n\t// \t\t\temail,\n\t// \t\t\tdata: JSON.stringify(data),\n\t// \t\t});\n\t// \t}\n\t// \tonComplete?.(data);\n\t// };\n\n\t// Initialize webcam\n\t// useEffect(() => {\n\t// \tif (isError || isSuccess) return;\n\t// \tif (navigator.mediaDevices.getUserMedia) {\n\t// \t\tnavigator.mediaDevices\n\t// \t\t\t.getUserMedia({ video: videoConstraints })\n\t// \t\t\t.then((stream) => {\n\t// \t\t\t\tstreamRef.current = stream;\n\t// \t\t\t\tif (webcamRef.current) {\n\t// \t\t\t\t\twebcamRef.current.srcObject = stream;\n\t// \t\t\t\t\tconsole.log(\"Webcam initialized\");\n\t// \t\t\t\t}\n\t// \t\t\t})\n\t// \t\t\t.catch((err) => console.error(\"Error accessing webcam:\", err));\n\t// \t}\n\n\t\t// Cleanup function\n\t// \treturn () => {\n\t// \t\tif (streamRef.current) {\n\t// \t\t\tstreamRef.current.getTracks().forEach((track) => track.stop());\n\t// \t\t}\n\t// \t};\n\t// }, [isError, isSuccess]);\n\n\t// // Face detection interval - only run when scanning is active\n\t// useEffect(() => {\n\t// \tif (!modelReady || !isScanning) return;\n\n\t// \tconst intervalId = setInterval(() => {\n\t// \t\tfaceScanDetector(webcamRef, canvasRef);\n\t// \t}, 500);\n\n\t// \t// eslint-disable-next-line consistent-return\n\t// \treturn () => clearInterval(intervalId);\n\t// }, [faceScanDetector, modelReady, isScanning]);\n\n\t// const getButtonText = () => {\n\t// \tif (isScanning) return \"Reset\";\n\t// \tif (!isModelLoaded) return \"Loading...\";\n\t// \treturn \"Start\";\n\t// };\n\n\t// console.log(\"Model ready:\", modelReady, \"Has error:\", hasError, \"Is scanning:\", isScanning, \"showLoader\", showLoader);\n\n\t// useEffect(() => {\n\t// \tsetScanStage(-1);\n\t// \tif (isError || isSuccess) return;\n\t// \tposthog.init(posthogPublicKey, { api_host: posthogPublicHost });\n\t// \tposthog.capture(\"$pageview\");\n\t// }, [isError, isSuccess]);\n\n\tif (isError) {\n\t\treturn <FaceScanErrorScreen config={resolvedConfig} />;\n\t}\n\n\tif (isSuccess) {\n\t\treturn (\n\t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t\t\t\t{/* <Asset genderType={gender} /> */}\n\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// return (\n\t// \t<>\n\t// \t\t<audio id=\"audioElement\" crossOrigin=\"anonymous\" preload=\"auto\" style={{ position: \"absolute\", zIndex: -99999 }} src=\"\" />\n\n\t// \t\t{showLoader && !hasError && (\n\t// \t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t// \t\t\t\t{/* <Asset genderType={gender} /> */}\n\t// \t\t\t\t<LoadingScreen url={resolvedConfig?.loader} />\n\t// \t\t\t</div>\n\t// \t\t)}\n\n\t\t\t{/* Always show camera view */}\n\t\t\t// <div className=\"h-full flex-col relative w-full flex justify-center items-center text-center rounded-t-[20px]\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t// \t<div className=\"flex-1 w-full max-w-md overflow-hidden\">\n\t\t\t// \t\t<div className=\"w-full h-full\">\n\t\t\t// \t\t\t<video\n\t\t\t// \t\t\t\tref={webcamRef}\n\t\t\t// \t\t\t\tautoPlay\n\t\t\t// \t\t\t\tplaysInline\n\t\t\t// \t\t\t\tmuted\n\t\t\t// \t\t\t\twidth={videoConstraints.width}\n\t\t\t// \t\t\t\theight={videoConstraints.height}\n\t\t\t// \t\t\t\tclassName=\"w-full h-full object-cover fixed left-0 top-0 z-0\"\n\t\t\t// \t\t\t\tstyle={{ transform: \"scaleX(-1)\" }}\n\t\t\t// \t\t\t/>\n\t\t\t// \t\t\t<canvas ref={canvasRef} width={videoConstraints.width} height={videoConstraints.height} style={{ transform: \"scaleX(-1)\", opacity: \"0\" }} />\n\t\t\t// \t\t</div>\n\t\t\t// \t</div>\n\t\t\t// </div>\n\n\t\t\t{/* Error overlay */}\n\t\t\t// {!showLoader && hasError && <FaceScanErrorScreen loading={showLoader} resetScanState={resetScanState} config={resolvedConfig} />}\n\n\t\t\t{/* Scan guide drawer - only show when scanning */}\n\t\t// \t{showGuideCard && !hasError && !showLoader && (\n\t\t// \t\t<Drawer\n\t\t// \t\t\topen\n\t\t// \t\t\tclassName=\"face-scan-small camera-draw\"\n\t\t// \t\t\tanchor=\"bottom\"\n\t\t// \t\t\tonClose={(event, reason) => {\n\t\t// \t\t\t\tif (reason === \"backdropClick\") {\n\t\t// \t\t\t\t\treturn;\n\t\t// \t\t\t\t}\n\t\t// \t\t\t}}\n\t\t// \t\t\thideBackdrop\n\t\t// \t\t>\n\t\t// \t\t\t<FaceScanGuide stage={scanStage} videoLoading={videoLoading} setVideoLoading={setVideoLoading} videoError={videoError} setVideoError={setVideoError} gender={gender} config={resolvedConfig}>\n\t\t// \t\t\t\t<div className=\"flex justify-center w-full p-[1rem]\">\n\t\t// \t\t\t\t\t<SpecificButton\n\t\t// \t\t\t\t\t\tdisabled={showLoader || !isModelLoaded}\n\t\t// \t\t\t\t\t\tclassName=\"!w-[60px] !h-[35px] !py-0 !px-0\"\n\t\t// \t\t\t\t\t\tbuttonText={getButtonText()}\n\t\t// \t\t\t\t\t\tpostfixIcon={isScanning ? <ArrowRight /> : \"\"}\n\t\t// \t\t\t\t\t\tbuttonFunc={isScanning ? resetScanState : startScan}\n\t\t// \t\t\t\t\t\tresolvedConfig={resolvedConfig}\n\t\t// \t\t\t\t\t/>\n\t\t// \t\t\t\t</div>\n\t\t// \t\t\t</FaceScanGuide>\n\t\t// \t\t</Drawer>\n\t\t// \t)}\n\t\t// </>\n\t// );\n};\n"],"names":["FaceScanErrorScreen","resetScanState","loading","config","_jsx","id","className","style","background","base","backgroundColor","children","_jsxs","Header","noTitle","resolvedConfig","fontFamily","heading","headingFontFamily","fontSize","headingFontSize","color","headingColor","fontWeight","headingFontWeight","subheading","subheadingFontFamily","subheadingFontSize","subheadingColor","subheadingFontWeight","SpecificButton","disabled","buttonText","postfixIcon","ArrowRight","buttonFunc","FaceScan","userDetails","onComplete","onScanError","onRetry","isError","isSuccess","useLocalConfig","primaryColor","LoadingScreen","url","loader"],"mappings":"qNAIA,SAASA,GAAoBC,eAAEA,EAAcC,QAAEA,EAAOC,OAAEA,IACvD,OACCC,SAAKC,GAAG,uBAAuBC,UAAU,kFAAkFC,MAAO,CAAEC,WAAYL,GAAQI,OAAOE,MAAMC,iBAAiBC,SACrLC,EAAA,MAAA,CAAKN,UAAU,oEACdF,EAACS,GAAOC,SAAO,EAACC,eAAgBZ,IAChCS,EAAA,MAAA,CAAKN,UAAU,oCAAmCK,SAAA,CACjDP,EAAA,KAAA,CACCE,UAAU,uCACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOU,SAASC,mBAAqB,wBACzDC,SAAUhB,GAAQI,OAAOU,SAASG,iBAAmB,OACrDC,MAAOlB,GAAQI,OAAOU,SAASK,cAAgB,OAC/CC,WAAYpB,GAAQI,OAAOU,SAASO,mBAAqB,UACzDb,SAAA,qBAIFP,EAAA,IAAA,CACCE,UAAU,cACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOkB,YAAYC,sBAAwB,sBAC/DP,SAAUhB,GAAQI,OAAOkB,YAAYE,oBAAsB,OAC3DN,MAAOlB,GAAQI,OAAOkB,YAAYG,iBAAmB,UACrDL,WAAYpB,GAAQI,OAAOkB,YAAYI,sBAAwB,UAC/DlB,SAAA,2CAIFP,EAAC0B,EAAc,CACdC,SAAU7B,EACVI,UAAU,iEACV0B,WAAW,aACXC,YAAa7B,EAAC8B,EAAU,CAAA,GACxBC,WAAY,IAAMlC,MAClBc,eAAgBZ,WAMtB,OC3BaiC,EAAoC,EAAGC,cAAaC,aAAYC,cAAaC,UAASrC,SAAOsC,UAAQC,gBAcjH,MAAM3B,EAAiB4B,EAAexC,GA4UtC,OAAIsC,EACIrC,EAACJ,EAAmB,CAACG,OAAQY,IAGjC2B,EAEFtC,EAAA,MAAA,CAAKE,UAAU,6BAA6BC,MAAO,CAAEC,WAAYO,GAAgBR,OAAOE,MAAMmC,cAAcjC,SAE3GP,EAACyC,EAAa,CAACC,IAAK/B,GAAgBgC,gBAJvC"}
|
|
1
|
+
{"version":3,"file":"faceScan.mjs","sources":["../src/components/faceScan/FaceScanErrorScreen.tsx","../src/components/faceScan/FaceScan.tsx"],"sourcesContent":["import { ArrowRight } from \"lucide-react\";\nimport Header from \"../Header\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\n\nfunction FaceScanErrorScreen({ resetScanState, loading, config }: { resetScanState?: () => void; loading?: boolean; config?: any }) {\n\treturn (\n\t\t<div id=\"faceScanErrorSuccess\" className=\"fixed top-[0] left-[0] z-[999] flex justify-center items-center w-full h-full\" style={{ background: config?.style?.base?.backgroundColor }}>\n\t\t\t<div className=\"flex flex-col w-full items-center p-[1rem] rounded-lg \">\n\t\t\t\t<Header noTitle resolvedConfig={config} />\n\t\t\t\t<div className=\"flex flex-col items-center w-full\">\n\t\t\t\t\t<h2\n\t\t\t\t\t\tclassName=\"text-xl font-semibold text-gray-800 \"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\tfontSize: config?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\tcolor: config?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tYour Scan Failed\n\t\t\t\t\t</h2>\n\t\t\t\t\t<p\n\t\t\t\t\t\tclassName=\"mb-[1.5rem]\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.subheading?.subheadingFontFamily || \"'Inter', sans-serif\",\n\t\t\t\t\t\t\tfontSize: config?.style?.subheading?.subheadingFontSize || \"14px\",\n\t\t\t\t\t\t\tcolor: config?.style?.subheading?.subheadingColor || \"#4b5563\",\n\t\t\t\t\t\t\tfontWeight: config?.style?.subheading?.subheadingFontWeight || \"normal\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\tPlease click below to reset your scan.\n\t\t\t\t\t</p>\n\t\t\t\t\t<SpecificButton\n\t\t\t\t\t\tdisabled={loading}\n\t\t\t\t\t\tclassName=\"w-full h-[45px] shadow-[0px_1px_2px_0px_#00000040] text-[16px]\"\n\t\t\t\t\t\tbuttonText=\"Reset Scan\"\n\t\t\t\t\t\tpostfixIcon={<ArrowRight />}\n\t\t\t\t\t\tbuttonFunc={() => resetScanState?.()}\n\t\t\t\t\t\tresolvedConfig={config}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default FaceScanErrorScreen;\n","\"use client\";\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { ArrowRight } from \"lucide-react\";\nimport { Drawer } from \"@mui/material\";\nimport FaceScanGuide from \"./FaceScanGuide\";\nimport FaceScanErrorScreen from \"./FaceScanErrorScreen\";\nimport posthog from \"posthog-js\";\nimport { FaceScanProps } from \"../../types/interfaces\";\nimport { generateUuid, handleScanTimeCapture, handleWebSocketCapture } from \"../../utils/utils\";\nimport { useLocalConfig } from \"../../config/useLocalConfig\";\nimport { posthogPublicHost, posthogPublicKey, videoConstraints, videoTypes, voiceOverAssetsPath } from \"../../utils/constants\";\nimport swan from \"../../utils/service/swanService\";\nimport speechService from \"../../utils/service/speechService\";\nimport useFaceScan from \"../../customHooks/useFaceScan\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport LoadingScreen from \"../LoadingScreen\";\n\nexport const FaceScan: React.FC<FaceScanProps> = ({ userDetails, onComplete, onScanError, onRetry, config,isError,isSuccess }) => {\n\t// const webcamRef = useRef<HTMLVideoElement | null>(null);\n\t// const canvasRef = useRef<HTMLCanvasElement | null>(null);\n\t// const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n\t// const recordedBlobsRef = useRef<Blob[] | null>([]);\n\t// const streamRef = useRef<MediaStream | null>(null);\n\t// const [showLoader, setShowLoader] = useState(false);\n\t// const [modelReady, setModelReady] = useState(false);\n\t// const [isScanning, setIsScanning] = useState(false);\n\t// const [showGuideCard, setShowGuideCard] = useState(true);\n\t// const [videoLoading, setVideoLoading] = useState(false);\n\t// const [videoError, setVideoError] = useState(false);\n\t// const [hasError, setHasError] = useState(false);\n\t// const [faceScanId, setFaceScanId] = useState(generateUuid());\n\tconst resolvedConfig = useLocalConfig(config);\n\t// const { email, gender, deviceFocalLength, shopDomain, callbackUrl } = userDetails;\n\t// const supportedTypes = useMemo(() => videoTypes.filter((type) => MediaRecorder.isTypeSupported(type)), []);\n\n\t// const stopRecording = useCallback(() => {\n\t// \tconsole.log(\"Stopping recording...\");\n\t// \tif (mediaRecorderRef.current && mediaRecorderRef.current.state === \"recording\") {\n\t// \t\tmediaRecorderRef.current.stop();\n\t// \t}\n\t// \tmediaRecorderRef.current = null;\n\t// }, []);\n\n\t// const uploadFinalVideo = async () => {\n\t// \tsetShowLoader(true);\n\t// \tconsole.log(\"upload final video\");\n\t// \tif (recordedBlobsRef.current) {\n\t// \t\tif (recordedBlobsRef.current.length === 0) {\n\t// \t\t\tconsole.error(\"No video data recorded\");\n\t// \t\t\tsetHasError(true);\n\t// \t\t\tsetShowLoader(false);\n\n\t// \t\t\treturn;\n\t// \t\t}\n\n\t\t\t// const videoFile = new File(recordedBlobsRef.current, `${faceScanId}.webm`, {\n\t\t\t// \ttype: \"video/webm\",\n\t\t\t// });\n\t\t\t// const fileSize = videoFile.size;\n\t\t\t// const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2);\n\t\t\t// const estimatedDuration = Math.round(fileSize / 10000);\n\t\t\t// const videoData = {\n\t\t\t// \tvideo_size_mb: parseFloat(fileSizeMB),\n\t\t\t// \tvideo_size_bytes: fileSize,\n\t\t\t// \tblob_count: recordedBlobsRef.current.length,\n\t\t\t// \testimated_duration_seconds: estimatedDuration,\n\t\t\t// };\n\n\t\t\t// const metaData = [\n\t\t\t// \t{ gender: gender },\n\t\t\t// \t{ face_scan_id: faceScanId },\n\t\t\t// \t{\n\t\t\t// \t\tfocal_length: `${deviceFocalLength}`,\n\t\t\t// \t},\n\t\t\t// \t{ customer_store_url: shopDomain },\n\t\t\t// \t{ scan_type: \"face_scan\" },\n\t\t\t// \t{ callback_url: callbackUrl },\n\t\t\t// ];\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_meta_data`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tdata: JSON.stringify({ metaData, videoData }),\n\t\t\t// });\n\n\t\t\t// const uploadStartTime = Date.now();\n\t\t\t// handleScanTimeCapture({\n\t\t\t// \teventName: `${shopDomain}/face_scan_upload_start`,\n\t\t\t// \tfaceScanId,\n\t\t\t// \temail,\n\t\t\t// \tstartTime: uploadStartTime,\n\t\t\t// });\n\n\t// \t\ttry {\n\t// \t\t\tconst res = await swan.fileUpload.faceScanFileUploader({\n\t// \t\t\t\tfile: videoFile,\n\t// \t\t\t\tobjectKey: faceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tarrayMetaData: metaData,\n\t// \t\t\t\tcontentType: videoFile.type,\n\t// \t\t\t});\n\n\t// \t\t\tconst uploadEndTime = Date.now();\n\t// \t\t\tconst uploadDuration = uploadEndTime - uploadStartTime;\n\n\t// \t\t\thandleScanTimeCapture({\n\t// \t\t\t\teventName: `${shopDomain}/face_scan_upload_complete`,\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\temail,\n\t// \t\t\t\tcompletionTime: uploadEndTime,\n\t// \t\t\t\tuploadDuration,\n\t// \t\t\t});\n\n\t// \t\t\tconsole.log(\"✅ Upload successful\", res);\n\t// \t\t\tswan.measurement.handlFaceScaneSocket({\n\t// \t\t\t\tfaceScanId,\n\t// \t\t\t\tonOpen: () => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"open\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tconsole.log(\"websocket connect open\");\n\t// \t\t\t\t},\n\t// \t\t\t\tonError: (err) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"error\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonError(err);\n\t// \t\t\t\t},\n\t// \t\t\t\tonSuccess: (data) => {\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"success\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t\tonSuccess(data);\n\t// \t\t\t\t},\n\t// \t\t\t\tonClose: () => {\n\t// \t\t\t\t\tconsole.log(\"websocket connect close\");\n\t// \t\t\t\t\thandleWebSocketCapture({\n\t// \t\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t// \t\t\t\t\t\tfaceScanID: faceScanId,\n\t// \t\t\t\t\t\tconnection: \"close\",\n\t// \t\t\t\t\t\ttype: \"faceScan_recommendation\",\n\t// \t\t\t\t\t\temail,\n\t// \t\t\t\t\t});\n\t// \t\t\t\t},\n\t// \t\t\t});\n\t// \t\t} catch (error) {\n\t// \t\t\tonError(error);\n\t// \t\t}\n\t// \t}\n\t// };\n\n\t// const startRecording = useCallback(() => {\n\t// \tconsole.log(\"Starting recording for stages 1-3...\");\n\t// \tconst stream = webcamRef.current?.srcObject as MediaStream | null;\n\t// \tif (!stream) return;\n\n\t// \t// Create a canvas to normalize orientation\n\t// \tconst videoTrack = stream.getVideoTracks()[0];\n\t// \tconst settings = videoTrack.getSettings();\n\t// \tconst { width = videoConstraints.width, height = videoConstraints.height } = settings;\n\n\t// \tconst canvas = document.createElement(\"canvas\");\n\t// \tconst ctx = canvas.getContext(\"2d\");\n\n\t// \t// Always force portrait (swap width/height if landscape)\n\t// \tif (width > height) {\n\t// \t\tcanvas.width = height;\n\t// \t\tcanvas.height = width;\n\t// \t} else {\n\t// \t\tcanvas.width = width;\n\t// \t\tcanvas.height = height;\n\t// \t}\n\n\t// \tconst videoEl = document.createElement(\"video\");\n\t// \tvideoEl.srcObject = stream;\n\t// \tvideoEl.muted = true;\n\t// \tvideoEl.playsInline = true;\n\t// \tvideoEl.play();\n\n\t// \t// Draw video frames onto canvas\n\t// \tconst drawFrame = () => {\n\t// \t\t// Rotate if camera gives landscape frames\n\t// \t\tif (width > height) {\n\t// \t\t\tctx?.save();\n\t// \t\t\tctx?.translate(canvas.width, 0);\n\t// \t\t\tctx?.rotate(Math.PI / 2);\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, height, width);\n\t// \t\t\tctx?.restore();\n\t// \t\t} else {\n\t// \t\t\tctx?.drawImage(videoEl, 0, 0, width, height);\n\t// \t\t}\n\t// \t\trequestAnimationFrame(drawFrame);\n\t// \t};\n\t// \tdrawFrame();\n\n\t\t// Capture portrait-normalized stream\n\t\t// const canvasStream = canvas.captureStream(30); // 30fps\n\t\t// const mediaRecorderOptions = supportedTypes.length > 0 ? { mimeType: supportedTypes[0] } : {};\n\t\t// let mediaRecorder;\n\n\t\t// try {\n\t\t// \tmediaRecorder = new MediaRecorder(canvasStream, mediaRecorderOptions);\n\t\t// } catch (e) {\n\t\t// \tconsole.error(\"MediaRecorder init failed:\", e);\n\t\t// \tsetHasError(true);\n\t\t// \tsetShowLoader(false);\n\t\t// \treturn;\n\t\t// }\n\n\t// \trecordedBlobsRef.current = [];\n\t// \tmediaRecorder.ondataavailable = (event) => {\n\t// \t\tif (event?.data && event.data.size > 0 && recordedBlobsRef.current) {\n\t// \t\t\trecordedBlobsRef.current.push(event.data);\n\t// \t\t}\n\t// \t};\n\t// \tmediaRecorder.onstop = () => {\n\t// \t\tconsole.log(\"Recording stopped, total blobs:\", recordedBlobsRef?.current?.length);\n\t// \t\tuploadFinalVideo();\n\t// \t};\n\n\t// \tmediaRecorder.start(100); // 100ms chunks\n\t// \tmediaRecorderRef.current = mediaRecorder;\n\t// \tconsole.log(\"Recording started successfully (portrait normalized)\");\n\t// }, [supportedTypes, uploadFinalVideo]);\n\n\t// const { faceScanDetector, scanStage, setScanStage, resetScan, isModelLoaded, startScanSequence } = useFaceScan({\n\t// \tfaceScanId,\n\t// \tshopDomain,\n\t// \tonScanComplete: () => {\n\t// \t\tstopRecording();\n\t// \t},\n\t// \tonModelReady: () => {\n\t// \t\tsetModelReady(true);\n\t// \t},\n\t// \tonStartRecording: () => {\n\t// \t\tconsole.log(\"Stage 0 completed - starting recording for stages 1-3\");\n\t// \t\tstartRecording();\n\t// \t},\n\t// });\n\n\t// const startScan = useCallback(() => {\n\t// \tif (!isModelLoaded) return;\n\t// \tsetScanStage(0);\n\t// \tspeechService.playAudio(`${voiceOverAssetsPath}face-scan-vos/Face-forward.mp3`);\n\t// \tsetTimeout(() => {\n\t// \t\tsetIsScanning(true);\n\t// \t\tsetTimeout(() => {\n\t// \t\t\tstartScanSequence();\n\t// \t\t}, 200);\n\t// // \t}, 200);\n\t// // }, [setScanStage, setIsScanning, startScanSequence, isModelLoaded]);\n\n\t// const resetScanState = useCallback(() => {\n\t// \tsetIsScanning(false);\n\t// \tsetShowGuideCard(true);\n\t// \tstopRecording();\n\t// \tresetScan();\n\t// \tonRetry?.();\n\t// \trecordedBlobsRef.current = [];\n\t// \tif (mediaRecorderRef.current) {\n\t// \t\tmediaRecorderRef.current = null;\n\t// \t}\n\t// \tsetHasError(false);\n\t// \tsetScanStage(-1);\n\t// \tsetFaceScanId(generateUuid());\n\t// \tif (webcamRef.current && streamRef.current) {\n\t// \t\twebcamRef.current.srcObject = streamRef.current;\n\t// \t\tconsole.log(\"Camera stream restored after reset\");\n\t// \t}\n\t// }, [setScanStage, setIsScanning, setShowGuideCard, stopRecording, resetScan, setFaceScanId]);\n\n\t// const onError = (data: any) => {\n\t// \tconsole.log(data, \"ws error\");\n\t// \tonScanError?.(data);\n\t// \tsetHasError(true);\n\t// \tsetShowLoader(false);\n\t// \tsetShowGuideCard(false);\n\t// \thandleScanTimeCapture({\n\t// \t\teventName: `${shopDomain}/faceScan`,\n\t// \t\tfaceScanId,\n\t// \t\tstatus: \"failed\",\n\t// \t\temail,\n\t// \t\tdata: JSON.stringify(data),\n\t// \t});\n\t// };\n\n\t// const onSuccess = (data: any) => {\n\t// \tconsole.log(data, \"ws success\");\n\t// \tif (data && data?.resultType === \"intermediate\") {\n\t// \t\thandleScanTimeCapture({\n\t// \t\t\teventName: `${shopDomain}/faceScan_success/intermediate`,\n\t// \t\t\tfaceScanId,\n\t// \t\t\tstatus: \"success\",\n\t// \t\t\temail,\n\t// \t\t\tdata: JSON.stringify(data),\n\t// \t\t});\n\t// \t}\n\t// \tonComplete?.(data);\n\t// };\n\n\t// Initialize webcam\n\t// useEffect(() => {\n\t// \tif (isError || isSuccess) return;\n\t// \tif (navigator.mediaDevices.getUserMedia) {\n\t// \t\tnavigator.mediaDevices\n\t// \t\t\t.getUserMedia({ video: videoConstraints })\n\t// \t\t\t.then((stream) => {\n\t// \t\t\t\tstreamRef.current = stream;\n\t// \t\t\t\tif (webcamRef.current) {\n\t// \t\t\t\t\twebcamRef.current.srcObject = stream;\n\t// \t\t\t\t\tconsole.log(\"Webcam initialized\");\n\t// \t\t\t\t}\n\t// \t\t\t})\n\t// \t\t\t.catch((err) => console.error(\"Error accessing webcam:\", err));\n\t// \t}\n\n\t\t// Cleanup function\n\t// \treturn () => {\n\t// \t\tif (streamRef.current) {\n\t// \t\t\tstreamRef.current.getTracks().forEach((track) => track.stop());\n\t// \t\t}\n\t// \t};\n\t// }, [isError, isSuccess]);\n\n\t// // Face detection interval - only run when scanning is active\n\t// useEffect(() => {\n\t// \tif (!modelReady || !isScanning) return;\n\n\t// \tconst intervalId = setInterval(() => {\n\t// \t\tfaceScanDetector(webcamRef, canvasRef);\n\t// \t}, 500);\n\n\t// \t// eslint-disable-next-line consistent-return\n\t// \treturn () => clearInterval(intervalId);\n\t// }, [faceScanDetector, modelReady, isScanning]);\n\n\t// const getButtonText = () => {\n\t// \tif (isScanning) return \"Reset\";\n\t// \tif (!isModelLoaded) return \"Loading...\";\n\t// \treturn \"Start\";\n\t// };\n\n\t// console.log(\"Model ready:\", modelReady, \"Has error:\", hasError, \"Is scanning:\", isScanning, \"showLoader\", showLoader);\n\n\t// useEffect(() => {\n\t// \tsetScanStage(-1);\n\t// \tif (isError || isSuccess) return;\n\t// \tposthog.init(posthogPublicKey, { api_host: posthogPublicHost });\n\t// \tposthog.capture(\"$pageview\");\n\t// }, [isError, isSuccess]);\n\n\tif (isError) {\n\t\treturn <FaceScanErrorScreen config={resolvedConfig} />;\n\t}\n\n\tif (isSuccess) {\n\t\treturn (\n\t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t\t\t\t{/* <Asset genderType={gender} /> */}\n\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} loaderType={\"default\"} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// return (\n\t// \t<>\n\t// \t\t<audio id=\"audioElement\" crossOrigin=\"anonymous\" preload=\"auto\" style={{ position: \"absolute\", zIndex: -99999 }} src=\"\" />\n\n\t// \t\t{showLoader && !hasError && (\n\t// \t\t\t<div className=\"fixed z-[9] w-full h-full\" style={{ background: resolvedConfig?.style?.base?.primaryColor }}>\n\t// \t\t\t\t{/* <Asset genderType={gender} /> */}\n\t// \t\t\t\t<LoadingScreen url={resolvedConfig?.loader} />\n\t// \t\t\t</div>\n\t// \t\t)}\n\n\t\t\t{/* Always show camera view */}\n\t\t\t// <div className=\"h-full flex-col relative w-full flex justify-center items-center text-center rounded-t-[20px]\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t// \t<div className=\"flex-1 w-full max-w-md overflow-hidden\">\n\t\t\t// \t\t<div className=\"w-full h-full\">\n\t\t\t// \t\t\t<video\n\t\t\t// \t\t\t\tref={webcamRef}\n\t\t\t// \t\t\t\tautoPlay\n\t\t\t// \t\t\t\tplaysInline\n\t\t\t// \t\t\t\tmuted\n\t\t\t// \t\t\t\twidth={videoConstraints.width}\n\t\t\t// \t\t\t\theight={videoConstraints.height}\n\t\t\t// \t\t\t\tclassName=\"w-full h-full object-cover fixed left-0 top-0 z-0\"\n\t\t\t// \t\t\t\tstyle={{ transform: \"scaleX(-1)\" }}\n\t\t\t// \t\t\t/>\n\t\t\t// \t\t\t<canvas ref={canvasRef} width={videoConstraints.width} height={videoConstraints.height} style={{ transform: \"scaleX(-1)\", opacity: \"0\" }} />\n\t\t\t// \t\t</div>\n\t\t\t// \t</div>\n\t\t\t// </div>\n\n\t\t\t{/* Error overlay */}\n\t\t\t// {!showLoader && hasError && <FaceScanErrorScreen loading={showLoader} resetScanState={resetScanState} config={resolvedConfig} />}\n\n\t\t\t{/* Scan guide drawer - only show when scanning */}\n\t\t// \t{showGuideCard && !hasError && !showLoader && (\n\t\t// \t\t<Drawer\n\t\t// \t\t\topen\n\t\t// \t\t\tclassName=\"face-scan-small camera-draw\"\n\t\t// \t\t\tanchor=\"bottom\"\n\t\t// \t\t\tonClose={(event, reason) => {\n\t\t// \t\t\t\tif (reason === \"backdropClick\") {\n\t\t// \t\t\t\t\treturn;\n\t\t// \t\t\t\t}\n\t\t// \t\t\t}}\n\t\t// \t\t\thideBackdrop\n\t\t// \t\t>\n\t\t// \t\t\t<FaceScanGuide stage={scanStage} videoLoading={videoLoading} setVideoLoading={setVideoLoading} videoError={videoError} setVideoError={setVideoError} gender={gender} config={resolvedConfig}>\n\t\t// \t\t\t\t<div className=\"flex justify-center w-full p-[1rem]\">\n\t\t// \t\t\t\t\t<SpecificButton\n\t\t// \t\t\t\t\t\tdisabled={showLoader || !isModelLoaded}\n\t\t// \t\t\t\t\t\tclassName=\"!w-[60px] !h-[35px] !py-0 !px-0\"\n\t\t// \t\t\t\t\t\tbuttonText={getButtonText()}\n\t\t// \t\t\t\t\t\tpostfixIcon={isScanning ? <ArrowRight /> : \"\"}\n\t\t// \t\t\t\t\t\tbuttonFunc={isScanning ? resetScanState : startScan}\n\t\t// \t\t\t\t\t\tresolvedConfig={resolvedConfig}\n\t\t// \t\t\t\t\t/>\n\t\t// \t\t\t\t</div>\n\t\t// \t\t\t</FaceScanGuide>\n\t\t// \t\t</Drawer>\n\t\t// \t)}\n\t\t// </>\n\t// );\n};\n"],"names":["FaceScanErrorScreen","resetScanState","loading","config","_jsx","id","className","style","background","base","backgroundColor","children","_jsxs","Header","noTitle","resolvedConfig","fontFamily","heading","headingFontFamily","fontSize","headingFontSize","color","headingColor","fontWeight","headingFontWeight","subheading","subheadingFontFamily","subheadingFontSize","subheadingColor","subheadingFontWeight","SpecificButton","disabled","buttonText","postfixIcon","ArrowRight","buttonFunc","FaceScan","userDetails","onComplete","onScanError","onRetry","isError","isSuccess","useLocalConfig","primaryColor","LoadingScreen","url","loader","loaderType"],"mappings":"qNAIA,SAASA,GAAoBC,eAAEA,EAAcC,QAAEA,EAAOC,OAAEA,IACvD,OACCC,SAAKC,GAAG,uBAAuBC,UAAU,kFAAkFC,MAAO,CAAEC,WAAYL,GAAQI,OAAOE,MAAMC,iBAAiBC,SACrLC,EAAA,MAAA,CAAKN,UAAU,oEACdF,EAACS,GAAOC,SAAO,EAACC,eAAgBZ,IAChCS,EAAA,MAAA,CAAKN,UAAU,oCAAmCK,SAAA,CACjDP,EAAA,KAAA,CACCE,UAAU,uCACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOU,SAASC,mBAAqB,wBACzDC,SAAUhB,GAAQI,OAAOU,SAASG,iBAAmB,OACrDC,MAAOlB,GAAQI,OAAOU,SAASK,cAAgB,OAC/CC,WAAYpB,GAAQI,OAAOU,SAASO,mBAAqB,UACzDb,SAAA,qBAIFP,EAAA,IAAA,CACCE,UAAU,cACVC,MAAO,CACNS,WAAYb,GAAQI,OAAOkB,YAAYC,sBAAwB,sBAC/DP,SAAUhB,GAAQI,OAAOkB,YAAYE,oBAAsB,OAC3DN,MAAOlB,GAAQI,OAAOkB,YAAYG,iBAAmB,UACrDL,WAAYpB,GAAQI,OAAOkB,YAAYI,sBAAwB,UAC/DlB,SAAA,2CAIFP,EAAC0B,EAAc,CACdC,SAAU7B,EACVI,UAAU,iEACV0B,WAAW,aACXC,YAAa7B,EAAC8B,EAAU,CAAA,GACxBC,WAAY,IAAMlC,MAClBc,eAAgBZ,WAMtB,OC3BaiC,EAAoC,EAAGC,cAAaC,aAAYC,cAAaC,UAASrC,SAAOsC,UAAQC,gBAcjH,MAAM3B,EAAiB4B,EAAexC,GA4UtC,OAAIsC,EACIrC,EAACJ,EAAmB,CAACG,OAAQY,IAGjC2B,EAEFtC,EAAA,MAAA,CAAKE,UAAU,6BAA6BC,MAAO,CAAEC,WAAYO,GAAgBR,OAAOE,MAAMmC,uBAE7FxC,EAACyC,EAAa,CAACC,IAAK/B,GAAgBgC,OAAQC,WAAY,mBAJ3D"}
|