@swan-admin/swan-web-component 1.0.122 → 1.0.124
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/BodyScan-C4Y72f4m.mjs +3 -0
- package/dist/BodyScan-C4Y72f4m.mjs.map +1 -0
- package/dist/BodyScan-CAAzIoLb.js +3 -0
- package/dist/BodyScan-CAAzIoLb.js.map +1 -0
- package/dist/{FaceScan-lUHosRqk.js → FaceScan-ClDsOhwN.js} +2 -2
- package/dist/{FaceScan-lUHosRqk.js.map → FaceScan-ClDsOhwN.js.map} +1 -1
- package/dist/{FaceScan-BdcqQIJ5.mjs → FaceScan-DEgrAZfU.mjs} +2 -2
- package/dist/{FaceScan-BdcqQIJ5.mjs.map → FaceScan-DEgrAZfU.mjs.map} +1 -1
- package/dist/Modal-Bo7k1S9H.js +3 -0
- package/dist/Modal-Bo7k1S9H.js.map +1 -0
- package/dist/Modal-CsO6kDBZ.mjs +3 -0
- package/dist/Modal-CsO6kDBZ.mjs.map +1 -0
- package/dist/bodyScan.js +1 -1
- package/dist/bodyScan.mjs +1 -1
- package/dist/faceScan.js +1 -1
- package/dist/faceScan.mjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/{pose-detection.esm-BV7VgekA.js → pose-detection.esm-DFB9u8DH.js} +1 -1
- package/dist/{pose-detection.esm-BV7VgekA.js.map → pose-detection.esm-DFB9u8DH.js.map} +1 -1
- package/dist/{pose-detection.esm-D8r1CbkV.mjs → pose-detection.esm-EvS-BndH.mjs} +1 -1
- package/dist/{pose-detection.esm-D8r1CbkV.mjs.map → pose-detection.esm-EvS-BndH.mjs.map} +1 -1
- package/package.json +1 -1
- package/dist/BodyScan-B5BvnBHj.js +0 -3
- package/dist/BodyScan-B5BvnBHj.js.map +0 -1
- package/dist/BodyScan-CL9reolN.mjs +0 -3
- package/dist/BodyScan-CL9reolN.mjs.map +0 -1
- package/dist/Modal-DxgejzjH.js +0 -3
- package/dist/Modal-DxgejzjH.js.map +0 -1
- package/dist/Modal-TzaSySDr.mjs +0 -3
- package/dist/Modal-TzaSySDr.mjs.map +0 -1
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import r,{useState as a,useCallback as s,useEffect as o,createContext as c,useLayoutEffect as i,useMemo as l,useContext as u,useRef as d}from"react";import{L as m,u as p,H as f,b as h,S as g,z as y,a as v,i as w,P as b,p as x,o as S,s as D,j as C,k as N,l as I,x as k,A as T,q as F,r as _,f as E,B as M,D as $,c as L,M as U,v as j,E as A,I as P,V as z,J as V,K as R,N as B,O as q,G as K,Q as O,R as W,T as H,h as J,n as G,w as Q,U as Y,t as X,C as Z,W as ee,y as te}from"./Modal-CsO6kDBZ.mjs";import ne from"clsx";import re from"react-webcam";import{X as ae}from"lucide-react";import{Dialog as se,Box as oe,Drawer as ce}from"@mui/material";import ie from"video.js";const le="DESKTOP",ue="TAB",de="MOBILE",me=c(void 0);function pe({children:t}){const[n,r]=a([window?.innerWidth,window?.innerHeight]),[s,c]=a(!1),u=()=>{r([window?.innerWidth,window?.innerHeight])};o(()=>{u()},[]),i(()=>(window.addEventListener("resize",u),()=>window.removeEventListener("resize",u)),[u]);let d=le;n[0]>768&&n[0]<1024&&(d=ue),n[0]<768&&(d=de);const m=l(()=>({size:n,setSize:r,clearInputs:s,setClearInputs:c,media:d}),[n,s,d]);return e(me.Provider,{value:m,children:t})}var fe=r.memo(function({size:n=16}){return t("svg",{width:n,height:n,viewBox:"0 0 25 25",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[e("path",{d:"M22.6968 14.6968C22.6968 16.8185 21.8539 18.8533 20.3536 20.3536C18.8533 21.8539 16.8185 22.6968 14.6968 22.6968",stroke:"white",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"M18.6968 11.6968V10.6968C18.6968 10.1663 18.4861 9.65764 18.111 9.28256C17.7359 8.90749 17.2272 8.69678 16.6968 8.69678C16.1663 8.69678 15.6576 8.90749 15.2826 9.28256C14.9075 9.65764 14.6968 10.1663 14.6968 10.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"M14.6968 10.6968V9.69678C14.6968 9.16634 14.4861 8.65764 14.111 8.28256C13.7359 7.90749 13.2272 7.69678 12.6968 7.69678C12.1663 7.69678 11.6576 7.90749 11.2826 8.28256C10.9075 8.65764 10.6968 9.16634 10.6968 9.69678V10.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"M10.6968 10.1968V4.69678C10.6968 4.16634 10.4861 3.65764 10.111 3.28256C9.73592 2.90749 9.22721 2.69678 8.69678 2.69678C8.16634 2.69678 7.65764 2.90749 7.28256 3.28256C6.90749 3.65764 6.69678 4.16634 6.69678 4.69678V14.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e("path",{d:"M18.6969 11.6968C18.6969 11.1663 18.9076 10.6576 19.2827 10.2826C19.6577 9.90749 20.1664 9.69678 20.6969 9.69678C21.2273 9.69678 21.736 9.90749 22.1111 10.2826C22.4862 10.6576 22.6969 11.1663 22.6969 11.6968V14.6968C22.6969 16.8185 21.854 18.8533 20.3537 20.3536C18.8534 21.8539 16.8186 22.6968 14.6969 22.6968H12.6969C9.89688 22.6968 8.19688 21.8368 6.70688 20.3568L3.10688 16.7568C2.76282 16.3757 2.57847 15.8769 2.592 15.3637C2.60554 14.8505 2.81593 14.3621 3.1796 13.9997C3.54327 13.6373 4.03238 13.4287 4.54565 13.417C5.05892 13.4053 5.55704 13.5914 5.93688 13.9368L7.69688 15.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})});var he=r.memo(function({angle:n,countdown:r,isScanning:a,isInTargetRange:o,stabilityScore:c,children:i}){const{translate:d}=u(m)||{},g=p(),y=s(()=>n<80?Math.max(0,Math.min(100,10*(n-60))):n>95?Math.max(0,Math.min(100,10*(105-n))):100,[n]),v=l(()=>{if(a)return g?.style?.angleDetector?.successAngleBackground||"#4f46e5";const e=y();if(0===e)return g?.style?.angleDetector?.successAngleLowBackground||"#ffffff";if(100===e)return g?.style?.angleDetector?.successAngleBackground||"#4f46e5";return`rgb(${Math.round(255-e/100*116)}, ${Math.round(255-e/100*163)}, ${Math.round(255-e/100*9)})`},[a,y]),w=s((e,t)=>t>70?e?`text-[${g?.style?.angleDetector?.successAngleTextLightColor}]/70`:`text-[${g?.style?.angleDetector?.successAngleTextLightColor}]`:e?`text-[${g?.style?.angleDetector?.successAngleTextLightColor}]/40`:`text-[${g?.style?.angleDetector?.successAngleTextDarkColor}]/70`,[]),b=s((e=!1)=>{const t=y();return a?`text-[${g?.style?.angleDetector?.successAngleTextLightColor}]`:w(e,t)},[a,y]),x=s((e,t)=>t>70||e?g?.style?.angleDetector?.successAngleTextLightColor:g?.style?.angleDetector?.successAngleTextDarkColor,[]),S=s((e=!1)=>{const t=y();return a?g?.style?.angleDetector?.successAngleTextLightColor:x(e,t)},[a,y]),D=r;function C(){document.documentElement.style.setProperty("--real-vh",window.innerHeight+"px")}return C(),window.addEventListener("resize",C),t("div",{className:"flex common-ui-main w-screen flex-col items-center h-[var(--real-vh)] overflow-hidden touch-none justify-center transition-all duration-300 max-w-[28rem] mx-auto",style:{backgroundColor:v},"data-testid":"level-screen","data-angle-state":a?"locked":o?"target":"adjusting",children:[e("div",{className:"flex justify-start fixed top-[.5rem] max-w-[28rem] mx-auto w-full px-[1rem]",children:e("div",{className:"flex justify-start ",children:e(f,{noTitle:!0})})}),null!==D?e("div",{className:"relative flex h-[6rem] w-[6rem] items-center justify-center rounded-[9999px] bg-[#fff]/20 transition-all duration-300",style:{border:`2px solid ${S()}`},children:e("div",{className:`text-[3rem] font-bold text-[${S()}]`,style:{fontFamily:g?.style?.base?.baseFontFamily||"Inter, sans-serif",color:S()},children:D})}):t("div",a?{className:"relative flex flex-col items-center justify-center",children:[e("div",{className:"relative flex h-16 w-16 items-center justify-center rounded-[9999px] border-2 bg-[#fff]/30 border-[#fff]",children:e("div",{className:"h-4 w-4 rounded-[9999px] animate-pulse bg-[#fff]/80"})}),i]}:{className:ne("relative flex h-[4rem] w-[4rem] items-center justify-center rounded-[9999px] ",o?"bg-[#fff]/20 border-[#fff]":"bg-[#fff]/10 border-[#fff]"),style:{transform:`translateY(${3*(90-n)}px)`,transition:"transform 0.2s ease-out",border:`2px solid ${S()}`},children:[e("div",{className:`h-[1rem] w-[1rem] rounded-[9999px] bg-[${S()}]/80`,style:{backgroundColor:`${S()}B3`}}),t("div",{className:ne("mt-[.5rem] text-center w-[180px] flex-col flex items-center absolute top-[60px]",b()),style:{color:S()},children:[e(fe,{size:30}),t("p",{style:{fontFamily:g?.style?.base?.baseFontFamily||"Inter, sans-serif"},children:[" ",d?.(h.startLevelCheck),e("br",{})]})]})]}),null!==D&&e("div",{className:"absolute bottom-[8rem] text-center",children:e("div",{className:ne("text-sm font-medium px-4 py-1.5 bg-black/20 rounded-[9999px] mt-8",b()),style:{fontFamily:g?.style?.base?.baseFontFamily||"Inter, sans-serif",color:S()},children:d?.(h.leavePhone)})}),o&&null===D&&!a&&e("div",{className:"absolute bottom-[8rem] w-[12rem]",children:e("div",{className:`h-[.375rem] w-full bg-[${S()}]/20 rounded-[9999px] overflow-hidden`,style:{backgroundColor:`${S()}33`},children:e("div",{className:`h-full bg-[${S()}]/70 transition-all duration-300 rounded-[9999px]`,style:{width:`${n}%`,backgroundColor:`${S()}B3`}})})}),null===D&&!a&&e("div",{className:"absolute bottom-[5rem] text-center",children:t("div",{className:ne("text-[1.5rem] font-light",b()),style:{color:S()},children:[Math.round(n),"°"]})}),null===D&&!a&&e("div",{className:"absolute bottom-[2rem] text-center max-w-[20rem] mx-auto",children:e("div",{className:ne("text-[.75rem] opacity-50",b()),style:{fontFamily:g?.style?.base?.baseFontFamily||"Inter, sans-serif",color:S()},children:d?.(h.placePhoneUpright)})})]})});function ge({resetScan:n,loadingCam:r,onUserMedia:a,onPause:s,pause:o,onReScan:c,recordingStarted:i,startSendingVideoFrames:l,showPause:d,webcamRef:p,onUserMediaError:f,resetDetector:w,videoConstraints:b,webcamKey:x,faceDone:S,isScanning:D}){const{media:C}=u(me)||{},{translate:N}=u(m)||{};return t("div",{className:"App w-screen h-[100vh] relative common-ui-main","data-testid":"camera-scan","data-scan-phase":S?"spinning":i?"pose-detecting":D?"starting":"ready",children:[e("span",{onClick:()=>{n(),w()},className:"fixed right-[20px] top-[20px] z-[999]",children:e(ae,{className:"text-[#fff]"})}),e("div",{className:"w-full h-full overflow-hidden ",children:C===de&&e(re,{audio:!1,ref:p,screenshotQuality:1,videoConstraints:b,mirrored:!0,screenshotFormat:"image/jpeg",onUserMedia:a,onUserMediaError:f,style:{position:"fixed",top:0,bottom:0,zIndex:0,width:"100%",height:"100%",objectFit:"cover"}},x)}),t("div",{className:"fixed bottom-[30px] w-full z-[999] flex flex-col gap-4 items-center justify-center",children:[d&&e(g,{className:"!w-[180px] !h-[40px] !py-[0] mx-auto ",prefix:y,buttonText:N?.(h.pause),buttonFunc:s,btnSecondary:!0}),o?e("div",{children:e(g,{className:"!w-[180px] !h-[40px] !py-[0] mx-auto",buttonText:N?.(h.restart),buttonFunc:c,btnSecondary:!0})}):i?null:e(g,{"data-testid":"start-scan-btn",className:"!w-[180px] !h-[40px] !py-[0] mx-auto !bg-[#ffffff] !text-[#000000] "+(r?"!opacity-50":""),buttonText:N?.(h.startScan),buttonFunc:l,disabled:r})]}),e("audio",{id:"audioElement",crossOrigin:"anonymous",preload:"auto",style:{position:"absolute",zIndex:-99999},src:`${v}scanAudioInstructions/silence.mp3`})]})}let ye=null,ve=null,we=null;let be,xe=null;var Se=r.memo(function({setIsScanLocked:t,resetDetector:n,scanID:r,setIsVideoUploaded:c,setScanFailsError:i,setScanStartTime:m,setScanUniqueKey:p,userDetails:f}){const{email:h,shopDomain:g,gender:y,heightInCm:$}=f,L=d(null),U=d(null),j=d(null),[A,P]=a([]),[z,V]=a(!0),[R,B]=a(!1),[q,K]=a(!1),[O,W]=a(!0),[H,J]=a(!1),G=d(null),[Q,Y]=a(""),[X,Z]=a([]),[ee,te]=a(""),[ne,re]=a(!1),[ae,se]=a(!1),[oe,ce]=a(!1),[ie,le]=a([]),[ue,de]=a(!1),me=d(!0),{poseDetector:pe}=function(){const[e,t]=a(0),[n,r]=a(0),[s,c]=a(!1),i=d(0),l=d(0),u=d(!0);async function m(){if("undefined"==typeof window||"undefined"==typeof navigator)return!1;try{console.log("Starting TensorFlow preload...");const[e,t,n]=await Promise.all([import("./pose-detection.esm-EvS-BndH.mjs"),import("@tensorflow/tfjs-core"),import("@tensorflow/tfjs-backend-webgl")]);we=e,await t.setBackend("webgl"),await t.ready(),console.log("TensorFlow backend ready (WebGL)");const r={runtime:"mediapipe",modelType:"full",solutionPath:"https://cdn.jsdelivr.net/npm/@mediapipe/pose"};try{ve=await we.createDetector(we.SupportedModels.BlazePose,r),console.log("MediaPipe detector created successfully")}catch(e){console.warn("MediaPipe failed, falling back to TFJS runtime:",e),ve=await we.createDetector(we.SupportedModels.BlazePose,{runtime:"tfjs",modelType:"full"}),console.log("TFJS detector created successfully")}return!0}catch(e){return console.error("Failed to load TensorFlow dependencies:",e),ye=null,!1}}function p(e){return!(!e||0===e.length)&&22===e.filter(e=>e[2]>.7).length}return o(()=>{if(u.current=!0,!ve)return ye||(ye=m()),ye.then(e=>{e&&u.current&&c(!0)}),()=>{u.current=!1};c(!0)},[]),o(()=>{l.current=e,e>6&&i.current<2&&(r(e=>e+1),t(0))},[e]),o(()=>{i.current=n},[n]),{poseDetector:async(e,n)=>{if(!ve||!u.current||!n?.current?.video)return;const r=n.current.video;if(!(r.readyState<2))try{const n=await ve.estimatePoses(r,{flipHorizontal:!1});if(!n||!n.length)return;const a=n[0].keypoints.slice(11).map(e=>[e.x>0&&e.x<720?e.x:-1,e.y>0&&e.y<1280?e.y:-1,e.score??0]);0!==i.current||p(a)||t(e=>e+1),1===i.current&&p(a)&&t(e=>e+1),2===i.current&&e()}catch(e){console.error("Pose detection error:",e)}},isLoaded:s,spinPhase:n,resetDetector:()=>{i.current=0,l.current=0,t(0),r(0)},retryLoading:async()=>{ve||ye||(ye=m(),await ye&&u.current&&c(!0))}}}(),fe=d(!0),he=d(!1),Se=d(0),De=d(0),Ce=d(0),Ne=d(0),Ie=d(!1),ke=d(!1),Te=d(!1),Fe=d(()=>{}),_e=d(()=>{}),Ee=d(null),[Me,$e]=a([]),[Le,Ue]=a(0),[je,Ae]=a(w),{setStartGyro:Pe,uploadScanFile:ze,setUploadLoading:Ve,swan:Re,onScanStart:Be,onCaptureComplete:qe}=u(b),Ke=d(r);o(()=>{Ke.current=r},[r]);const Oe=d(ie);o(()=>{Oe.current=ie},[ie]);const We=function({timeoutMs:e,onTimeout:t}){const n=d(null),r=d(t);o(()=>{r.current=t},[t]);const a=s(()=>{n.current&&(clearTimeout(n.current),n.current=null)},[]),c=s(()=>{n.current&&clearTimeout(n.current),n.current=setTimeout(()=>{n.current=null,r.current()},e)},[e]);return o(()=>a,[a]),{start:c,pet:c,stop:a}}({timeoutMs:12e3,onTimeout:s(()=>{x.capture(`${g}/pose_feedback_timeout`,{scanID:Ke.current,email:h,framesSent:Ce.current,id:Ee.current})},[g,h])}),He=()=>{he.current=!1,We.stop(),clearTimeout(be),G.current&&clearTimeout(G.current),p(E()),i(""),Ve?.(!1),P([]),j.current=null,V(!0),B(!1),K(!1),W(!0),J(!1),G.current=null,Y(""),Z([]),te(""),re(!ne),Pe(!1),D.stopAudio(),se(!1),ce(!1),Se.current=0,De.current=0,Ce.current=0,Ne.current=0,Ie.current=!1,Te.current=!1,null!==U.current&&U.current.stop(),ie.forEach(e=>{U.current&&U.current.removeEventListener("dataavailable",e)}),le([]),me.current=!0,de(!1),c(!1),L.current?.stream&&V(!1)},Je=s(()=>{setTimeout(()=>{V(!1)},1e3)},[]),Ge=s(e=>{if(console.log("camera error",e),je===w)return Ae(S),void Ue(e=>e+1);V(!1)},[je]),Qe=s(()=>{me.current=!1,We.stop(),ce(!0),de(!1),G.current&&clearTimeout(G.current),U.current&&U.current.pause(),D.stopAudio(),Re.poseDetection.disconnect(),x.capture(`${g}/pose_detection_disconnected`,{scanID:r,email:h,id:Ee.current,reason:"pause"}),Se.current=0,De.current=0,Ie.current=!1,Te.current=!1,U.current&&U.current.stop(),ie.forEach(e=>{U.current&&U.current.removeEventListener("dataavailable",e)}),le([]),clearTimeout(be)},[U,ie,D,me]),Ye=l(()=>C(),[]),Xe=N(Me),Ze=I(Xe),et=s(async()=>{me.current=!0,await D.playAudio(`${v}SpotOn.mp3`),ce(!1),de(!1),G.current&&clearTimeout(G.current),B(!1)},[G,D]),tt=s(async()=>{if(!ke.current&&!Ee.current){ke.current=!0;try{Ee.current=await Re.poseDetection.connect(),x.capture(`${g}/pose_detection_connected`,{scanID:Ke.current,email:h,id:Ee.current})}catch(e){console.log(e,"while connecting websocket"),x.capture(`${g}/pose_detection_connect_failed`,{scanID:r,email:h,error:String(e)})}finally{ke.current=!1}}},[r,g,h]),nt=s(({data:e})=>{e&&e.size>0&&me.current&&(P(t=>t.concat(e)),!he.current&&L.current&&(he.current=!0,pe(()=>{et(),k({eventName:`${g}/tensorFlow`,scanID:Ke.current,email:h,message:"recording stopped by tensorflow "})},L)))},[et,k,g,h,L]),rt=s(async()=>{const e=X[X.length-1];e&&me.current&&(await D.playAudio(v+e),me.current&&(be=setTimeout(rt,2e3)))},[X,me]),at=s(()=>{U&&U.current&&U.current.stop(),B(!0);try{if(L&&L.current&&L.current.stream){const e=L.current.stream.getVideoTracks()[0];if(e){const t=e.getSettings();j.current=t.frameRate||null}const t={videoBitsPerSecond:15e5};Me[0]&&(t.mimeType=Me[0]),U.current=new MediaRecorder(L.current.stream,t),U.current.addEventListener("dataavailable",nt),U.current.addEventListener("stop",()=>{W(!0)}),le([...ie,nt]),U.current.start(1e3),W(!1)}}catch(e){console.log("error while using media recorder",e)}},[L,Me,nt,ie]),st=s(async()=>{G.current&&clearTimeout(G.current),at(),me.current&&(G.current=setTimeout(async()=>{me.current&&(await D.playAudio(`${v}SpotOn.mp3`),B(!1))},15e3)),K(!0),me.current&&await D.playAudio(`${v}Spin.mp3`)},[at,me,D]);o(()=>{Fe.current=st},[st]);const ot=s(({data:e})=>{const t=Re.poseDetection.connected();if(e.size>0&&t){Ie.current||(Ie.current=!0,Re.poseDetection.poseStatus(async e=>{if(We.pet(),x.capture(`${g}/pose_instruction_received`,{scanID:Ke.current,email:h,audio:e?.audio??"",audioEmpty:!(e?.audio&&e.audio.length>0),status:e?.status,sid:e?.sid,sidMatchesConnection:e?.sid===Ee.current,counter:Se.current,consecutiveFalse:De.current,type:"body_scan_instruction"}),e&&e.audio&&e.audio.length>0){const t=document.querySelector("#audioElement");!0===e.status&&e.sid===Ee.current?(De.current=0,Se.current<2?(Se.current+=1,1!==Se.current||Te.current?2===Se.current&&t?.paused&&(clearTimeout(be),D.playAudio(v+e.audio)):(Te.current=!0,clearTimeout(be),D.stopAudio(),await D.playAudio(v+e.audio))):(We.stop(),te(e.audio),clearTimeout(be),Re.poseDetection.disconnect(),x.capture(`${g}/pose_detection_disconnected`,{scanID:r,email:h,id:Ee.current,reason:"pose_confirmed"}),Ie.current=!1,setTimeout(()=>Fe.current(),1e3))):(De.current+=1,De.current>=3&&(Se.current=0,Te.current=!1),!t?.paused||xe&&xe?.audioName===e.audio?xe?.audioName===e.audio&&t?.paused&&(xe&&xe.no_of_times_skipped>=xe.skipCount?(xe.no_of_times_skipped=0,D.playAudio(v+e.audio)):xe&&(xe.no_of_times_skipped+=1)):(xe={skipCount:2,no_of_times_skipped:0,audioName:e.audio},D.playAudio(v+e.audio)))}}));const e=L?.current?L.current.getScreenshot():null;e?(Ce.current+=1,1===Ce.current&&We.start(),Re.poseDetection.videoEmit({image:e,scanId:Ke.current}),1!==Ce.current&&Ce.current%5!=0||x.capture(`${g}/pose_frame_sent`,{scanID:Ke.current,email:h,sid:Ee.current,framesSent:Ce.current})):(Ne.current+=1,1!==Ne.current&&Ne.current%5!=0||x.capture(`${g}/pose_frame_skipped_null`,{scanID:Ke.current,email:h,framesSkipped:Ne.current}))}},[L,r,Re,D,g,h]),ct=s(async()=>{J(!0),Pe(!0),Be?.(),fe.current&&(fe.current=!1,k({eventName:"scan started",scanID:Ke.current,status:"success",email:h})),m(T()),V(!0),me.current&&await D.playAudio(`${v}StartScan.mp3`),me.current&&await D.playAudio(`${v}LiftArmsAndHoldAtHip.mp3`),me.current&&(J(!1),B(!0),se(!0),de(!0),Je());try{if(L&&L.current&&L.current.stream&&me.current){const e=L.current.stream.getVideoTracks()[0];if(e){const t=e.getSettings();j.current=t.frameRate||null}const t={videoBitsPerSecond:15e5};Me[0]&&(t.mimeType=Me[0]),me.current&&(U.current=new MediaRecorder(L.current.stream,t)),me.current&&U.current&&U.current.addEventListener("dataavailable",ot),le([...Oe.current,ot]),U.current&&U.current.start(1e3),W(!1),me.current&&(be=setTimeout(rt,2e3))}}catch(e){console.log("error ----------",e)}},[L,Me,ot,rt,me,Be]);return o(()=>{_e.current=ct},[ct]),o(()=>(g&&(t(!0),tt()),()=>{const e=Ee.current;Ee.current=null,e&&(Re.poseDetection.disconnect(),x.capture(`${g}/pose_detection_disconnected`,{scanID:Ke.current,email:h,id:e,reason:"unmount"})),ie.forEach(e=>{U?.current?.removeEventListener("dataavailable",e)})}),[g]),o(()=>{X.push(Q)},[Q]),o(()=>{Z([])},[ee]),o(()=>{const e=A.length&&A.length>0;O||!e||R||!me.current||oe||U&&U.current&&(R||U.current.stop())},[O,R,A,oe]),o(()=>{const e=A.length&&A.length>0;if(O&&e&&!R&&U&&U.current&&me.current&&!oe&&!R){const e=new File(A,`${r}.${Ze}`,{type:Xe});qe?.(),Ve?.(!0),i(""),ze?.(e,j.current)}},[O,R,A,oe,U,me]),o(()=>{_e.current=ct},[ct]),o(()=>{const e=F(Ye,_);$e(e)},[Ye]),o(()=>{He()},[]),e(ge,{resetDetector:n,resetScan:He,loadingCam:z,onPause:Qe,showRestart:ae,pause:oe,onReScan:async()=>{M({eventName:`${g}/rescan`,email:h,scanID:Ke.current,height:$,gender:y,status:!1}),Re.poseDetection.disconnect(),He(),await tt()},recordingStarted:R,isScanning:H,startSendingVideoFrames:ct,faceDone:q,stopRecording:et,showPause:ue,webcamRef:L,onUserMedia:Je,onUserMediaError:Ge,videoConstraints:je,webcamKey:Le})});function De({scanID:t,userDetails:n,setIsVideoUploaded:r,setScanFailsError:s,setScanStartTime:c,setScanUniqueKey:i}){const[l,u]=a(90),[m,p]=a(0),[f,h]=a(null),[g,y]=a(!1),[v,w]=a(0),b=d([]),[x,S]=a(!1),D=l-m,C=D>=80&&D<=95;o(()=>{if("undefined"!=typeof window&&window.DeviceOrientationEvent){const e=e=>{if(null!==e.beta){let t=Math.abs(e.beta);null!==e.gamma&&(t*=Math.cos(e.gamma*Math.PI/180)),t=Math.max(0,Math.min(180,t)),u(t)}},t=async()=>{const t=DeviceOrientationEvent;if(t&&"function"==typeof t.requestPermission)try{"granted"===await t.requestPermission()&&window.addEventListener("deviceorientation",e)}catch(e){console.error("Permission error",e)}else window.addEventListener("deviceorientation",e);return()=>{e&&window.removeEventListener("deviceorientation",e)}};document.addEventListener("click",t,{once:!0})}},[]),o(()=>{if(b.current=[...b.current.slice(-4),D],b.current.length>=5){const e=Math.max(...b.current)-Math.min(...b.current);w(C&&e<2?e=>Math.min(100,e+10):e=>Math.max(0,e-20))}C||null===f&&!g||I()},[D,C,f,g]),o(()=>{v>=100&&null===f&&!g&&N(),v<50&&null!==f&&I()},[v,f,g]);const N=()=>{h(3);const e=setInterval(()=>{h(t=>null===t||t<=1?(clearInterval(e),y(!0),null):t-1)},1e3)},I=()=>{h(null),x||y(!1)};return e(he,{angle:D,countdown:f,isScanning:g,isInTargetRange:C,stabilityScore:v,children:g&&e(Se,{setIsScanLocked:S,resetDetector:()=>{u(90),p(0),h(null),y(!1),w(0),S(!1),b.current=[]},scanID:t,userDetails:n,setIsVideoUploaded:r,setScanFailsError:s,setScanStartTime:c,setScanUniqueKey:i})})}function Ce({setShowDrawer:t,config:n,loader:r}){const[c,i]=a({disabled:!1,message:""}),[l,u]=a(!0),m=d(null),p=s(async()=>{const e=await $();i(e),u(!1)},[]);return o(()=>{p()},[]),l?e(L,{url:r,loaderType:"black"}):c?.disabled?e(U,{config:n,message:c?.message}):e(re,{audio:!1,ref:m,screenshotQuality:1,videoConstraints:j,mirrored:!0,onUserMedia:()=>t?.(!0),screenshotFormat:"image/jpeg",style:{position:"relative",top:0,bottom:0,zIndex:0,width:"100%",height:"100%",objectFit:"cover"}})}function Ne({link:t,onReady:n,wrapperClassName:r="[&_video]:rounded-t-[20px] w-full h-full"}){const a=d(null),s=d(null);let c={autoplay:!0,controls:!1,responsive:!0,fluid:!0,muted:!0,navigationUI:"hide",preload:"metadata",poster:A};return o(()=>{if(!s.current&&t&&a?.current){const e=ie(a.current,{...c});e.ready(()=>{s.current=e;const r={...c,sources:[{src:t,type:"application/x-mpegURL"}]};e.autoplay(r.autoplay),e.src(r.sources),n?.(e)})}},[t,a]),o(()=>{const e=s.current;return()=>{e&&!e.isDisposed()&&(e.dispose(),s.current=null)}},[s]),e("div",{className:r,children:e("video",{ref:a,muted:!0,className:"video-js",playsInline:!0,onDrag:e=>e.preventDefault()})})}function Ie({scanFailsError:r,onNext:s,gender:o,setScanUniqueKey:c,setIsVideoUploaded:i}){const{translate:l}=u(m)||{},d=p(),[y,v]=a(!1),w=()=>{v(!y)};return t(n,{children:[t("div",{"data-testid":"scan-error-message",className:"flex common-ui-main flex-col h-full max-w-[28rem] mx-auto w-full rounded-t-[20px] overflow-y-auto",style:{background:d?.style?.base?.backgroundColor},children:[e("div",{className:"w-full max-w-[28rem] mx-auto pt-[1rem] px-[1rem]",children:e(f,{noTitle:!0})}),e("div",{className:"flex-1",children:r&&t("div",{className:"px-[1rem]",children:[e("h2",{className:"text-center",style:{fontFamily:d?.style?.heading?.headingFontFamily||"SeriouslyNostalgic Fn",fontSize:d?.style?.heading?.headingFontSize||"32px",color:d?.style?.heading?.headingColor||"#000",fontWeight:d?.style?.heading?.headingFontWeight||"normal"},children:l?.(h.issueWithScan)}),e("p",{style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif",fontSize:d?.style?.base?.baseFontSize||"16px",color:d?.style?.base?.baseTextColor||"#1E1E1E"},children:l?.(h.reason)}),e("p",{style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif",fontSize:d?.style?.base?.baseFontSize||"16px",color:d?.style?.base?.baseTextColor||"#1E1E1E"},children:P(r)}),e("img",{className:"my-[0.5rem] aspect-[2/1.4] w-full object-cover",onClick:w,src:z[o],alt:"icon"})]})}),r&&e("div",{className:"p-[1rem] flex gap-[0.5rem]",children:e(g,{"data-testid":"scan-again-btn",disabled:!1,buttonText:l?.(h.scanAgain),className:"!shadow-none",buttonFunc:()=>{s?s?.():c?.(E()),i?.(!1)}})})]}),y&&t(se,{className:"w-screen h-screen video-modal",onClose:w,open:y,children:[e("div",{className:"flex justifyEnd ",children:e("span",{className:"closeBtn",onClick:w,children:e(ae,{className:"absolute right-[8px] top-[8px] text-white z-[9]"})})}),e("div",{className:"aspect-video object-cover rounded-[20px] ",children:e(Ne,{link:o?V[o].PRE_LINK:V.male.PRE_LINK,wrapperClassName:"w-screen h-screen fixed top-[0] left-[0]"})})]})]})}function ke({scanId:n,userDetails:r,config:c,isVideoUploadedCorrect:i,isMeasurementAvailable:l,onComplete:d,isSuccess:p,onCustomScanSuccess:y,swan:v}){const{gender:w,shopDomain:b,heightInCm:S,deviceFocalLength:D,userName:C,email:N,scanType:I}=r||{},k=[O,W,H].includes(I??""),[T,F]=a(!0),_=s(async()=>{try{(k||I===R)&&(await(v?.auth.addUser({scanId:n,email:N,name:C,gender:w,height:S})),y?.()),x.capture(b??"",{scanID:n,email:N,height:S,focalLength:D,clothesFit:"0",gender:w})}catch(e){console.log(e)}},[k]),{translate:E}=u(m)||{};o(()=>{(i||l)&&_()},[i,l]);const M=(p||i||l)&&!k;return e(oe,{className:"flex h-full w-full flex-col common-ui-main",children:t("div",{className:"h-full w-full flex-col items-center justify-center flex",children:[e(Ce,{loader:c?.loader,setShowDrawer:F}),e(ce,{open:T,onClose:(e,t)=>{},className:"camera-drawer",anchor:"bottom",children:e("div",{className:"max-w-[28rem] mx-auto w-full h-full flex text-center flex-col justify-between items-center bg-primary rounded-t-[30px] p-[1rem]",style:{background:c?.style?.base?.backgroundColor},children:t("div",{className:"w-full h-full flex flex-col",children:[e(f,{title:E?.(h.measurementsBeingTaken)}),e("div",{className:"flex items-center justify-center flex-1",children:e("video",{preload:"auto",className:"max-h-[calc(100vh-450px)] mx-auto w-full object-contain border-none",muted:!0,loop:!0,autoPlay:!0,playsInline:!0,children:e("source",{src:w===K.Male?B:q,type:"video/mp4"})})}),M&&e(g,{className:"!w-[180px] mx-auto",buttonText:E?.(h.next),buttonFunc:()=>{d?.()}})]})})})]})})}const Te=()=>{const{userDetails:n,onRetry:r,isError:a,isSuccess:s,gender:o,scanUniqueKey:c,scanFailsError:i,setScanUniqueKey:l,setIsVideoUploaded:d,isMeasurementAvailable:m,isVideoUploadedCorrect:f,loading:h,showDeniedModal:g,uploadLoading:y,setScanFailsError:v,setScanStartTime:w,resetScan:x,onCustomScanSuccess:S,swan:D,onComplete:C}=u(b),N=p();return a?t("div",{"data-scan-state":"error",children:[e(Ce,{}),e(ce,{anchor:"bottom",open:!0,className:"camera-drawer",onClose:(e,t)=>{},children:e(Ie,{scanFailsError:!0,onNext:r,gender:o})})]}):s?e("div",{"data-scan-state":"success",children:e(ke,{isSuccess:!0,onComplete:C})}):h?e("div",{"data-scan-state":"loading",className:"flex top-0 !mt-0 left-0 z-[999] absolute justify-center items-center w-full h-full",style:{background:N?.style?.base?.backgroundColor},children:e(L,{url:N?.loader,loaderType:"black"})}):g.disabled?e("div",{"data-scan-state":"camera-denied",children:e(U,{})}):y||i||m||f?(y||m||f)&&!i?e("div",{"data-scan-state":"processing",children:e(ke,{scanId:c,isMeasurementAvailable:m,userDetails:n,isVideoUploadedCorrect:f,onCustomScanSuccess:S,swan:D,onComplete:C,config:N})}):t("div",{"data-scan-state":"error-retry",children:[e(Ce,{}),e(ce,{anchor:"bottom",open:!0,className:"camera-drawer",onClose:(e,t)=>{},children:e(Ie,{scanFailsError:i,onNext:()=>{r?.(),x()},setScanUniqueKey:l,gender:o,setIsVideoUploaded:d})})]}):e("div",{"data-scan-state":"scanning",children:e(De,{scanID:c,userDetails:n,setIsVideoUploaded:d,setScanFailsError:v,setScanStartTime:w,setScanUniqueKey:l})})},Fe=t=>{const{config:n,onRetry:r,onScanStart:c,onCaptureComplete:i,onUploadStart:u,onUploadEnd:d,onMeasurementSocketStart:m,onMeasurementSocketClose:p,onIntermediateScanSuccess:f,onScanError:h,onScanSuccess:g,onCustomScanSuccess:y,onComplete:v,isError:w,isSuccess:x}=t,S={email:"",shopDomain:"",gender:"male",heightInCm:0,scanType:"",deviceFocalLength:0,deviceModelName:"",sourceTag:"",callbackUrl:null,...t.userDetails??{}},D=t.token??"",{gender:C,scanType:N,shopDomain:I,heightInCm:F,email:_,deviceFocalLength:M,deviceModelName:L,callbackUrl:U,sourceTag:j}=S,A=l(()=>J(D),[D]),[z,V]=a(!1),[R,B]=a(!1),[q,K]=a(!1),[O,W]=a({disabled:!1,message:""}),[H,ne]=a(""),[re,ae]=a(!1),[se,oe]=a(!0),[ce,ie]=a(!1),[le,ue]=a(""),[de,me]=a(T()),{gyroData:fe}=function(e){const[t,n]=a([]),[r,c]=a(!1),i=s(e=>{try{const{alpha:t,beta:r,gamma:a}=e;n(e=>[...e,{alpha:t?.toString()||void 0,beta:r?.toString()||void 0,gamma:a?.toString()||void 0,timestamp:(new Date).toISOString()}])}catch(e){console.log(e)}},[]),l=s(async()=>{const e=DeviceOrientationEvent;if(void 0!==e&&"function"==typeof e.requestPermission)try{"granted"===await e.requestPermission()?c(!0):console.warn("Device orientation permission denied.")}catch(e){console.error("Error requesting device orientation permission:",e)}else c(!0)},[]);return o(()=>(e&&r?window.addEventListener("deviceorientation",i):n([]),()=>{window.removeEventListener("deviceorientation",i)}),[e,r,i]),o(()=>{e&&l()},[e,l]),{gyroData:t}}(ce);G();const he=s(()=>{ue(E()),ne(""),V(!1),ae(!1)},[]),ge=()=>{me(null),ue("")},ye=s(async e=>{if(e&&"success"===e?.scanStatus&&"intermediate"===e?.resultType&&200===e?.code)return k({eventName:`${I}/measurement_success/intermediate`,scanID:le,status:"success",email:_}),B(!0),void f?.(e);g?.(e),B(!0),ge();const t=le;K(!0),k({eventName:`${I}/measurement_success/fit-view`,scanID:t,status:"success",email:_}),de&&k({eventName:`${I}/scan_completion_time`,scanID:t,status:"success",completionTime:T()-de,email:_})},[N,I,le]),ve=e=>{K(!1),ne(""),B(!1),A.measurement.handleMeasurementSocket({scanId:le,onPreopen:()=>{te({eventName:`${I}/webSocket`,scanID:le,connection:"pre_open",type:"measurement_recommendation",email:_})},onOpen:()=>{m?.(),te({eventName:`${I}/webSocket`,scanID:le,connection:"open",type:"measurement_recommendation",email:_})},onClose:()=>{p?.(),te({eventName:`${I}/webSocket`,scanID:le,connection:"close",type:"measurement_recommendation",email:_})},onError:e=>{var t;t=e,h?.({...t,message:P(t)}),ge(),K(!1),ne(t),B(!1),k({eventName:`${I}/measurement_failed/fit-view`,scanID:le,status:"failed",email:_,message:P(t)}),de&&k({eventName:`${I}/scan_completion_time`,scanID:le,status:"failed",completionTime:T()-de,email:_}),te({eventName:`${I}/webSocket`,scanID:le,connection:"error",type:"measurement_recommendation",email:_})},onSuccess:e=>{te({eventName:`${I}/webSocket`,scanID:le,connection:"success",type:"measurement_recommendation",email:_}),ye(e)}})},we=s(async(e,t)=>{const n=Number((e.size/1048576).toFixed(2)),r=null!=t?t:await Q(e),a=Y({gender:C,focal_length:`${M}`,height:`${F}`,customer_store_url:I,clothes_fit:"0",scan_type:ee(N),callback_url:U,source_tag:j});k({eventName:`${I}/body_scan_meta_data`,scanID:le,email:_,data:JSON.stringify([...a,{fileSizeInMB:n,video_fps:r||0}])}),u?.();try{await A.fileUpload.uploadFileFrontend({file:e,arrayMetaData:a,scanId:le,email:_}),await A.fileUpload.setDeviceInfo({model:L,detection:"manual",gyro:fe,scanId:le}),console.log("video successfully uploaded"),ae(!1),k({eventName:`${I}/scan_success`,scanID:le,status:"success",email:_,data:JSON.stringify(a)}),k({eventName:"scan finished",scanID:le,status:"success",email:_}),me(T()),setTimeout(()=>{ae(!0)},3e3)}catch(e){k({eventName:"scan finished",scanID:le,status:"failed",email:_,message:P(e)}),k({eventName:`${I}/scan_failed`,scanID:le,status:"failed",email:_,message:P(e),data:JSON.stringify(a)}),ne(P(e)),ae(!1),h?.({...e,message:P(e)}),ge()}finally{ie(!1),d?.()}},[le,fe,u,d]),be=s(async()=>{if(+F<152.4||+F>213.36)return void h?.({message:"Height must be between 152.4cm (5ft) and 213.36cm (7ft)"});const e=await $();e.disabled?(k({eventName:`${I}/camera_activation`,scanID:le,status:"failed",email:_}),oe(!1)):(oe(!1),k({eventName:`${I}/camera_activation`,scanID:le,status:"success",email:_})),W(e),ne(""),ae(!1)},[_,le,ue,I]);return o(()=>{w||x||I&&be()},[I,F,w,x]),o(()=>{w||x||re&&I&&le&&ve()},[re,I,le,w,x]),e("div",{"data-testid":"body-scan-root",children:e(X,{children:e(Z,{config:n,children:e(pe,{children:e(b.Provider,{value:{userDetails:S,onRetry:r,onScanError:h,isError:w,isSuccess:x,onScanSuccess:g,gender:C,scanUniqueKey:le,scanFailsError:H,setScanUniqueKey:ue,setIsVideoUploaded:ae,isMeasurementAvailable:q,isVideoUploadedCorrect:R,loading:se,showDeniedModal:O,uploadLoading:z,setStartGyro:ie,uploadScanFile:we,setUploadLoading:V,resetScan:he,setScanFailsError:ne,setScanStartTime:me,onScanStart:c,onCaptureComplete:i,onCustomScanSuccess:y,onComplete:v,swan:A},children:e(Te,{})})})})})})},_e=t=>{const[n,r]=a(!1);return o(()=>{r(!0)},[]),n?e(Fe,{...t}):e("div",{"data-testid":"body-scan-root"})};export{_e as B};
|
|
3
|
+
//# sourceMappingURL=BodyScan-C4Y72f4m.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BodyScan-C4Y72f4m.mjs","sources":["../src/utils/context/mediaContext.tsx","../src/Icons/SwitchIcon.tsx","../src/components/bodyScan/LevelScreen.tsx","../src/components/bodyScan/CameraScanChild.tsx","../src/customHooks/useTensorFlow.ts","../src/components/bodyScan/ScanningComponent.tsx","../src/customHooks/useWatchdog.ts","../src/components/bodyScan/AngleDetector.tsx","../src/components/bodyScan/CameraPermission.tsx","../src/components/bodyScan/VideoPlayer.tsx","../src/components/bodyScan/ScanErrorMessage.tsx","../src/components/Signup.tsx","../src/components/bodyScan/BodyScanSteps.tsx","../src/components/bodyScan/BodyScan.tsx","../src/customHooks/useGyroSensor.ts"],"sourcesContent":["\"use client\"\nimport React, {\n createContext,\n Dispatch,\n ReactNode,\n SetStateAction,\n useEffect,\n useLayoutEffect,\n useMemo,\n useState,\n} from \"react\";\n\nexport const MEDIA_TYPES = {\n DESKTOP: \"DESKTOP\",\n TAB: \"TAB\",\n MOBILE: \"MOBILE\",\n};\n\ninterface MediaContextType {\n size: number[];\n setSize: Dispatch<SetStateAction<number[]>>;\n clearInputs: boolean;\n setClearInputs: Dispatch<SetStateAction<boolean>>;\n media: string;\n}\n\nexport const MediaContext = createContext<MediaContextType | undefined>(\n undefined\n);\n\nfunction MediaContextProvider({ children }: { children: ReactNode }) {\n const [size, setSize] = useState([window?.innerWidth, window?.innerHeight]);\n const [clearInputs, setClearInputs] = useState(false);\n\n const updateSize = () => {\n setSize([window?.innerWidth, window?.innerHeight]);\n };\n useEffect(() => {\n updateSize();\n }, []);\n useLayoutEffect(() => {\n window.addEventListener(\"resize\", updateSize);\n return () => window.removeEventListener(\"resize\", updateSize);\n }, [updateSize]);\n let media = MEDIA_TYPES.DESKTOP;\n if (size[0] > 768 && size[0] < 1024) {\n media = MEDIA_TYPES.TAB;\n }\n if (size[0] < 768) {\n media = MEDIA_TYPES.MOBILE;\n }\n\n const mediaDetails = useMemo(\n () => ({\n size,\n setSize,\n clearInputs,\n setClearInputs,\n media,\n }),\n [size, clearInputs, media]\n );\n\n return (\n <MediaContext.Provider value={mediaDetails}>\n {children}\n </MediaContext.Provider>\n );\n}\n\nexport default MediaContextProvider;\n","import React from \"react\";\n\nfunction SwitchIcon({ size = 16 }: { size?: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 25 25\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M22.6968 14.6968C22.6968 16.8185 21.8539 18.8533 20.3536 20.3536C18.8533 21.8539 16.8185 22.6968 14.6968 22.6968\"\n stroke=\"white\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M18.6968 11.6968V10.6968C18.6968 10.1663 18.4861 9.65764 18.111 9.28256C17.7359 8.90749 17.2272 8.69678 16.6968 8.69678C16.1663 8.69678 15.6576 8.90749 15.2826 9.28256C14.9075 9.65764 14.6968 10.1663 14.6968 10.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M14.6968 10.6968V9.69678C14.6968 9.16634 14.4861 8.65764 14.111 8.28256C13.7359 7.90749 13.2272 7.69678 12.6968 7.69678C12.1663 7.69678 11.6576 7.90749 11.2826 8.28256C10.9075 8.65764 10.6968 9.16634 10.6968 9.69678V10.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M10.6968 10.1968V4.69678C10.6968 4.16634 10.4861 3.65764 10.111 3.28256C9.73592 2.90749 9.22721 2.69678 8.69678 2.69678C8.16634 2.69678 7.65764 2.90749 7.28256 3.28256C6.90749 3.65764 6.69678 4.16634 6.69678 4.69678V14.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M18.6969 11.6968C18.6969 11.1663 18.9076 10.6576 19.2827 10.2826C19.6577 9.90749 20.1664 9.69678 20.6969 9.69678C21.2273 9.69678 21.736 9.90749 22.1111 10.2826C22.4862 10.6576 22.6969 11.1663 22.6969 11.6968V14.6968C22.6969 16.8185 21.854 18.8533 20.3537 20.3536C18.8534 21.8539 16.8186 22.6968 14.6969 22.6968H12.6969C9.89688 22.6968 8.19688 21.8368 6.70688 20.3568L3.10688 16.7568C2.76282 16.3757 2.57847 15.8769 2.592 15.3637C2.60554 14.8505 2.81593 14.3621 3.1796 13.9997C3.54327 13.6373 4.03238 13.4287 4.54565 13.417C5.05892 13.4053 5.55704 13.5914 5.93688 13.9368L7.69688 15.6968\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n\nexport default React.memo(SwitchIcon);\n","/* eslint-disable no-nested-ternary */\n\nimport React, { useCallback, useContext, useMemo } from \"react\";\nimport Header from \"../Header\";\nimport cn from \"clsx\";\nimport { LevelScreenProps } from \"../../types/interfaces\";\nimport SwitchIcon from \"../../Icons/SwitchIcon\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { useConfig } from \"../../utils/context/configContext\";\n\nfunction LevelScreen({ angle, countdown, isScanning, isInTargetRange, stabilityScore, children }: Omit<LevelScreenProps, \"config\">) {\n\tconst { translate } = useContext(LanguageContext) || {};\n\tconst config = useConfig();\n\n\tconst getProximityToTarget = useCallback(() => {\n\t\tif (angle < 80) return Math.max(0, Math.min(100, (angle - 60) * 10));\n\t\tif (angle > 95) return Math.max(0, Math.min(100, (105 - angle) * 10));\n\t\treturn 100;\n\t}, [angle]);\n\n\tconst backgroundColor = useMemo(() => {\n\t\tif (isScanning) return config?.style?.angleDetector?.successAngleBackground || \"#4f46e5\";\n\n\t\tconst proximity = getProximityToTarget();\n\t\tif (proximity === 0) return config?.style?.angleDetector?.successAngleLowBackground || \"#ffffff\";\n\t\tif (proximity === 100) return config?.style?.angleDetector?.successAngleBackground || \"#4f46e5\";\n\n const r = Math.round(255 - (255 - 139) * (proximity / 100));\n const g = Math.round(255 - (255 - 92) * (proximity / 100));\n const b = Math.round(255 - (255 - 246) * (proximity / 100));\n return `rgb(${r}, ${g}, ${b})`;\n }, [isScanning, getProximityToTarget]);\n\n\tconst handelTextColour = useCallback((isLight: boolean, proximity: number) => {\n\t\tif (proximity > 70) {\n\t\t\treturn isLight ? `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]/70` : `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]`;\n\t\t}\n\t\treturn isLight ? `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]/40` : `text-[${config?.style?.angleDetector?.successAngleTextDarkColor}]/70`;\n\t}, []);\n\n\tconst getTextColor = useCallback(\n\t\t(isLight = false) => {\n\t\t\tconst proximity = getProximityToTarget();\n\t\t\tif (isScanning) return `text-[${config?.style?.angleDetector?.successAngleTextLightColor}]`;\n\t\t\treturn handelTextColour(isLight, proximity);\n\t\t},\n\t\t[isScanning, getProximityToTarget],\n\t);\n\n\tconst handelColourCode = useCallback((isLight: boolean, proximity: number) => {\n\t\tif (proximity > 70) {\n\t\t\treturn isLight ? config?.style?.angleDetector?.successAngleTextLightColor : config?.style?.angleDetector?.successAngleTextLightColor;\n\t\t}\n\t\treturn isLight ? config?.style?.angleDetector?.successAngleTextLightColor : config?.style?.angleDetector?.successAngleTextDarkColor;\n\t}, []);\n\tconst getColorCode = useCallback(\n\t\t(isLight = false) => {\n\t\t\tconst proximity = getProximityToTarget();\n\t\t\tif (isScanning) return config?.style?.angleDetector?.successAngleTextLightColor;\n\t\t\treturn handelColourCode(isLight, proximity);\n\t\t},\n\t\t[isScanning, getProximityToTarget],\n\t);\n\tconst cd = countdown;\n\tfunction fixVh() {\n\t\tdocument.documentElement.style.setProperty(\"--real-vh\", window.innerHeight + \"px\");\n\t}\n\tfixVh();\n\twindow.addEventListener(\"resize\", fixVh);\n\treturn (\n\t\t<div\n\t\t\tclassName=\"flex common-ui-main w-screen flex-col items-center h-[var(--real-vh)] overflow-hidden touch-none justify-center transition-all duration-300 max-w-[28rem] mx-auto\"\n\t\t\tstyle={{ backgroundColor }}\n\t\t\tdata-testid=\"level-screen\"\n\t\t\tdata-angle-state={isScanning ? \"locked\" : isInTargetRange ? \"target\" : \"adjusting\"}\n\t\t>\n\t\t\t<div className=\"flex justify-start fixed top-[.5rem] max-w-[28rem] mx-auto w-full px-[1rem]\">\n\t\t\t\t<div className=\"flex justify-start \">\n\t\t\t\t\t<Header noTitle />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{cd !== null ? (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"relative flex h-[6rem] w-[6rem] items-center justify-center rounded-[9999px] bg-[#fff]/20 transition-all duration-300\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tborder: `2px solid ${getColorCode()}`,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={`text-[3rem] font-bold text-[${getColorCode()}]`}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n color: getColorCode(),\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{cd}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : isScanning ? (\n\t\t\t\t<div className=\"relative flex flex-col items-center justify-center\">\n\t\t\t\t\t<div className=\"relative flex h-16 w-16 items-center justify-center rounded-[9999px] border-2 bg-[#fff]/30 border-[#fff]\">\n\t\t\t\t\t\t<div className=\"h-4 w-4 rounded-[9999px] animate-pulse bg-[#fff]/80\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\"relative flex h-[4rem] w-[4rem] items-center justify-center rounded-[9999px] \", isInTargetRange ? \"bg-[#fff]/20 border-[#fff]\" : \"bg-[#fff]/10 border-[#fff]\")}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\ttransform: `translateY(${(90 - angle) * 3}px)`,\n\t\t\t\t\t\ttransition: \"transform 0.2s ease-out\",\n\t\t\t\t\t\tborder: `2px solid ${getColorCode()}`,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div className={`h-[1rem] w-[1rem] rounded-[9999px] bg-[${getColorCode()}]/80`}\n style={{\n\t\t\t\t\t\tbackgroundColor: `${getColorCode()}B3`,\n\t\t\t\t\t}}\n />\n\t\t\t\t\t<div\n\t\t\t\t\t\t// className=\" text-[#fff]\"\n\t\t\t\t\t\tclassName={cn(\"mt-[.5rem] text-center w-[180px] flex-col flex items-center absolute top-[60px]\", getTextColor())}\n style={{\n\t\t\t\t\t\tcolor: getColorCode(),\n\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<SwitchIcon size={30} />\n\t\t\t\t\t\t<p\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{\" \"}\n\t\t\t\t\t\t\t{translate?.(LanguageKeys.startLevelCheck)}\n\t\t\t\t\t\t\t<br />\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{cd !== null && (\n\t\t\t\t<div className=\"absolute bottom-[8rem] text-center\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\"text-sm font-medium px-4 py-1.5 bg-black/20 rounded-[9999px] mt-8\", getTextColor())}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n color: getColorCode(),\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{translate?.(LanguageKeys.leavePhone)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{isInTargetRange && cd === null && !isScanning && (\n\t\t\t\t<div className=\"absolute bottom-[8rem] w-[12rem]\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={`h-[.375rem] w-full bg-[${getColorCode()}]/20 rounded-[9999px] overflow-hidden`}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tbackgroundColor: `${getColorCode()}33`, // 20% opacity\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={`h-full bg-[${getColorCode()}]/70 transition-all duration-300 rounded-[9999px]`}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\twidth: `${angle}%`,\n\t\t\t\t\t\t\t\tbackgroundColor: `${getColorCode()}B3`,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n {cd === null && !isScanning && (\n <div className=\"absolute bottom-[5rem] text-center\">\n <div className={cn(\"text-[1.5rem] font-light\", getTextColor())}\n style={{\n\t\t\t\t\t\tcolor: getColorCode(),\n\t\t\t\t\t}}\n >\n {Math.round(angle)}°\n </div>\n </div>\n )}\n\n\t\t\t{cd === null && !isScanning && (\n\t\t\t\t<div className=\"absolute bottom-[2rem] text-center max-w-[20rem] mx-auto\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\"text-[.75rem] opacity-50\", getTextColor())}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tfontFamily: config?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n color: getColorCode(),\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{translate?.(LanguageKeys.placePhoneUpright)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\nexport default React.memo(LevelScreen);\n","import { useContext } from \"react\";\nimport Webcam from \"react-webcam\";\nimport { CameraScanChildProps } from \"../../types/interfaces\";\nimport { MEDIA_TYPES, MediaContext } from \"../../utils/context/mediaContext\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport { pauseIcon, voiceOverAssetsPath } from \"../../utils/constants\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { X } from \"lucide-react\";\n\n\n\nfunction CameraScanChild({\n resetScan,\n loadingCam,\n onUserMedia,\n onPause,\n pause,\n onReScan,\n recordingStarted,\n startSendingVideoFrames,\n showPause,\n webcamRef,\n onUserMediaError,\n resetDetector,\n videoConstraints,\n webcamKey,\n faceDone,\n isScanning,\n}: Omit<CameraScanChildProps, \"config\">) {\n const { media } = useContext(MediaContext) || {};\n const { translate } = useContext(LanguageContext) || {};\n\n return (\n <div className=\"App w-screen h-[100vh] relative common-ui-main\" data-testid=\"camera-scan\" data-scan-phase={faceDone ? \"spinning\" : recordingStarted ? \"pose-detecting\" : isScanning ? \"starting\" : \"ready\"}>\n <span\n onClick={() => {\n resetScan();\n resetDetector();\n }}\n className=\"fixed right-[20px] top-[20px] z-[999]\"\n >\n <X className=\"text-[#fff]\" />\n </span>\n\n <div className=\"w-full h-full overflow-hidden \">\n {media === MEDIA_TYPES.MOBILE && (\n <Webcam\n key={webcamKey}\n audio={false}\n ref={webcamRef}\n screenshotQuality={1}\n videoConstraints={videoConstraints}\n mirrored\n screenshotFormat=\"image/jpeg\"\n onUserMedia={onUserMedia}\n onUserMediaError={onUserMediaError}\n style={{\n position: \"fixed\",\n top: 0,\n bottom: 0,\n zIndex: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n }}\n />\n )}\n </div>\n\n <div className=\"fixed bottom-[30px] w-full z-[999] flex flex-col gap-4 items-center justify-center\">\n {showPause && (\n <SpecificButton\n className=\"!w-[180px] !h-[40px] !py-[0] mx-auto \"\n prefix={pauseIcon}\n buttonText={translate?.(LanguageKeys.pause)}\n buttonFunc={onPause}\n btnSecondary\n />\n )}\n\n {pause ? (\n <div>\n <SpecificButton\n className=\"!w-[180px] !h-[40px] !py-[0] mx-auto\"\n buttonText={translate?.(LanguageKeys.restart)}\n buttonFunc={onReScan}\n btnSecondary\n />\n </div>\n ) : !recordingStarted ? (\n <SpecificButton\n data-testid=\"start-scan-btn\"\n className={`!w-[180px] !h-[40px] !py-[0] mx-auto !bg-[#ffffff] !text-[#000000] ${\n loadingCam ? \"!opacity-50\" : \"\"\n }`}\n buttonText={translate?.(LanguageKeys.startScan)}\n buttonFunc={startSendingVideoFrames}\n disabled={loadingCam}\n />\n ) : null}\n </div>\n\n <audio\n id=\"audioElement\"\n crossOrigin=\"anonymous\"\n preload=\"auto\"\n style={{\n position: \"absolute\",\n zIndex: -99999,\n }}\n src={`${voiceOverAssetsPath}scanAudioInstructions/silence.mp3`}\n />\n </div>\n );\n}\n\nexport default CameraScanChild;\n","import { useState, useRef, useEffect } from \"react\";\n// We keep this, but we will make it optional in the logic below\n\ntype KeypointTuple = [number, number, number];\n\n// GLOBAL SINGLETONS (Persist across component re-mounts)\n// This prevents reloading the heavy model if the user navigates away and back.\nlet globalLoadingPromise: Promise<boolean> | null = null;\nlet globalDetector: any = null;\nlet globalPoseLib: any = null;\n\nexport default function useTensorFlow() {\n const [consecutiveFronts, setConsecutiveFronts] = useState(0);\n const [spinPhase, setSpinPhase] = useState(0);\n const [isLoaded, setIsLoaded] = useState(false);\n\n const spinPhaseRef = useRef(0);\n const consecutiveFrontsRef = useRef(0);\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true; // Reset on mount\n\n // 1. If already loaded globally, just use it.\n if (globalDetector) {\n setIsLoaded(true);\n return;\n }\n\n // 2. If not loading, start the process.\n if (!globalLoadingPromise) {\n globalLoadingPromise = loadDependenciesGlobal();\n }\n\n // 3. Wait for the global promise to resolve.\n globalLoadingPromise.then((success) => {\n if (success && mounted.current) {\n setIsLoaded(true);\n }\n });\n\n return () => {\n mounted.current = false;\n };\n }, []);\n\n /**\n * Loads TensorFlow/MediaPipe dynamically.\n * This logic is ISOLATED from the server.\n */\n async function loadDependenciesGlobal(): Promise<boolean> {\n // GUARDRAIL 1: Strict Environment Check\n // Ensure we are in a browser environment with media capabilities\n if (\n typeof window === 'undefined' || \n typeof navigator === 'undefined'\n ) {\n return false;\n }\n \n try {\n console.log(\"Starting TensorFlow preload...\");\n \n // GUARDRAIL 2: Dynamic Imports\n // These heavy modules are only fetched by the browser, never the server.\n const [poseLib, tfjsCore, tfjsBackend] = await Promise.all([\n import(\"@tensorflow-models/pose-detection\"),\n import(\"@tensorflow/tfjs-core\"),\n import(\"@tensorflow/tfjs-backend-webgl\") // Triggers backend registration side-effect\n ]);\n \n globalPoseLib = poseLib;\n \n // Explicitly set the backend to WebGL for performance\n await tfjsCore.setBackend('webgl');\n await tfjsCore.ready();\n console.log(\"TensorFlow backend ready (WebGL)\");\n\n // GUARDRAIL 3: Zero-Config Asset Loading\n // If TENSORFLOW_SOLUTION_PATH fails or is missing, we must fallback \n // to a CDN so the user doesn't have to copy files manually.\n const modelConfig = {\n runtime: \"mediapipe\",\n modelType: \"full\",\n solutionPath:\"https://cdn.jsdelivr.net/npm/@mediapipe/pose\", // Fallback for Zero Config\n } as const;\n\n try {\n globalDetector = await globalPoseLib.createDetector(\n globalPoseLib.SupportedModels.BlazePose,\n modelConfig\n );\n console.log(\"MediaPipe detector created successfully\");\n } catch (mediapipeError) {\n console.warn(\"MediaPipe failed, falling back to TFJS runtime:\", mediapipeError);\n \n // Fallback to TFJS runtime (slower but works without external assets)\n globalDetector = await globalPoseLib.createDetector(\n globalPoseLib.SupportedModels.BlazePose,\n {\n runtime: \"tfjs\",\n modelType: \"full\",\n }\n );\n console.log(\"TFJS detector created successfully\");\n }\n \n return true;\n } catch (error) {\n console.error(\"Failed to load TensorFlow dependencies:\", error);\n globalLoadingPromise = null; // Reset so retry is possible\n return false;\n }\n }\n\n function isFrontFrame(body: KeypointTuple[]) {\n // Guardrail: Ensure points exist before filtering\n if (!body || body.length === 0) return false;\n return body.filter((p) => p[2] > 0.7).length === 22;\n }\n\n const poseDetector = async (callback: () => void, webcamRef: any) => {\n // GUARDRAIL 4: Runtime Safety\n // Check if everything is truly ready before calculating\n if (!globalDetector || !mounted.current || !webcamRef?.current?.video) {\n return;\n }\n \n // Check if video is actually playing and has data\n const video = webcamRef.current.video;\n if (video.readyState < 2) return; \n\n try {\n const poses = await globalDetector.estimatePoses(\n video,\n { flipHorizontal: false }\n );\n\n if (!poses || !poses.length) return;\n\n const keypoints = poses[0].keypoints;\n \n // Standardize keypoints\n const body: KeypointTuple[] = keypoints\n .slice(11) // Ignore face keypoints (0-10)\n .map((kp: any) => [\n kp.x > 0 && kp.x < 720 ? kp.x : -1,\n kp.y > 0 && kp.y < 1280 ? kp.y : -1,\n kp.score ?? 0,\n ]);\n\n if (spinPhaseRef.current === 0 && !isFrontFrame(body)) {\n setConsecutiveFronts((prev) => prev + 1);\n }\n\n if (spinPhaseRef.current === 1 && isFrontFrame(body)) {\n setConsecutiveFronts((prev) => prev + 1);\n }\n\n if (spinPhaseRef.current === 2) callback();\n } catch (err) {\n console.error(\"Pose detection error:\", err);\n }\n };\n\n useEffect(() => {\n consecutiveFrontsRef.current = consecutiveFronts;\n if (consecutiveFronts > 6 && spinPhaseRef.current < 2) {\n setSpinPhase((s) => s + 1);\n setConsecutiveFronts(0);\n }\n }, [consecutiveFronts]);\n\n useEffect(() => {\n spinPhaseRef.current = spinPhase;\n }, [spinPhase]);\n\n const resetDetector = () => {\n spinPhaseRef.current = 0;\n consecutiveFrontsRef.current = 0;\n setConsecutiveFronts(0);\n setSpinPhase(0);\n };\n\n const retryLoading = async () => {\n if (!globalDetector && !globalLoadingPromise) {\n globalLoadingPromise = loadDependenciesGlobal();\n const success = await globalLoadingPromise;\n if (success && mounted.current) {\n setIsLoaded(true);\n }\n }\n };\n\n return { \n poseDetector, \n isLoaded,\n spinPhase,\n resetDetector,\n retryLoading\n };\n}","/* eslint-disable no-use-before-define */\nimport React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport CameraScanChild from \"./CameraScanChild\";\nimport posthog from \"../../utils/posthog\";\nimport Webcam from \"react-webcam\";\nimport { ScanningComponentProps } from \"../../types/interfaces\";\nimport useTensorFlow from \"../../customHooks/useTensorFlow\";\nimport useWatchdog from \"../../customHooks/useWatchdog\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\nimport {\n\tgenerateUuid,\n\tgetCurrentTimeInSeconds,\n\tgetPreferredMediaRecorderTypes,\n\tgetRecordingExtension,\n\tgetRecordingMimeType,\n\tgetSupportedMediaRecorderTypes,\n\thandleScanTimeCapture,\n\trescanSupportCaptureEvent,\n} from \"../../utils/utils\";\nimport speechService from \"../../utils/service/speechService\";\nimport { videoConstraintsExact, videoConstraintsFallback, videoTypes, voiceOverAssetsPath } from \"../../utils/constants\";\n\n// If frames are streaming to the pose-detection backend but no instruction comes\n// back within this window, treat the scan as stalled and surface a retry instead\n// of freezing forever (see the watchdog wiring below).\nconst POSE_FEEDBACK_TIMEOUT_MS = 12000;\n\nlet audioTimeoutId: ReturnType<typeof setTimeout> | undefined;\nlet lastVideoPlayed: {\n\tskipCount: number;\n\tno_of_times_skipped: number;\n\taudioName: string;\n} | null = null;\n\nfunction ScanningComponent({ setIsScanLocked, resetDetector, scanID, setIsVideoUploaded, setScanFailsError, setScanStartTime, setScanUniqueKey, userDetails }: Omit<ScanningComponentProps, \"config\">) {\n\tconst { email, shopDomain, gender, heightInCm } = userDetails;\n\tconst webcamRef = useRef<Webcam | null>(null);\n\tconst mediaRecorderRef = useRef<MediaRecorder | null>(null);\n\tconst recordedFpsRef = useRef<number | null>(null);\n\tconst [recordedChunks, setRecordedChunks] = useState<Blob[]>([]);\n\tconst [loadingCam, setLoadingCam] = useState(true);\n\tconst [recordingStarted, setRecordingStarted] = useState(false);\n\tconst [faceDone, setFaceDone] = useState(false);\n\tconst [mediaRecorderStopped, setMediaRecorderStopped] = useState(true);\n\tconst [isScanning, setIsScanning] = useState(false);\n\tconst captureVideoTimeOutHandle = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst [audioSource, setAudioSource] = useState(\"\");\n\tconst [audioSourceList, setAudioSourceList] = useState<string[]>([]);\n\tconst [emptyAudioSource, setEmptyAudioSource] = useState(\"\");\n\tconst [startAgain, setStartAgain] = useState(false);\n\tconst [showRestart, setRestart] = useState(false);\n\tconst [pause, setPause] = useState(false);\n\tconst [events, setEvents] = useState<any>([]);\n\tconst [showPause, setShowPause] = useState(false);\n\tconst allowAudioToPlay = useRef(true);\n\tconst { poseDetector } = useTensorFlow();\n\tconst firstScan = useRef(true);\n\tconst poseStoppedRef = useRef(false);\n\tconst counterRef = useRef(0);\n\tconst consecutiveFalseRef = useRef(0);\n\t// Diagnostics: count frames actually emitted to the BE vs. skipped because the\n\t// webcam screenshot came back null (a silent cause of \"scan frozen\" with no feedback).\n\tconst framesSentRef = useRef(0);\n\tconst framesSkippedRef = useRef(0);\n\tconst poseListenerRegistered = useRef(false);\n\tconst isConnectingRef = useRef(false);\n\tconst brilliantPlayedRef = useRef(false);\n\tconst postPoseProcessRef = useRef<() => void>(() => {});\n\tconst startSendingVideoFramesRef = useRef<() => void>(() => {});\n\tconst idRef = useRef<string | null>(null);\n\tconst [supportedTypes, setSupportedTypes] = useState<string[]>([]);\n\tconst [webcamKey, setWebcamKey] = useState(0);\n\tconst [currentVideoConstraints, setCurrentVideoConstraints] = useState<MediaTrackConstraints>(videoConstraintsExact);\n\tconst { setStartGyro, uploadScanFile, setUploadLoading, swan, onScanStart, onCaptureComplete } = useContext(ParamsContext);\n\n\t// Refs to avoid stale closures in callbacks after pause/restart\n\tconst scanIDRef = useRef(scanID);\n\tuseEffect(() => {\n\t\tscanIDRef.current = scanID;\n\t}, [scanID]);\n\n\tconst eventsRef = useRef(events);\n\tuseEffect(() => {\n\t\teventsRef.current = events;\n\t}, [events]);\n\n\t// Watchdog (LOG-ONLY): the scan depends on the pose-detection backend sending\n\t// guidance back while we stream frames. If frames are flowing but nothing comes\n\t// back for POSE_FEEDBACK_TIMEOUT_MS, this fires a single telemetry event so the\n\t// stall is visible in analytics. It deliberately does NOT change the UX — the\n\t// screen behaves exactly as before — so this build is pure measurement. Once the\n\t// data confirms the pattern, the recovery action (stop + retry UI) can be enabled.\n\t// The dog is started when the first frame goes out, petted on every backend\n\t// message, and stopped on pause/confirm/restart/unmount.\n\tconst handlePoseFeedbackTimeout = useCallback(() => {\n\t\tposthog.capture(`${shopDomain}/pose_feedback_timeout`, {\n\t\t\tscanID: scanIDRef.current,\n\t\t\temail,\n\t\t\tframesSent: framesSentRef.current,\n\t\t\tid: idRef.current,\n\t\t});\n\t\t// --- Recovery action (disabled for this measurement-only build) ---\n\t\t// Uncomment to turn the stall into a recoverable retry instead of a freeze:\n\t\t// allowAudioToPlay.current = false;\n\t\t// speechService.stopAudio();\n\t\t// clearTimeout(audioTimeoutId);\n\t\t// if (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\t// if (mediaRecorderRef.current) {\n\t\t// \ttry {\n\t\t// \t\tmediaRecorderRef.current.stop();\n\t\t// \t} catch (e) {\n\t\t// \t\tconsole.log(\"watchdog: error stopping recorder\", e);\n\t\t// \t}\n\t\t// }\n\t\t// swan.poseDetection.disconnect();\n\t\t// poseListenerRegistered.current = false;\n\t\t// setScanFailsError(\"Scan timed out. Please try again.\");\n\t}, [shopDomain, email]);\n\n\tconst poseWatchdog = useWatchdog({ timeoutMs: POSE_FEEDBACK_TIMEOUT_MS, onTimeout: handlePoseFeedbackTimeout });\n\n\tconst resetScan = () => {\n\t\tposeStoppedRef.current = false;\n\t\tposeWatchdog.stop();\n\t\tclearTimeout(audioTimeoutId);\n\t\tif (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\tsetScanUniqueKey(generateUuid());\n\t\tsetScanFailsError(\"\");\n\t\tsetUploadLoading?.(false);\n\t\tsetRecordedChunks([]);\n\t\trecordedFpsRef.current = null;\n\t\tsetLoadingCam(true);\n\t\tsetRecordingStarted(false);\n\t\tsetFaceDone(false);\n\t\tsetMediaRecorderStopped(true);\n\t\tsetIsScanning(false);\n\t\tcaptureVideoTimeOutHandle.current = null;\n\t\tsetAudioSource(\"\");\n\t\tsetAudioSourceList([]);\n\t\tsetEmptyAudioSource(\"\");\n\t\tsetStartAgain(!startAgain);\n\t\tsetStartGyro(false);\n\t\tspeechService.stopAudio();\n\t\tsetRestart(false);\n\t\tsetPause(false);\n\t\tcounterRef.current = 0;\n\t\tconsecutiveFalseRef.current = 0;\n\t\tframesSentRef.current = 0;\n\t\tframesSkippedRef.current = 0;\n\t\tposeListenerRegistered.current = false;\n\t\tbrilliantPlayedRef.current = false;\n\t\tif (mediaRecorderRef.current !== null) {\n\t\t\tmediaRecorderRef.current.stop();\n\t\t}\n\n\t\tevents.forEach((el: any) => {\n\t\t\tif (mediaRecorderRef.current) mediaRecorderRef.current.removeEventListener(\"dataavailable\", el);\n\t\t});\n\n\t\tsetEvents([]);\n\t\tallowAudioToPlay.current = true;\n\t\tsetShowPause(false);\n\t\tsetIsVideoUploaded(false);\n\t\tif (webcamRef.current?.stream) {\n\t\t\tsetLoadingCam(false);\n\t\t}\n\t};\n\n\tconst handleRestart = async () => {\n\t\trescanSupportCaptureEvent({\n\t\t\teventName: `${shopDomain}/rescan`,\n\t\t\temail,\n\t\t\tscanID: scanIDRef.current,\n\t\t\theight: heightInCm,\n\t\t\tgender,\n\t\t\tstatus: false,\n\t\t});\n\t\tswan.poseDetection.disconnect();\n\t\tresetScan();\n\t\tawait handleSocket();\n\t};\n\n\tconst handleUserMedia = useCallback(() => {\n\t\tsetTimeout(() => {\n\t\t\tsetLoadingCam(false);\n\t\t}, 1000);\n\t}, []);\n\n\tconst handleUserMediaError = useCallback(\n\t\t(error: string | DOMException) => {\n\t\t\tconsole.log(\"camera error\", error);\n\t\t\t// Retry once with relaxed constraints for broader device compatibility.\n\t\t\tif (currentVideoConstraints === videoConstraintsExact) {\n\t\t\t\tsetCurrentVideoConstraints(videoConstraintsFallback);\n\t\t\t\tsetWebcamKey((prev) => prev + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetLoadingCam(false);\n\t\t},\n\t\t[currentVideoConstraints],\n\t);\n\n\tconst handlePause = useCallback(() => {\n\t\tallowAudioToPlay.current = false;\n\t\tposeWatchdog.stop();\n\t\tsetPause(true);\n\t\tsetShowPause(false);\n\t\tif (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\tif (mediaRecorderRef.current) {\n\t\t\tmediaRecorderRef.current.pause();\n\t\t}\n\t\tspeechService.stopAudio();\n\t\tswan.poseDetection.disconnect();\n\t\tposthog.capture(`${shopDomain}/pose_detection_disconnected`, {\n\t\t\tscanID: scanID,\n\t\t\temail,\n\t\t\tid: idRef.current,\n\t\t\treason: \"pause\",\n\t\t});\n\t\tcounterRef.current = 0;\n\t\tconsecutiveFalseRef.current = 0;\n\t\tposeListenerRegistered.current = false;\n\t\tbrilliantPlayedRef.current = false;\n\t\tif (mediaRecorderRef.current) {\n\t\t\tmediaRecorderRef.current.stop();\n\t\t}\n\t\tevents.forEach((el: any) => {\n\t\t\tif (mediaRecorderRef.current) mediaRecorderRef.current.removeEventListener(\"dataavailable\", el);\n\t\t});\n\t\tsetEvents([]);\n\t\tclearTimeout(audioTimeoutId);\n\t}, [mediaRecorderRef, events, speechService, allowAudioToPlay]);\n\n\t// const supportedTypes = videoTypes.filter((type) => MediaRecorder.isTypeSupported(type));\n\tconst preferredVideoTypes = useMemo(() => getPreferredMediaRecorderTypes(), []);\n\tconst recordingMimeType = getRecordingMimeType(supportedTypes);\n\tconst recordingExtension = getRecordingExtension(recordingMimeType);\n\n\tconst stopRecording = useCallback(async () => {\n\t\tallowAudioToPlay.current = true;\n\t\tawait speechService.playAudio(`${voiceOverAssetsPath}SpotOn.mp3`);\n\t\tsetPause(false);\n\t\tsetShowPause(false);\n\t\tif (captureVideoTimeOutHandle.current) clearTimeout(captureVideoTimeOutHandle.current);\n\t\tsetRecordingStarted(false);\n\t}, [captureVideoTimeOutHandle, speechService]);\n\n\tconst handleSocket = useCallback(async () => {\n\t\t// Guard against React StrictMode double-mount: skip if already connecting or connected\n\t\tif (isConnectingRef.current || idRef.current) return;\n\t\tisConnectingRef.current = true;\n\t\ttry {\n\t\t\tidRef.current = await swan.poseDetection.connect();\n\n\t\t\tposthog.capture(`${shopDomain}/pose_detection_connected`, {\n\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\temail,\n\t\t\t\tid: idRef.current,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.log(error, \"while connecting websocket\");\n\t\t\tposthog.capture(`${shopDomain}/pose_detection_connect_failed`, {\n\t\t\t\tscanID: scanID,\n\t\t\t\temail,\n\t\t\t\terror: String(error),\n\t\t\t});\n\t\t} finally {\n\t\t\tisConnectingRef.current = false;\n\t\t}\n\t}, [scanID, shopDomain, email]);\n\nconst handleSpinDataAvailable = useCallback(\n\t\t({ data }: BlobEvent) => {\n\t\t\tif (data && data.size > 0 && allowAudioToPlay.current) {\n\t\t\t\tsetRecordedChunks((prev) => prev.concat(data));\n\t\t\t\tif (!poseStoppedRef.current && webcamRef.current) {\n\t\t\t\t\tposeStoppedRef.current = true;\n\t\t\t\t\tposeDetector(() => {\n\t\t\t\t\t\tstopRecording();\n\t\t\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\t\t\teventName: `${shopDomain}/tensorFlow`,\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\tmessage: \"recording stopped by tensorflow \",\n\t\t\t\t\t\t});\n\t\t\t\t\t}, webcamRef);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[stopRecording, handleScanTimeCapture, shopDomain, email, webcamRef],\n\t);\n\n\tconst playAudio = useCallback(async () => {\n\t\tconst lastAudio = audioSourceList[audioSourceList.length - 1];\n\t\tif (lastAudio && allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(voiceOverAssetsPath + lastAudio);\n\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\taudioTimeoutId = setTimeout(playAudio, 2000);\n\t\t\t}\n\t\t}\n\t}, [audioSourceList, allowAudioToPlay]);\n\n\tconst handleStartCaptureClick = useCallback(() => {\n\t\tif (mediaRecorderRef && mediaRecorderRef.current) {\n\t\t\tmediaRecorderRef.current.stop();\n\t\t}\n\t\tsetRecordingStarted(true);\n\t\ttry {\n\t\t\tif (webcamRef && webcamRef.current && webcamRef.current.stream) {\n\t\t\t\t// Capture FPS from stream settings\n\t\t\t\tconst videoTrack = webcamRef.current.stream.getVideoTracks()[0];\n\t\t\t\tif (videoTrack) {\n\t\t\t\t\tconst settings = videoTrack.getSettings();\n\t\t\t\t\trecordedFpsRef.current = settings.frameRate || null;\n\t\t\t\t}\n\n\t\t\t\tconst mediaRecorderOptions: MediaRecorderOptions = {\n\t\t\t\t\tvideoBitsPerSecond: 1_500_000,\n\t\t\t\t};\n\t\t\t\tif (supportedTypes[0]) {\n\t\t\t\t\tmediaRecorderOptions.mimeType = supportedTypes[0];\n\t\t\t\t}\n\t\t\t\tmediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, mediaRecorderOptions);\n\t\t\t\tmediaRecorderRef.current.addEventListener(\"dataavailable\", handleSpinDataAvailable);\n\n\t\t\t\t// Mark recorder as fully stopped only after all dataavailable events have fired\n\t\t\t\tmediaRecorderRef.current.addEventListener(\"stop\", () => {\n\t\t\t\t\tsetMediaRecorderStopped(true);\n\t\t\t\t});\n\n\t\t\t\tsetEvents([...events, handleSpinDataAvailable]);\n\t\t\t\tmediaRecorderRef.current.start(1000);\n\t\t\t\tsetMediaRecorderStopped(false);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.log(\"error while using media recorder\", e);\n\t\t}\n\t}, [webcamRef, supportedTypes, handleSpinDataAvailable, events]);\n\n\tconst postPoseProcess = useCallback(async () => {\n\t\tif (captureVideoTimeOutHandle.current) {\n\t\t\tclearTimeout(captureVideoTimeOutHandle.current);\n\t\t}\n\t\thandleStartCaptureClick();\n\t\tif (allowAudioToPlay.current) {\n\t\t\tcaptureVideoTimeOutHandle.current = setTimeout(async () => {\n\t\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}SpotOn.mp3`);\n\t\t\t\t\tsetRecordingStarted(false);\n\t\t\t\t}\n\t\t\t}, 15000);\n\t\t}\n\t\tsetFaceDone(true);\n\t\tif (allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}Spin.mp3`);\n\t\t}\n\t}, [handleStartCaptureClick, allowAudioToPlay, speechService]);\n\n\tuseEffect(() => {\n\t\tpostPoseProcessRef.current = postPoseProcess;\n\t}, [postPoseProcess]);\n\n\tconst handleDataAvailable = useCallback(\n\t\t({ data }: BlobEvent) => {\n\t\t\tconst connected = swan.poseDetection.connected();\n\t\t\tif (data.size > 0 && connected) {\n\t\t\t\tif (!poseListenerRegistered.current) {\n\t\t\t\t\tposeListenerRegistered.current = true;\n\t\t\t\t\tswan.poseDetection.poseStatus(async (data: any) => {\n\t\t\t\t\t\t// The backend is alive and talking — reset the stall watchdog.\n\t\t\t\t\t\tposeWatchdog.pet();\n\t\t\t\t\t\t// Capture EVERY backend pose message BEFORE the audio gate below, so\n\t\t\t\t\t\t// telemetry can distinguish \"BE silent\" from \"BE sending audio-less\n\t\t\t\t\t\t// frames\". The latter would silently stall the scan, since all of the\n\t\t\t\t\t\t// handling below is gated on data.audio being non-empty.\n\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_instruction_received`, {\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\taudio: data?.audio ?? \"\",\n\t\t\t\t\t\t\taudioEmpty: !(data?.audio && data.audio.length > 0),\n\t\t\t\t\t\t\tstatus: data?.status,\n\t\t\t\t\t\t\tsid: data?.sid,\n\t\t\t\t\t\t\tsidMatchesConnection: data?.sid === idRef.current,\n\t\t\t\t\t\t\tcounter: counterRef.current,\n\t\t\t\t\t\t\tconsecutiveFalse: consecutiveFalseRef.current,\n\t\t\t\t\t\t\ttype: \"body_scan_instruction\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (data && data.audio && data.audio.length > 0) {\n\t\t\t\t\t\t\tconst audio = document.querySelector(\"#audioElement\") as HTMLAudioElement | null;\n\n\t\t\t\t\t\t\tif (data.status === true && data.sid === idRef.current) {\n\t\t\t\t\t\t\t\tconsecutiveFalseRef.current = 0;\n\t\t\t\t\t\t\t\tif (counterRef.current < 2) {\n\t\t\t\t\t\t\t\t\tcounterRef.current += 1;\n\t\t\t\t\t\t\t\t\tif (counterRef.current === 1 && !brilliantPlayedRef.current) {\n\t\t\t\t\t\t\t\t\t\tbrilliantPlayedRef.current = true;\n\t\t\t\t\t\t\t\t\t\tclearTimeout(audioTimeoutId);\n\t\t\t\t\t\t\t\t\t\tspeechService.stopAudio();\n\t\t\t\t\t\t\t\t\t\tawait speechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t\t} else if (counterRef.current === 2 && audio?.paused) {\n\t\t\t\t\t\t\t\t\t\tclearTimeout(audioTimeoutId);\n\t\t\t\t\t\t\t\t\t\tspeechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// Alignment confirmed — no more guidance expected, disarm watchdog.\n\t\t\t\t\t\t\t\t\tposeWatchdog.stop();\n\t\t\t\t\t\t\t\t\tsetEmptyAudioSource(data.audio);\n\t\t\t\t\t\t\t\t\tclearTimeout(audioTimeoutId);\n\t\t\t\t\t\t\t\t\tswan.poseDetection.disconnect();\n\t\t\t\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_detection_disconnected`, {\n\t\t\t\t\t\t\t\t\t\tscanID: scanID,\n\t\t\t\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\t\t\t\tid: idRef.current,\n\t\t\t\t\t\t\t\t\t\treason: \"pose_confirmed\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tposeListenerRegistered.current = false;\n\t\t\t\t\t\t\t\t\tsetTimeout(() => postPoseProcessRef.current(), 1000);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconsecutiveFalseRef.current += 1;\n\t\t\t\t\t\t\t\tif (consecutiveFalseRef.current >= 3) {\n\t\t\t\t\t\t\t\t\tcounterRef.current = 0;\n\t\t\t\t\t\t\t\t\tbrilliantPlayedRef.current = false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (audio?.paused && (!lastVideoPlayed || lastVideoPlayed?.audioName !== data.audio)) {\n\t\t\t\t\t\t\t\t\tlastVideoPlayed = {\n\t\t\t\t\t\t\t\t\t\tskipCount: 2,\n\t\t\t\t\t\t\t\t\t\tno_of_times_skipped: 0,\n\t\t\t\t\t\t\t\t\t\taudioName: data.audio,\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tspeechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t} else if (lastVideoPlayed?.audioName === data.audio && audio?.paused) {\n\t\t\t\t\t\t\t\t\tif (lastVideoPlayed && lastVideoPlayed.no_of_times_skipped >= lastVideoPlayed.skipCount) {\n\t\t\t\t\t\t\t\t\t\tlastVideoPlayed.no_of_times_skipped = 0;\n\t\t\t\t\t\t\t\t\t\tspeechService.playAudio(voiceOverAssetsPath + data.audio);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tif (lastVideoPlayed) lastVideoPlayed.no_of_times_skipped += 1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst frame = webcamRef?.current ? webcamRef.current.getScreenshot() : null;\n\t\t\t\tif (frame) {\n\t\t\t\t\tframesSentRef.current += 1;\n\t\t\t\t\t// First frame out -> start the stall watchdog; backend messages pet it.\n\t\t\t\t\tif (framesSentRef.current === 1) poseWatchdog.start();\n\t\t\t\t\tswan.poseDetection.videoEmit({\n\t\t\t\t\t\timage: frame,\n\t\t\t\t\t\tscanId: scanIDRef.current,\n\t\t\t\t\t});\n\t\t\t\t\t// Track frame delivery so a \"connected but no frames\" stall is visible in\n\t\t\t\t\t// telemetry. Log the first frame, then every 5th to bound event volume.\n\t\t\t\t\tif (framesSentRef.current === 1 || framesSentRef.current % 5 === 0) {\n\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_frame_sent`, {\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\tsid: idRef.current,\n\t\t\t\t\t\t\tframesSent: framesSentRef.current,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Screenshot came back null/empty -> frames are NOT reaching the BE, which\n\t\t\t\t\t// silently stalls the scan with no pose feedback. Track it (throttled).\n\t\t\t\t\tframesSkippedRef.current += 1;\n\t\t\t\t\tif (framesSkippedRef.current === 1 || framesSkippedRef.current % 5 === 0) {\n\t\t\t\t\t\tposthog.capture(`${shopDomain}/pose_frame_skipped_null`, {\n\t\t\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\tframesSkipped: framesSkippedRef.current,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[webcamRef, scanID, swan, speechService, shopDomain, email],\n\t);\n\n\tconst startSendingVideoFrames = useCallback(async () => {\n\t\tsetIsScanning(true);\n\t\tsetStartGyro(true);\n\t\tonScanStart?.();\n\t\tif (firstScan.current) {\n\t\t\tfirstScan.current = false;\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: \"scan started\",\n\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\tstatus: \"success\",\n\t\t\t\temail,\n\t\t\t});\n\t\t}\n\t\tsetScanStartTime(getCurrentTimeInSeconds());\n\t\tsetLoadingCam(true);\n\t\tif (allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}StartScan.mp3`);\n\t\t}\n\t\tif (allowAudioToPlay.current) {\n\t\t\tawait speechService.playAudio(`${voiceOverAssetsPath}LiftArmsAndHoldAtHip.mp3`);\n\t\t}\n\t\tif (allowAudioToPlay.current) {\n\t\t\tsetIsScanning(false);\n\t\t\tsetRecordingStarted(true);\n\t\t\tsetRestart(true);\n\t\t\tsetShowPause(true);\n\t\t\thandleUserMedia();\n\t\t}\n\t\ttry {\n\t\t\tif (webcamRef && webcamRef.current && webcamRef.current.stream && allowAudioToPlay.current) {\n\t\t\t\t// Capture FPS from stream settings\n\t\t\t\tconst videoTrack = webcamRef.current.stream.getVideoTracks()[0];\n\t\t\t\tif (videoTrack) {\n\t\t\t\t\tconst settings = videoTrack.getSettings();\n\t\t\t\t\trecordedFpsRef.current = settings.frameRate || null;\n\t\t\t\t}\n\n\t\t\t\tconst mediaRecorderOptions: MediaRecorderOptions = {\n\t\t\t\t\tvideoBitsPerSecond: 1_500_000,\n\t\t\t\t};\n\t\t\t\tif (supportedTypes[0]) {\n\t\t\t\t\tmediaRecorderOptions.mimeType = supportedTypes[0];\n\t\t\t\t}\n\t\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\t\tmediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, mediaRecorderOptions);\n\t\t\t\t}\n\t\t\t\tif (allowAudioToPlay.current && mediaRecorderRef.current) {\n\t\t\t\t\tmediaRecorderRef.current.addEventListener(\"dataavailable\", handleDataAvailable);\n\t\t\t\t}\n\n\t\t\t\tsetEvents([...eventsRef.current, handleDataAvailable]);\n\t\t\t\tif (mediaRecorderRef.current) mediaRecorderRef.current.start(1000);\n\t\t\t\tsetMediaRecorderStopped(false);\n\t\t\t\tif (allowAudioToPlay.current) {\n\t\t\t\t\taudioTimeoutId = setTimeout(playAudio, 2000);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.log(\"error ----------\", err);\n\t\t}\n\t}, [webcamRef, supportedTypes, handleDataAvailable, playAudio, allowAudioToPlay, onScanStart]);\n\n\tuseEffect(() => {\n\t\tstartSendingVideoFramesRef.current = startSendingVideoFrames;\n\t}, [startSendingVideoFrames]);\n\n\tuseEffect(() => {\n\t\tif (shopDomain) {\n\t\t\tsetIsScanLocked(true);\n\t\t\thandleSocket();\n\t\t}\n\t\treturn () => {\n\t\t\t// Capture then reset id so next genuine mount can reconnect.\n\t\t\t// Do NOT reset isConnectingRef here — keeping it true blocks the\n\t\t\t// StrictMode second-mount call while the first connect is still awaiting.\n\t\t\tconst prevId = idRef.current;\n\t\t\tidRef.current = null;\n\t\t\tif (prevId) {\n\t\t\t\tswan.poseDetection.disconnect();\n\t\t\t\tposthog.capture(`${shopDomain}/pose_detection_disconnected`, {\n\t\t\t\t\tscanID: scanIDRef.current,\n\t\t\t\t\temail,\n\t\t\t\t\tid: prevId,\n\t\t\t\t\treason: \"unmount\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tevents.forEach((el: any) => {\n\t\t\t\tmediaRecorderRef?.current?.removeEventListener(\"dataavailable\", el);\n\t\t\t});\n\t\t};\n\t}, [shopDomain]);\n\tuseEffect(() => {\n\t\taudioSourceList.push(audioSource);\n\t}, [audioSource]);\n\n\tuseEffect(() => {\n\t\tsetAudioSourceList([]);\n\t}, [emptyAudioSource]);\n\tuseEffect(() => {\n\t\tconst bypassChecksToBackup = recordedChunks.length && recordedChunks.length > 0;\n\t\tif (!mediaRecorderStopped && bypassChecksToBackup && !recordingStarted && allowAudioToPlay.current && !pause) {\n\t\t\tif (mediaRecorderRef && mediaRecorderRef.current) {\n\t\t\t\tif (!recordingStarted) {\n\t\t\t\t\tmediaRecorderRef.current.stop();\n\t\t\t\t\t// mediaRecorderStopped will be set by the \"stop\" event listener\n\t\t\t\t\t// which fires AFTER all dataavailable events are done\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}, [mediaRecorderStopped, recordingStarted, recordedChunks, pause]);\n\n\tuseEffect(() => {\n\t\tconst bypassChecksToBackup = recordedChunks.length && recordedChunks.length > 0;\n\t\tif (mediaRecorderStopped && bypassChecksToBackup && !recordingStarted) {\n\t\t\tif (mediaRecorderRef && mediaRecorderRef.current && allowAudioToPlay.current && !pause) {\n\t\t\t\tif (!recordingStarted) {\n\t\t\t\t\tconst videoFile = new File(recordedChunks, `${scanID}.${recordingExtension}`, {\n\t\t\t\t\t\ttype: recordingMimeType,\n\t\t\t\t\t});\n\t\t\t\t\tonCaptureComplete?.();\n\t\t\t\t\tsetUploadLoading?.(true);\n\t\t\t\t\tsetScanFailsError(\"\");\n\t\t\t\t\t// Pass FPS captured at recording time\n\t\t\t\t\tuploadScanFile?.(videoFile, recordedFpsRef.current);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}, [mediaRecorderStopped, recordingStarted, recordedChunks, pause, mediaRecorderRef, allowAudioToPlay]);\n\tuseEffect(() => {\n\t\tstartSendingVideoFramesRef.current = startSendingVideoFrames;\n\t}, [startSendingVideoFrames]);\n\n\tuseEffect(() => {\n\t\tconst supported = getSupportedMediaRecorderTypes(preferredVideoTypes, videoTypes);\n\t\tsetSupportedTypes(supported);\n\t}, [preferredVideoTypes]);\n\tuseEffect(() => {\n\t\tresetScan();\n\t}, []);\n\n\treturn (\n\t\t<CameraScanChild\n\t\t\tresetDetector={resetDetector}\n\t\t\tresetScan={resetScan}\n\t\t\tloadingCam={loadingCam}\n\t\t\tonPause={handlePause}\n\t\t\tshowRestart={showRestart}\n\t\t\tpause={pause}\n\t\t\tonReScan={handleRestart}\n\t\t\trecordingStarted={recordingStarted}\n\t\t\tisScanning={isScanning}\n\t\t\tstartSendingVideoFrames={startSendingVideoFrames}\n\t\t\tfaceDone={faceDone}\n\t\t\tstopRecording={stopRecording}\n\t\t\tshowPause={showPause}\n\t\t\twebcamRef={webcamRef}\n\t\t\tonUserMedia={handleUserMedia}\n\t\t\tonUserMediaError={handleUserMediaError}\n\t\t\tvideoConstraints={currentVideoConstraints}\n\t\t\twebcamKey={webcamKey}\n\t\t/>\n\t);\n}\n\nexport default React.memo(ScanningComponent);\n","import { useCallback, useEffect, useRef } from \"react\";\n\ninterface UseWatchdogOptions {\n\t/** How long (ms) to wait without a `pet()` before `onTimeout` fires. */\n\ttimeoutMs: number;\n\t/** Invoked once when the watchdog expires (no `pet()` within `timeoutMs`). */\n\tonTimeout: () => void;\n}\n\nexport interface WatchdogControls {\n\t/** Arm (or re-arm) the countdown. Call when the watched activity begins. */\n\tstart: () => void;\n\t/** Reset the countdown. Call on every progress signal to keep it alive. */\n\tpet: () => void;\n\t/** Disarm the watchdog so no timeout fires until `start()`/`pet()` again. */\n\tstop: () => void;\n}\n\n/**\n * Generic watchdog timer. Fires `onTimeout` if `pet()` is not called within\n * `timeoutMs` of the last `start()`/`pet()`.\n *\n * Use it to detect a stalled pipeline — e.g. video frames streaming out to a\n * server but no responses coming back — and recover gracefully (show a retry,\n * disconnect, log) instead of leaving the UI frozen forever.\n *\n * The pending timer is cleared automatically on unmount. `onTimeout` is read\n * through a ref, so passing a fresh inline callback each render never restarts\n * an already-armed timer.\n */\nfunction useWatchdog({ timeoutMs, onTimeout }: UseWatchdogOptions): WatchdogControls {\n\tconst timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst onTimeoutRef = useRef(onTimeout);\n\n\tuseEffect(() => {\n\t\tonTimeoutRef.current = onTimeout;\n\t}, [onTimeout]);\n\n\tconst stop = useCallback(() => {\n\t\tif (timerRef.current) {\n\t\t\tclearTimeout(timerRef.current);\n\t\t\ttimerRef.current = null;\n\t\t}\n\t}, []);\n\n\tconst arm = useCallback(() => {\n\t\tif (timerRef.current) clearTimeout(timerRef.current);\n\t\ttimerRef.current = setTimeout(() => {\n\t\t\ttimerRef.current = null;\n\t\t\tonTimeoutRef.current();\n\t\t}, timeoutMs);\n\t}, [timeoutMs]);\n\n\t// Clear any pending timer when the consuming component unmounts.\n\tuseEffect(() => stop, [stop]);\n\n\treturn { start: arm, pet: arm, stop };\n}\n\nexport default useWatchdog;\n","/* eslint-disable no-use-before-define */\n\nimport { useEffect, useState, useRef } from \"react\";\nimport { IOSDeviceOrientationEvent } from \"../../customHooks/useGyroSensor\";\nimport LevelScreen from \"./LevelScreen\";\nimport ScanningComponent from \"./ScanningComponent\";\nimport { AngleDetectorProps } from \"../../types/interfaces\";\n\nexport default function AngleDetector({\n scanID,\n userDetails,\n setIsVideoUploaded,\n setScanFailsError,\n setScanStartTime,\n setScanUniqueKey,\n}: Omit<AngleDetectorProps, \"config\">) {\n const [angle, setAngle] = useState(90);\n const [calibrationOffset, setCalibrationOffset] = useState(0);\n const [countdown, setCountdown] = useState<number | null>(null);\n const [isScanning, setIsScanning] = useState(false);\n const [stabilityScore, setStabilityScore] = useState(0);\n const lastAnglesRef = useRef<number[]>([]);\n const [isScanLocked, setIsScanLocked] = useState(false);\n const calibratedAngle = angle - calibrationOffset;\n const isInTargetRange = calibratedAngle >= 80 && calibratedAngle <= 95;\n\n const resetDetector = () => {\n setAngle(90);\n setCalibrationOffset(0);\n setCountdown(null);\n setIsScanning(false);\n setStabilityScore(0);\n setIsScanLocked(false);\n lastAnglesRef.current = [];\n };\n\n useEffect(() => {\n if (typeof window !== \"undefined\" && window.DeviceOrientationEvent) {\n const handleOrientation = (event: DeviceOrientationEvent) => {\n if (event.beta !== null) {\n let newAngle = Math.abs(event.beta);\n if (event.gamma !== null) {\n newAngle *= Math.cos((event.gamma * Math.PI) / 180);\n }\n newAngle = Math.max(0, Math.min(180, newAngle));\n setAngle(newAngle);\n }\n };\n\n const requestPermission = async () => {\n const DeviceOrientation =\n DeviceOrientationEvent as unknown as IOSDeviceOrientationEvent;\n\n if (\n DeviceOrientation &&\n typeof DeviceOrientation.requestPermission === \"function\"\n ) {\n try {\n const permission = await DeviceOrientation.requestPermission();\n if (permission === \"granted\") {\n // wrappedHandler = timeout(handleOrientation, 2000);\n window.addEventListener(\"deviceorientation\", handleOrientation);\n }\n } catch (e) {\n console.error(\"Permission error\", e);\n }\n } else {\n // wrappedHandler = handleOrientation;\n window.addEventListener(\"deviceorientation\", handleOrientation);\n }\n\n return () => {\n if (handleOrientation) {\n window.removeEventListener(\"deviceorientation\", handleOrientation);\n }\n };\n };\n\n document.addEventListener(\"click\", requestPermission, { once: true });\n }\n }, []);\n\n useEffect(() => {\n lastAnglesRef.current = [\n ...lastAnglesRef.current.slice(-4),\n calibratedAngle,\n ];\n\n if (lastAnglesRef.current.length >= 5) {\n const variation =\n Math.max(...lastAnglesRef.current) - Math.min(...lastAnglesRef.current);\n if (isInTargetRange && variation < 2) {\n setStabilityScore((prev) => Math.min(100, prev + 10));\n } else {\n setStabilityScore((prev) => Math.max(0, prev - 20));\n }\n }\n\n if (!isInTargetRange && (countdown !== null || isScanning)) {\n resetCountdown();\n }\n }, [calibratedAngle, isInTargetRange, countdown, isScanning]);\n\n\tuseEffect(() => {\n\t\tif (stabilityScore >= 100 && countdown === null && !isScanning ) {\n\t\t\tstartCountdown();\n\t\t}\n\t\tif (stabilityScore < 50 && countdown !== null) {\n\t\t\tresetCountdown();\n\t\t}\n\t}, [stabilityScore, countdown, isScanning]);\n\n const startCountdown = () => {\n setCountdown(3);\n const timer = setInterval(() => {\n setCountdown((prev) => {\n if (prev === null || prev <= 1) {\n clearInterval(timer);\n setIsScanning(true);\n return null;\n }\n return prev - 1;\n });\n }, 1000);\n };\n\n const resetCountdown = () => {\n setCountdown(null);\n if (!isScanLocked) {\n setIsScanning(false);\n }\n };\n\n\n return (\n <LevelScreen\n angle={calibratedAngle}\n countdown={countdown}\n isScanning={isScanning}\n isInTargetRange={isInTargetRange}\n stabilityScore={stabilityScore}\n >\n {isScanning && (\n <ScanningComponent\n setIsScanLocked={setIsScanLocked}\n resetDetector={resetDetector}\n scanID={scanID}\n userDetails={userDetails}\n setIsVideoUploaded={setIsVideoUploaded}\n setScanFailsError={setScanFailsError}\n setScanStartTime={setScanStartTime}\n setScanUniqueKey={setScanUniqueKey}\n />\n )}\n </LevelScreen>\n );\n}\n","import { useEffect, useState, useRef, useCallback, Dispatch, SetStateAction } from \"react\";\nimport Webcam from \"react-webcam\";\nimport Modal from \"../Modal\";\nimport LoadingScreen from \"../LoadingScreen\";\nimport { checkCameraPermission } from \"../../utils/utils\";\nimport { videoConstraints } from \"../../utils/constants\";\n\n\nfunction CameraPermission({ setShowDrawer, config,loader }: { setShowDrawer?: Dispatch<SetStateAction<boolean>>; config?: any,loader?:string }) {\n\tconst [showDeniedModal, setShowDeniedModal] = useState({\n\t\tdisabled: false,\n\t\tmessage: \"\",\n\t});\n\tconst [loading, setLoading] = useState(true);\n\tconst webcamRef = useRef(null);\n\n\tconst handleCameraPermission = useCallback(async () => {\n\t\tconst permission = await checkCameraPermission();\n\t\tsetShowDeniedModal(permission);\n\t\tsetLoading(false);\n\t}, []);\n\n\tuseEffect(() => {\n\t\thandleCameraPermission();\n\t}, []);\n\n\tif (loading) {\n\t\treturn <LoadingScreen url={loader} loaderType=\"black\" />\n\t}\n\n\tif (showDeniedModal?.disabled) {\n\t\treturn <Modal config={config} message={showDeniedModal?.message} />;\n\t}\n\treturn (\n\t\t<Webcam\n\t\t\taudio={false}\n\t\t\tref={webcamRef}\n\t\t\tscreenshotQuality={1}\n\t\t\tvideoConstraints={videoConstraints}\n\t\t\tmirrored\n\t\t\tonUserMedia={() => setShowDrawer?.(true)}\n\t\t\tscreenshotFormat=\"image/jpeg\"\n\t\t\tstyle={{\n\t\t\t\tposition: \"relative\",\n\t\t\t\ttop: 0,\n\t\t\t\tbottom: 0,\n\t\t\t\tzIndex: 0,\n\t\t\t\twidth: \"100%\",\n\t\t\t\theight: \"100%\",\n\t\t\t\tobjectFit: \"cover\",\n\t\t\t}}\n\t\t/>\n\t);\n}\n\nexport default CameraPermission;\n","\nimport React, { useEffect, useRef } from \"react\";\nimport videojs from \"video.js\";\nimport type Player from \"video.js/dist/types/player\";\nimport { videoPoster } from \"../../utils/constants\";\n\nfunction VideoPlayer({ link, onReady, wrapperClassName = \"[&_video]:rounded-t-[20px] w-full h-full\" }: { link?: string; wrapperClassName?: string; onReady?: (args: Player) => void }) {\n\tconst videoReference = useRef<HTMLVideoElement | null>(null);\n\tconst playerReference = useRef<Player | null>(null);\n\n\tlet videoJsOptions: any = {\n\t\tautoplay: true,\n\t\tcontrols: false,\n\t\tresponsive: true,\n\t\tfluid: true,\n\t\tmuted: true,\n\t\tnavigationUI: \"hide\",\n\t\tpreload: \"metadata\",\n\t\tposter: videoPoster,\n\t};\n\n\tuseEffect(() => {\n\t\tif (!playerReference.current && link && videoReference?.current) {\n\t\t\tconst player = videojs(videoReference.current, {\n\t\t\t\t...videoJsOptions,\n\t\t\t});\n\t\t\tplayer.ready(() => {\n\t\t\t\tplayerReference.current = player;\n\n\t\t\t\tconst updatedOptions = {\n\t\t\t\t\t...videoJsOptions,\n\t\t\t\t\tsources: [{ src: link, type: \"application/x-mpegURL\" }],\n\t\t\t\t};\n\n\t\t\t\tplayer.autoplay(updatedOptions.autoplay);\n\t\t\t\tplayer.src(updatedOptions.sources);\n\n\t\t\t\tonReady?.(player);\n\t\t\t});\n\t\t}\n\t}, [link, videoReference]);\n\n\tuseEffect(() => {\n\t\tconst player = playerReference.current;\n\t\treturn () => {\n\t\t\tif (player && !player.isDisposed()) {\n\t\t\t\tplayer.dispose();\n\t\t\t\tplayerReference.current = null;\n\t\t\t}\n\t\t};\n\t}, [playerReference]);\n\n\treturn (\n\t\t<div className={wrapperClassName}>\n\t\t\t<video ref={videoReference} muted className=\"video-js\" playsInline onDrag={(e) => e.preventDefault()} />\n\t\t</div>\n\t);\n}\nexport default VideoPlayer;\n","import { useContext, useState } from \"react\";\nimport {Dialog} from \"@mui/material\";\nimport { LanguageContext } from \"../../utils/context/languageContext\";\nimport Header from \"../Header\";\nimport VideoPlayer from \"./VideoPlayer\";\nimport { ScanErrorMessageProps } from \"../../types/interfaces\";\nimport { generateUuid, handleErrorMessage } from \"../../utils/utils\";\nimport { GENDER, VIDEO_POSTER_GENDER_BASED } from \"../../utils/constants\";\nimport SpecificButton from \"../../atoms/specificButton/SpecificButton\";\nimport { LanguageKeys } from \"../../utils/languageKeys\";\nimport { X } from \"lucide-react\";\nimport { useConfig } from \"../../utils/context/configContext\";\n\nfunction ScanErrorMessage({ scanFailsError, onNext, gender, setScanUniqueKey, setIsVideoUploaded }: ScanErrorMessageProps) {\n\tconst { translate } = useContext(LanguageContext) || {};\n\tconst resolvedConfig = useConfig();\n\tconst [showModal, setShowModal] = useState(false);\n\n\tconst handleBtnClick = () => {\n\t\tif (onNext) {\n\t\t\tonNext?.();\n\t\t} else {\n\t\t\tsetScanUniqueKey?.(generateUuid());\n\t\t}\n\t\tsetIsVideoUploaded?.(false);\n\t};\n\n\tconst handleShowModal = () => {\n\t\tsetShowModal(!showModal);\n\t};\n\n\n\treturn (\n\t\t<>\n\t\t\t<div data-testid=\"scan-error-message\" className=\"flex common-ui-main flex-col h-full max-w-[28rem] mx-auto w-full rounded-t-[20px] overflow-y-auto\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t\t<div className=\"w-full max-w-[28rem] mx-auto pt-[1rem] px-[1rem]\">\n\t\t\t\t\t<Header noTitle />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t{scanFailsError && (\n\t\t\t\t\t\t<div className=\"px-[1rem]\">\n\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\tclassName=\"text-center\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tfontFamily: resolvedConfig?.style?.heading?.headingFontFamily || \"SeriouslyNostalgic Fn\",\n\t\t\t\t\t\t\t\t\tfontSize: resolvedConfig?.style?.heading?.headingFontSize || \"32px\",\n\t\t\t\t\t\t\t\t\tcolor: resolvedConfig?.style?.heading?.headingColor || \"#000\",\n\t\t\t\t\t\t\t\t\tfontWeight: resolvedConfig?.style?.heading?.headingFontWeight || \"normal\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{translate?.(LanguageKeys.issueWithScan)}\n\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tfontFamily: resolvedConfig?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n\t\t\t\t\t\t\t\t\tfontSize: resolvedConfig?.style?.base?.baseFontSize || \"16px\",\n\t\t\t\t\t\t\t\t\tcolor: resolvedConfig?.style?.base?.baseTextColor || \"#1E1E1E\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{translate?.(LanguageKeys.reason)}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tfontFamily: resolvedConfig?.style?.base?.baseFontFamily || \"Inter, sans-serif\",\n\t\t\t\t\t\t\t\t\tfontSize: resolvedConfig?.style?.base?.baseFontSize || \"16px\",\n\t\t\t\t\t\t\t\t\tcolor: resolvedConfig?.style?.base?.baseTextColor || \"#1E1E1E\",\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{handleErrorMessage(scanFailsError)}\n\t\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t\t<img className=\"my-[0.5rem] aspect-[2/1.4] w-full object-cover\" onClick={handleShowModal} src={VIDEO_POSTER_GENDER_BASED[gender]} alt=\"icon\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t{scanFailsError && (\n\t\t\t\t\t<div className=\"p-[1rem] flex gap-[0.5rem]\">\n\t\t\t\t\t\t<SpecificButton data-testid=\"scan-again-btn\" disabled={false} buttonText={translate?.(LanguageKeys.scanAgain)} className=\"!shadow-none\" buttonFunc={handleBtnClick} />\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{showModal && (\n\t\t\t\t<Dialog className=\"w-screen h-screen video-modal\" onClose={handleShowModal} open={showModal}>\n\t\t\t\t\t<div className=\"flex justifyEnd \">\n\t\t\t\t\t\t<span className=\"closeBtn\" onClick={handleShowModal}>\n\t\t\t\t\t\t\t<X className=\"absolute right-[8px] top-[8px] text-white z-[9]\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"aspect-video object-cover rounded-[20px] \">\n\t\t\t\t\t\t<VideoPlayer link={gender ? GENDER[gender].PRE_LINK : GENDER.male.PRE_LINK} wrapperClassName=\"w-screen h-screen fixed top-[0] left-[0]\" />\n\t\t\t\t\t</div>\n\t\t\t\t</Dialog>\n\t\t\t)}\n\t\t</>\n\t);\n}\n\nexport default ScanErrorMessage;\n","import { useCallback, useContext, useEffect, useState } from \"react\";\nimport {Box,Drawer} from \"@mui/material\";\nimport posthog from \"../utils/posthog\";\nimport Header from \"./Header\";\nimport CameraPermission from \"./bodyScan/CameraPermission\";\nimport { SignUpProps } from \"../types/interfaces\";\nimport { CLOTHING_BANNER_SCAN, CLOTHING_CUSTOM_FIT_SCAN, CLOTHING_CUSTOM_SCAN, CLOTHING_TRYON_CONSUMER_SCAN, maleMeasurementProgress, measurementProgress } from \"../utils/constants\";\nimport { GenderType } from \"../utils/enums\";\nimport SpecificButton from \"../atoms/specificButton/SpecificButton\";\nimport { LanguageContext } from \"../utils/context/languageContext\";\nimport { LanguageKeys } from \"../utils/languageKeys\";\n\nfunction SignUp({\n\tscanId,\n\tuserDetails,\n\tconfig,\n\tisVideoUploadedCorrect,\n\tisMeasurementAvailable,\n\tonComplete,\n\tisSuccess,\n\tonCustomScanSuccess,\n\tswan,\n}: SignUpProps) {\n\tconst { gender, shopDomain, heightInCm, deviceFocalLength, userName, email, scanType } = userDetails || {};\n\tconst isCustom = [CLOTHING_CUSTOM_SCAN, CLOTHING_BANNER_SCAN, CLOTHING_CUSTOM_FIT_SCAN].includes(scanType ?? \"\");\n\tconst [showDrawer, setShowDrawer] = useState(true);\n\tconst handlePostHogEvent = () => {\n\t\tposthog.capture(shopDomain??\"\", {\n\t\t\tscanID: scanId,\n\t\t\temail: email,\n\t\t\theight: heightInCm,\n\t\t\tfocalLength: deviceFocalLength,\n\t\t\tclothesFit: \"0\",\n\t\t\tgender: gender,\n\t\t});\n\t};\n\n\tconst handleSignUp = useCallback(async () => {\n\t\ttry {\n\t\t\tif (isCustom || scanType=== CLOTHING_TRYON_CONSUMER_SCAN) {\n\t\t\t\tawait swan?.auth.addUser({\n\t\t\t\t\tscanId,\n\t\t\t\t\temail,\n\t\t\t\t\tname: userName,\n\t\t\t\t\tgender,\n\t\t\t\t\theight: heightInCm,\n\t\t\t\t});\n\t\t\t\tonCustomScanSuccess?.();\n\t\t\t}\n\t\t\thandlePostHogEvent();\n\t\t} catch (error) {\n\t\t\tconsole.log(error);\n\t\t}\n\t}, [isCustom]);\n\n\tconst { translate } = useContext(LanguageContext) || {};\n\n\tuseEffect(() => {\n\t\tif (isVideoUploadedCorrect || isMeasurementAvailable) {\n\t\t\thandleSignUp();\n\t\t}\n\t}, [isVideoUploadedCorrect, isMeasurementAvailable]);\n\n\t// For custom scans the flow auto-redirects via onCustomScanSuccess once addUser\n\t// resolves, so the manual Next button must never show (otherwise a stray\n\t// \"Continue\" appears during the addUser network window). The drawer's progress\n\t// video + \"measurements being taken\" header already serves as the processing\n\t// state while the redirect is in flight.\n\tconst showNextButton = (isSuccess || isVideoUploadedCorrect || isMeasurementAvailable) && !isCustom;\n\n\treturn (\n\t\t<Box className=\"flex h-full w-full flex-col common-ui-main\">\n\t\t\t<div className=\"h-full w-full flex-col items-center justify-center flex\">\n\t\t\t\t<CameraPermission loader={config?.loader} setShowDrawer={setShowDrawer} />\n\t\t\t\t<Drawer\n\t\t\t\t\topen={showDrawer}\n\t\t\t\t\tonClose={(_, 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\tclassName=\"camera-drawer\"\n\t\t\t\t\tanchor=\"bottom\"\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"max-w-[28rem] mx-auto w-full h-full flex text-center flex-col justify-between items-center bg-primary rounded-t-[30px] p-[1rem]\"\n\t\t\t\t\t\tstyle={{ background: config?.style?.base?.backgroundColor }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"w-full h-full flex flex-col\">\n\t\t\t\t\t\t\t<Header title={translate?.(LanguageKeys.measurementsBeingTaken)} />\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-center flex-1\">\n\t\t\t\t\t\t\t\t<video preload=\"auto\" className=\"max-h-[calc(100vh-450px)] mx-auto w-full object-contain border-none\" muted loop autoPlay playsInline>\n\t\t\t\t\t\t\t\t\t<source src={gender === GenderType.Male ? maleMeasurementProgress : measurementProgress} type=\"video/mp4\" />\n\t\t\t\t\t\t\t\t</video>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{showNextButton && <SpecificButton className=\"!w-[180px] mx-auto\" buttonText={translate?.(LanguageKeys.next)} buttonFunc={()=>{onComplete?.()\n\t\t\t\t\t\t\t}} />}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</Drawer>\n\t\t\t</div>\n\t\t</Box>\n\t);\n}\nexport default SignUp;\n","\"use client\";\nimport { useContext } from \"react\";\nimport AngleDetector from \"./AngleDetector\";\nimport CameraPermission from \"./CameraPermission\";\nimport Modal from \"../Modal\";\nimport { Drawer } from \"@mui/material\";\nimport ScanErrorMessage from \"./ScanErrorMessage\";\nimport LoadingScreen from \"../LoadingScreen\";\nimport SignUp from \"../Signup\";\nimport { useConfig } from \"../../utils/context/configContext\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\n\nexport const BodyScanSteps = () => {\n\tconst {\n\t\tuserDetails,\n\t\tonRetry,\n\t\tisError,\n\t\tisSuccess,\n\t\tgender,\n\t\tscanUniqueKey,\n\t\tscanFailsError,\n\t\tsetScanUniqueKey,\n\t\tsetIsVideoUploaded,\n\t\tisMeasurementAvailable,\n\t\tisVideoUploadedCorrect,\n\t\tloading,\n\t\tshowDeniedModal,\n\t\tuploadLoading,\n\t\tsetScanFailsError,\n\t\tsetScanStartTime,\n\t\tresetScan,\n\t\tonCustomScanSuccess,\n\t\tswan,\n\t\tonComplete,\n\t} = useContext(ParamsContext);\n\tconst resolvedConfig = useConfig();\n\n\tif (isError) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"error\">\n\t\t\t\t<CameraPermission />\n\t\t\t\t<Drawer\n\t\t\t\t\tanchor=\"bottom\"\n\t\t\t\t\topen={true}\n\t\t\t\t\tclassName=\"camera-drawer\"\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>\n\t\t\t\t\t<ScanErrorMessage scanFailsError onNext={onRetry} gender={gender} />\n\t\t\t\t</Drawer>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (isSuccess) {\n\t\treturn <div data-scan-state=\"success\"><SignUp isSuccess onComplete={onComplete} /></div>;\n\t}\n\n\tif (loading) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"loading\" className=\"flex top-0 !mt-0 left-0 z-[999] absolute justify-center items-center w-full h-full\" style={{ background: resolvedConfig?.style?.base?.backgroundColor }}>\n\t\t\t\t<LoadingScreen url={resolvedConfig?.loader} loaderType=\"black\" />\n\t\t\t</div>\n\t\t);\n\t}\n\tif (showDeniedModal.disabled) {\n\t\treturn <div data-scan-state=\"camera-denied\"><Modal /></div>;\n\t}\n\n\tif (!uploadLoading && !scanFailsError && !isMeasurementAvailable && !isVideoUploadedCorrect) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"scanning\">\n\t\t\t<AngleDetector\n\t\t\t\tscanID={scanUniqueKey}\n\t\t\t\tuserDetails={userDetails}\n\t\t\t\tsetIsVideoUploaded={setIsVideoUploaded}\n\t\t\t\tsetScanFailsError={setScanFailsError}\n\t\t\t\tsetScanStartTime={setScanStartTime}\n\t\t\t\tsetScanUniqueKey={setScanUniqueKey}\n\t\t\t/>\n\t\t\t</div>\n\t\t);\n\t}\n\tif ((uploadLoading || isMeasurementAvailable || isVideoUploadedCorrect) && !scanFailsError) {\n\t\treturn (\n\t\t\t<div data-scan-state=\"processing\">\n\t\t\t<SignUp\n\t\t\t\tscanId={scanUniqueKey}\n\t\t\t\tisMeasurementAvailable={isMeasurementAvailable}\n\t\t\t\tuserDetails={userDetails}\n\t\t\t\tisVideoUploadedCorrect={isVideoUploadedCorrect}\n\t\t\t\tonCustomScanSuccess={onCustomScanSuccess}\n\t\t\t\tswan={swan}\n\t\t\t\tonComplete={onComplete}\n\t\t\t\tconfig={resolvedConfig}\n\t\t\t/>\n\t\t\t</div>\n\t\t);\n\t}\n\treturn (\n\t\t<div data-scan-state=\"error-retry\">\n\t\t\t<CameraPermission />\n\t\t\t<Drawer\n\t\t\t\tanchor=\"bottom\"\n\t\t\t\topen\n\t\t\t\tclassName=\"camera-drawer\"\n\t\t\t\tonClose={(event, reason) => {\n\t\t\t\t\tif (reason === \"backdropClick\") {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<ScanErrorMessage\n\t\t\t\t\tscanFailsError={scanFailsError}\n\t\t\t\t\tonNext={() => {\n\t\t\t\t\t\tonRetry?.();\n\t\t\t\t\t\tresetScan();\n\t\t\t\t\t}}\n\t\t\t\t\tsetScanUniqueKey={setScanUniqueKey}\n\t\t\t\t\tgender={gender}\n\t\t\t\t\tsetIsVideoUploaded={setIsVideoUploaded}\n\t\t\t\t/>\n\t\t\t</Drawer>\n\t\t</div>\n\t);\n};\n","\"use client\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport useGyroSensor from \"../../customHooks/useGyroSensor\";\nimport usePosthogPageview from \"../../customHooks/usePosthogPageview\";\nimport {\n\tcheckCameraPermission,\n\tcreateObjectMetadataArray,\n\tgenerateUuid,\n\tgetCurrentTimeInSeconds,\n\tgetScanType,\n\tgetVideoFPS,\n\thandleErrorMessage,\n\thandleScanTimeCapture,\n\thandleWebSocketCapture,\n} from \"../../utils/utils\";\nimport LanguageContextProvider from \"../../utils/context/languageContext\";\nimport { ConfigProvider } from \"../../utils/context/configContext\";\nimport { getSwanService } from \"../../utils/service/swanService\";\nimport ParamsContext from \"../../utils/context/paramsContext\";\nimport MediaContextProvider from \"../../utils/context/mediaContext\";\nimport { BodyScanProps, UserDetails } from \"../../types/interfaces\";\nimport { BodyScanSteps } from \"./BodyScanSteps\";\n\nconst clothesFit = \"0\";\n\nconst BodyScan: React.FC<BodyScanProps> = (props) => {\n\tconst {\n\t\tconfig,\n\t\tonRetry,\n\t\tonScanStart,\n\t\tonCaptureComplete,\n\t\tonUploadStart,\n\t\tonUploadEnd,\n\t\tonMeasurementSocketStart,\n\t\tonMeasurementSocketClose,\n\t\tonIntermediateScanSuccess,\n\t\tonScanError,\n\t\tonScanSuccess,\n\t\tonCustomScanSuccess,\n\t\tonComplete,\n\t\tisError,\n\t\tisSuccess,\n\t} = props;\n\tconst userDetails: UserDetails = {\n\t\temail: \"\",\n\t\tshopDomain: \"\",\n\t\tgender: \"male\",\n\t\theightInCm: 0,\n\t\tscanType: \"\",\n\t\tdeviceFocalLength: 0,\n\t\tdeviceModelName: \"\",\n\t\tsourceTag: \"\",\n\t\tcallbackUrl: null,\n\t\t...(props.userDetails ?? {}),\n\t};\n\tconst token = props.token ?? \"\";\n\tconst { gender, scanType, shopDomain, heightInCm, email, deviceFocalLength, deviceModelName, callbackUrl, sourceTag } = userDetails;\n\tconst swan = useMemo(() => getSwanService(token), [token]);\n\tconst [uploadLoading, setUploadLoading] = useState(false);\n\tconst [isVideoUploadedCorrect, setIsVideoUploadedCorrect] = useState(false);\n\tconst [isMeasurementAvailable, setIsMeasurementAvailable] = useState(false);\n\tconst [showDeniedModal, setShowDeniedModal] = useState({\n\t\tdisabled: false,\n\t\tmessage: \"\",\n\t});\n\tconst [scanFailsError, setScanFailsError] = useState(\"\");\n\tconst [isVideoUploaded, setIsVideoUploaded] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [startGyro, setStartGyro] = useState(false);\n\tconst [scanUniqueKey, setScanUniqueKey] = useState(\"\");\n\tconst [scanStartTime, setScanStartTime] = useState<number | null>(getCurrentTimeInSeconds());\n\tconst { gyroData } = useGyroSensor(startGyro);\n\tusePosthogPageview();\n\n\tconst resetScan = useCallback(() => {\n\t\tsetScanUniqueKey(generateUuid());\n\t\tsetScanFailsError(\"\");\n\t\tsetUploadLoading(false);\n\t\tsetIsVideoUploaded(false);\n\t}, []);\n\n\tconst onError = (data: any) => {\n\t\tonScanError?.({ ...data, message: handleErrorMessage(data) });\n\t\tclearScanStartTimeAndScanId();\n\t\tsetIsMeasurementAvailable(false);\n\t\tsetScanFailsError(data);\n\t\tsetIsVideoUploadedCorrect(false);\n\t\thandleScanTimeCapture({\n\t\t\teventName: `${shopDomain}/measurement_failed/fit-view`,\n\t\t\tscanID: scanUniqueKey,\n\t\t\tstatus: \"failed\",\n\t\t\temail,\n\t\t\tmessage: handleErrorMessage(data),\n\t\t});\n\t\tif (scanStartTime)\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/scan_completion_time`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tcompletionTime: getCurrentTimeInSeconds() - scanStartTime,\n\t\t\t\temail,\n\t\t\t});\n\t};\n\n\tconst clearScanStartTimeAndScanId = () => {\n\t\tsetScanStartTime(null);\n\t\tsetScanUniqueKey(\"\");\n\t};\n\n\tconst onSuccess = useCallback(\n\t\tasync (data: any) => {\n\t\t\tif (data && data?.scanStatus === \"success\" && data?.resultType === \"intermediate\" && data?.code === 200) {\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/measurement_success/intermediate`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tsetIsVideoUploadedCorrect(true);\n\t\t\t\tonIntermediateScanSuccess?.(data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonScanSuccess?.(data);\n\t\t\tsetIsVideoUploadedCorrect(true);\n\t\t\tclearScanStartTimeAndScanId();\n\t\t\tconst scanIdToSet = scanUniqueKey;\n\t\t\tsetIsMeasurementAvailable(true);\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/measurement_success/fit-view`,\n\t\t\t\tscanID: scanIdToSet,\n\t\t\t\tstatus: \"success\",\n\t\t\t\temail,\n\t\t\t});\n\t\t\tif (scanStartTime)\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/scan_completion_time`,\n\t\t\t\t\tscanID: scanIdToSet,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\tcompletionTime: getCurrentTimeInSeconds() - scanStartTime,\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t},\n\t\t[scanType, shopDomain, scanUniqueKey],\n\t);\n\n\tconst handleMeasurementRecommendation = (scanId?: string) => {\n\t\tsetIsMeasurementAvailable(false);\n\t\tsetScanFailsError(\"\");\n\t\tsetIsVideoUploadedCorrect(false);\n\t\tswan.measurement.handleMeasurementSocket({\n\t\t\tscanId: scanId || scanUniqueKey,\n\t\t\tonPreopen: () => {\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"pre_open\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonOpen: () => {\n\t\t\t\tonMeasurementSocketStart?.();\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"open\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonClose: () => {\n\t\t\t\tonMeasurementSocketClose?.();\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"close\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tonError(err);\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"error\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t},\n\t\t\tonSuccess: (data) => {\n\t\t\t\thandleWebSocketCapture({\n\t\t\t\t\teventName: `${shopDomain}/webSocket`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tconnection: \"success\",\n\t\t\t\t\ttype: \"measurement_recommendation\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tonSuccess(data);\n\t\t\t},\n\t\t});\n\t};\n\n\tconst uploadScanFile = useCallback(\n\t\tasync (file: File, recordedFps?: number | null) => {\n\t\t\tconst fileSizeInMB = Number((file.size / (1024 * 1024)).toFixed(2));\n\t\t\t// Use FPS captured at recording time, fallback to calculation if not available\n\t\t\tconst videoFps = recordedFps !== undefined && recordedFps !== null ? recordedFps : await getVideoFPS(file);\n\t\t\tconst arrayMetaData = createObjectMetadataArray({\n\t\t\t\tgender,\n\t\t\t\tfocal_length: `${deviceFocalLength}`,\n\t\t\t\theight: `${heightInCm}`,\n\t\t\t\tcustomer_store_url: shopDomain,\n\t\t\t\tclothes_fit: clothesFit,\n\t\t\t\tscan_type: getScanType(scanType),\n\t\t\t\tcallback_url: callbackUrl,\n\t\t\t\tsource_tag: sourceTag,\n\t\t\t});\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/body_scan_meta_data`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\temail,\n\t\t\t\tdata: JSON.stringify([...arrayMetaData, { fileSizeInMB, video_fps: videoFps || 0 }]),\n\t\t\t});\n\t\t\tonUploadStart?.();\n\t\t\ttry {\n\t\t\t\tawait swan.fileUpload.uploadFileFrontend({\n\t\t\t\t\tfile,\n\t\t\t\t\tarrayMetaData,\n\t\t\t\t\tscanId: scanUniqueKey,\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tawait swan.fileUpload.setDeviceInfo({\n\t\t\t\t\tmodel: deviceModelName,\n\t\t\t\t\tdetection: \"manual\",\n\t\t\t\t\tgyro: gyroData,\n\t\t\t\t\tscanId: scanUniqueKey,\n\t\t\t\t});\n\t\t\t\tconsole.log(\"video successfully uploaded\");\n\t\t\t\tsetIsVideoUploaded(false);\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/scan_success`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\temail,\n\t\t\t\t\tdata: JSON.stringify(arrayMetaData),\n\t\t\t\t});\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: \"scan finished\",\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\temail,\n\t\t\t\t});\n\t\t\t\tsetScanStartTime(getCurrentTimeInSeconds());\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tsetIsVideoUploaded(true);\n\t\t\t\t}, 3000);\n\t\t\t} catch (error:any) {\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: \"scan finished\",\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\temail,\n\t\t\t\t\tmessage: handleErrorMessage(error),\n\t\t\t\t});\n\t\t\t\thandleScanTimeCapture({\n\t\t\t\t\teventName: `${shopDomain}/scan_failed`,\n\t\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\temail,\n\t\t\t\t\tmessage: handleErrorMessage(error),\n\t\t\t\t\tdata: JSON.stringify(arrayMetaData),\n\t\t\t\t});\n\t\t\t\tsetScanFailsError(handleErrorMessage(error));\n\t\t\t\tsetIsVideoUploaded(false);\n\t\t\t\tonScanError?.({...error, message: handleErrorMessage(error) });\n\t\t\t\tclearScanStartTimeAndScanId();\n\t\t\t} finally {\n\t\t\t\tsetStartGyro(false);\n\t\t\t\tonUploadEnd?.();\n\t\t\t}\n\t\t},\n\t\t[scanUniqueKey, gyroData, onUploadStart, onUploadEnd],\n\t);\n\t// const checkMeasurementStatus = useCallback(\n\t// \tasync (scanId: string) => {\n\t// \t\ttry {\n\t// \t\t\tconst res = await swan.measurement.getMeasurementResult(scanId);\n\t// \t\t\tconst isMeasured = res?.data?.isMeasured;\n\t// \t\t\tconst tempScanStartTime = scanStartTime || getCurrentTimeInSeconds();\n\t// \t\t\tconst currentTimeInSeconds = getCurrentTimeInSeconds();\n\t// \t\t\tconst fiveMinutesInSeconds = 5 * 60;\n\t// \t\t\tsetLoading(false);\n\t// \t\t\tif (isMeasured === false) {\n\t// \t\t\t\tclearScanStartTimeAndScanId();\n\t// \t\t\t\tsetScanUniqueKey(generateUuid());\n\t// \t\t\t\treturn;\n\t// \t\t\t}\n\t// \t\t\tsetUploadLoading(true);\n\t// \t\t\tif (isMeasured === true) {\n\t// \t\t\t\tawait onSuccess(null);\n\t// \t\t\t} else if (isMeasured === null && tempScanStartTime > currentTimeInSeconds + fiveMinutesInSeconds) {\n\t// \t\t\t\tonError(res?.data?.error);\n\t// \t\t\t} else {\n\t// \t\t\t\thandleMeasurementRecommendation(scanId);\n\t// \t\t\t}\n\t// \t\t} catch (error) {\n\t// \t\t\tconsole.log(error);\n\t// \t\t\tclearScanStartTimeAndScanId();\n\t// \t\t\tsetScanUniqueKey(generateUuid());\n\t// \t\t\tsetUploadLoading(false);\n\t// \t\t}\n\t// \t},\n\t// \t[onError, onSuccess, setScanUniqueKey],\n\t// );\n\n\tconst handleCameraPermission = useCallback(async () => {\n\t\tif (+heightInCm < 152.4 || +heightInCm > 213.36) {\n\t\t\tonScanError?.({\n\t\t\t\tmessage: \"Height must be between 152.4cm (5ft) and 213.36cm (7ft)\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconst permission = await checkCameraPermission();\n\t\tif (permission.disabled) {\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/camera_activation`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\temail,\n\t\t\t});\n\t\t\tsetLoading(false);\n\t\t} else {\n\t\t\t// const scanId = scanUniqueKey;\n\t\t\t// if (scanId) {\n\t\t\t// \tcheckMeasurementStatus(scanId);\n\t\t\t// } else {\n\t\t\t// \tsetLoading(false);\n\t\t\t// }\n\t\t\tsetLoading(false);\n\t\t\thandleScanTimeCapture({\n\t\t\t\teventName: `${shopDomain}/camera_activation`,\n\t\t\t\tscanID: scanUniqueKey,\n\t\t\t\tstatus: \"success\",\n\t\t\t\temail,\n\t\t\t});\n\t\t}\n\t\tsetShowDeniedModal(permission);\n\t\tsetScanFailsError(\"\");\n\t\tsetIsVideoUploaded(false);\n\t}, [email, scanUniqueKey, setScanUniqueKey, shopDomain]);\n\n\tuseEffect(() => {\n\t\tif (isError || isSuccess) return;\n\t\tif (shopDomain) {\n\t\t\thandleCameraPermission();\n\t\t}\n\t}, [shopDomain, heightInCm, isError, isSuccess]);\n\n\tuseEffect(() => {\n\t\tif (isError || isSuccess) return;\n\t\tif (isVideoUploaded && shopDomain && scanUniqueKey) {\n\t\t\thandleMeasurementRecommendation();\n\t\t}\n\t}, [isVideoUploaded, shopDomain, scanUniqueKey, isError, isSuccess]);\n\treturn (\n\t\t<div data-testid=\"body-scan-root\">\n\t\t<LanguageContextProvider>\n\t\t\t<ConfigProvider config={config}>\n\t\t\t\t<MediaContextProvider>\n\t\t\t\t\t<ParamsContext.Provider\n\t\t\t\t\t\tvalue={{\n\t\t\t\t\t\t\tuserDetails,\n\t\t\t\t\t\t\tonRetry,\n\t\t\t\t\t\t\tonScanError,\n\t\t\t\t\t\t\tisError,\n\t\t\t\t\t\t\tisSuccess,\n\t\t\t\t\t\t\tonScanSuccess,\n\t\t\t\t\t\t\tgender,\n\t\t\t\t\t\t\tscanUniqueKey,\n\t\t\t\t\t\t\tscanFailsError,\n\t\t\t\t\t\t\tsetScanUniqueKey,\n\t\t\t\t\t\t\tsetIsVideoUploaded,\n\t\t\t\t\t\t\tisMeasurementAvailable,\n\t\t\t\t\t\t\tisVideoUploadedCorrect,\n\t\t\t\t\t\t\tloading,\n\t\t\t\t\t\t\tshowDeniedModal,\n\t\t\t\t\t\t\tuploadLoading,\n\t\t\t\t\t\t\tsetStartGyro,\n\t\t\t\t\t\t\tuploadScanFile,\n\t\t\t\t\t\t\tsetUploadLoading,\n\t\t\t\t\t\t\tresetScan,\n\t\t\t\t\t\t\tsetScanFailsError,\n\t\t\t\t\t\t\tsetScanStartTime,\n\t\t\t\t\t\t\tonScanStart,\n\t\t\t\t\t\t\tonCaptureComplete,\n\t\t\t\t\t\t\tonCustomScanSuccess,\n\t\t\t\t\t\t\tonComplete,\n\t\t\t\t\t\t\tswan,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<BodyScanSteps />\n\t\t\t\t\t</ParamsContext.Provider>\n\t\t\t\t</MediaContextProvider>\n\t\t\t</ConfigProvider>\n\t\t</LanguageContextProvider>\n\t\t</div>\n\t);\n};\n\n// SSR mount-gate: the camera/scan tree is browser-only (touches window/document\n// during render and uses the webcam). This thin wrapper renders a neutral\n// placeholder on the server and the first client render, then mounts the real\n// component only after the effect fires in the browser. This lets consumers\n// import BodyScan into a server-rendered app (Next/Remix) without a crash or a\n// dynamic(ssr:false) wrapper. None of BodyScanInner's hooks run on the server.\nconst BodyScanClientGate: React.FC<BodyScanProps> = (props) => {\n\tconst [mounted, setMounted] = useState(false);\n\tuseEffect(() => {\n\t\tsetMounted(true);\n\t}, []);\n\n\tif (!mounted) {\n\t\treturn <div data-testid=\"body-scan-root\" />;\n\t}\n\n\treturn <BodyScan {...props} />;\n};\n\nexport { BodyScanClientGate as BodyScan };\n","import { useCallback, useEffect, useState } from \"react\";\n\ninterface GyroDataItem {\n alpha?: string;\n beta?: string;\n gamma?: string;\n timestamp?: string;\n}\n\nexport interface IOSDeviceOrientationEvent extends DeviceOrientationEvent {\n requestPermission?: () => Promise<PermissionState>;\n}\n\nfunction useGyroSensor(startGyro: boolean) {\n const [gyroData, setGyroData] = useState<GyroDataItem[]>([]);\n const [permissionGranted, setPermissionGranted] = useState(false);\n\n const handleOrientation = useCallback((event: DeviceOrientationEvent) => {\n try {\n const { alpha, beta, gamma } = event;\n setGyroData((prev: GyroDataItem[]) => [\n ...prev,\n {\n alpha: alpha?.toString() || undefined,\n beta: beta?.toString() || undefined,\n gamma: gamma?.toString() || undefined,\n timestamp: new Date().toISOString(),\n },\n ]);\n } catch (error) {\n console.log(error);\n }\n }, []);\n\n const requestPermission = useCallback(async () => {\n const DeviceOrientation =\n DeviceOrientationEvent as unknown as IOSDeviceOrientationEvent;\n\n if (\n typeof DeviceOrientation !== \"undefined\" &&\n typeof DeviceOrientation.requestPermission === \"function\"\n ) {\n try {\n const response = await DeviceOrientation.requestPermission();\n if (response === \"granted\") {\n setPermissionGranted(true);\n } else {\n console.warn(\"Device orientation permission denied.\");\n }\n } catch (error) {\n console.error(\"Error requesting device orientation permission:\", error);\n }\n } else {\n // Permission not required on non-iOS devices\n setPermissionGranted(true);\n }\n }, []);\n\n useEffect(() => {\n if (startGyro && permissionGranted) {\n window.addEventListener(\"deviceorientation\", handleOrientation);\n } else {\n setGyroData([]);\n }\n return () => {\n window.removeEventListener(\"deviceorientation\", handleOrientation);\n };\n }, [startGyro, permissionGranted, handleOrientation]);\n\n useEffect(() => {\n if (startGyro) {\n requestPermission();\n }\n }, [startGyro, requestPermission]);\n\n return { gyroData };\n}\n\nexport default useGyroSensor;\n"],"names":["MEDIA_TYPES","MediaContext","createContext","undefined","MediaContextProvider","children","size","setSize","useState","window","innerWidth","innerHeight","clearInputs","setClearInputs","updateSize","useEffect","useLayoutEffect","addEventListener","removeEventListener","media","mediaDetails","useMemo","_jsx","Provider","value","SwitchIcon","React","memo","_jsxs","width","height","viewBox","fill","xmlns","d","stroke","strokeWidth","strokeLinecap","strokeLinejoin","LevelScreen","angle","countdown","isScanning","isInTargetRange","stabilityScore","translate","useContext","LanguageContext","config","useConfig","getProximityToTarget","useCallback","Math","max","min","backgroundColor","style","angleDetector","successAngleBackground","proximity","successAngleLowBackground","round","handelTextColour","isLight","successAngleTextLightColor","successAngleTextDarkColor","getTextColor","handelColourCode","getColorCode","cd","fixVh","document","documentElement","setProperty","className","Header","noTitle","border","fontFamily","base","baseFontFamily","color","cn","transform","transition","LanguageKeys","startLevelCheck","leavePhone","placePhoneUpright","CameraScanChild","resetScan","loadingCam","onUserMedia","onPause","pause","onReScan","recordingStarted","startSendingVideoFrames","showPause","webcamRef","onUserMediaError","resetDetector","videoConstraints","webcamKey","faceDone","onClick","X","Webcam","audio","ref","screenshotQuality","mirrored","screenshotFormat","position","top","bottom","zIndex","objectFit","SpecificButton","prefix","pauseIcon","buttonText","buttonFunc","btnSecondary","restart","startScan","disabled","id","crossOrigin","preload","src","voiceOverAssetsPath","globalLoadingPromise","globalDetector","globalPoseLib","audioTimeoutId","lastVideoPlayed","ScanningComponent","setIsScanLocked","scanID","setIsVideoUploaded","setScanFailsError","setScanStartTime","setScanUniqueKey","userDetails","email","shopDomain","gender","heightInCm","useRef","mediaRecorderRef","recordedFpsRef","recordedChunks","setRecordedChunks","setLoadingCam","setRecordingStarted","setFaceDone","mediaRecorderStopped","setMediaRecorderStopped","setIsScanning","captureVideoTimeOutHandle","audioSource","setAudioSource","audioSourceList","setAudioSourceList","emptyAudioSource","setEmptyAudioSource","startAgain","setStartAgain","showRestart","setRestart","setPause","events","setEvents","setShowPause","allowAudioToPlay","poseDetector","consecutiveFronts","setConsecutiveFronts","spinPhase","setSpinPhase","isLoaded","setIsLoaded","spinPhaseRef","consecutiveFrontsRef","mounted","async","loadDependenciesGlobal","navigator","console","log","poseLib","tfjsCore","tfjsBackend","Promise","all","import","setBackend","ready","modelConfig","runtime","modelType","solutionPath","createDetector","SupportedModels","BlazePose","mediapipeError","warn","error","isFrontFrame","body","length","filter","p","current","then","success","s","callback","video","readyState","poses","estimatePoses","flipHorizontal","keypoints","slice","map","kp","x","y","score","prev","err","retryLoading","useTensorFlow","firstScan","poseStoppedRef","counterRef","consecutiveFalseRef","framesSentRef","framesSkippedRef","poseListenerRegistered","isConnectingRef","brilliantPlayedRef","postPoseProcessRef","startSendingVideoFramesRef","idRef","supportedTypes","setSupportedTypes","setWebcamKey","currentVideoConstraints","setCurrentVideoConstraints","videoConstraintsExact","setStartGyro","uploadScanFile","setUploadLoading","swan","onScanStart","onCaptureComplete","ParamsContext","scanIDRef","eventsRef","poseWatchdog","timeoutMs","onTimeout","timerRef","onTimeoutRef","stop","clearTimeout","arm","setTimeout","start","pet","useWatchdog","posthog","capture","framesSent","generateUuid","speechService","stopAudio","forEach","el","stream","handleUserMedia","handleUserMediaError","videoConstraintsFallback","handlePause","poseDetection","disconnect","reason","preferredVideoTypes","getPreferredMediaRecorderTypes","recordingMimeType","getRecordingMimeType","recordingExtension","getRecordingExtension","stopRecording","playAudio","handleSocket","connect","String","handleSpinDataAvailable","data","concat","handleScanTimeCapture","eventName","message","lastAudio","handleStartCaptureClick","videoTrack","getVideoTracks","settings","getSettings","frameRate","mediaRecorderOptions","videoBitsPerSecond","mimeType","MediaRecorder","e","postPoseProcess","handleDataAvailable","connected","poseStatus","audioEmpty","status","sid","sidMatchesConnection","counter","consecutiveFalse","type","querySelector","paused","audioName","no_of_times_skipped","skipCount","frame","getScreenshot","videoEmit","image","scanId","framesSkipped","getCurrentTimeInSeconds","prevId","push","bypassChecksToBackup","videoFile","File","supported","getSupportedMediaRecorderTypes","videoTypes","rescanSupportCaptureEvent","AngleDetector","setAngle","calibrationOffset","setCalibrationOffset","setCountdown","setStabilityScore","lastAnglesRef","isScanLocked","calibratedAngle","DeviceOrientationEvent","handleOrientation","event","beta","newAngle","abs","gamma","cos","PI","requestPermission","DeviceOrientation","once","variation","resetCountdown","startCountdown","timer","setInterval","clearInterval","CameraPermission","setShowDrawer","loader","showDeniedModal","setShowDeniedModal","loading","setLoading","handleCameraPermission","permission","checkCameraPermission","LoadingScreen","url","loaderType","Modal","VideoPlayer","link","onReady","wrapperClassName","videoReference","playerReference","videoJsOptions","autoplay","controls","responsive","fluid","muted","navigationUI","poster","videoPoster","player","videojs","updatedOptions","sources","isDisposed","dispose","playsInline","onDrag","preventDefault","ScanErrorMessage","scanFailsError","onNext","resolvedConfig","showModal","setShowModal","handleShowModal","_Fragment","background","heading","headingFontFamily","fontSize","headingFontSize","headingColor","fontWeight","headingFontWeight","issueWithScan","baseFontSize","baseTextColor","handleErrorMessage","VIDEO_POSTER_GENDER_BASED","alt","scanAgain","Dialog","onClose","open","GENDER","PRE_LINK","male","SignUp","isVideoUploadedCorrect","isMeasurementAvailable","onComplete","isSuccess","onCustomScanSuccess","deviceFocalLength","userName","scanType","isCustom","CLOTHING_CUSTOM_SCAN","CLOTHING_BANNER_SCAN","CLOTHING_CUSTOM_FIT_SCAN","includes","showDrawer","handleSignUp","CLOTHING_TRYON_CONSUMER_SCAN","auth","addUser","name","focalLength","clothesFit","showNextButton","Box","Drawer","_","anchor","title","measurementsBeingTaken","loop","autoPlay","GenderType","Male","maleMeasurementProgress","measurementProgress","next","BodyScanSteps","onRetry","isError","scanUniqueKey","uploadLoading","BodyScan","props","onUploadStart","onUploadEnd","onMeasurementSocketStart","onMeasurementSocketClose","onIntermediateScanSuccess","onScanError","onScanSuccess","deviceModelName","sourceTag","callbackUrl","token","getSwanService","setIsVideoUploadedCorrect","setIsMeasurementAvailable","isVideoUploaded","startGyro","scanStartTime","gyroData","setGyroData","permissionGranted","setPermissionGranted","alpha","toString","timestamp","Date","toISOString","useGyroSensor","usePosthogPageview","clearScanStartTimeAndScanId","onSuccess","scanStatus","resultType","code","scanIdToSet","completionTime","handleMeasurementRecommendation","measurement","handleMeasurementSocket","onPreopen","handleWebSocketCapture","connection","onOpen","onError","file","recordedFps","fileSizeInMB","Number","toFixed","videoFps","getVideoFPS","arrayMetaData","createObjectMetadataArray","focal_length","customer_store_url","clothes_fit","scan_type","getScanType","callback_url","source_tag","JSON","stringify","video_fps","fileUpload","uploadFileFrontend","setDeviceInfo","model","detection","gyro","LanguageContextProvider","ConfigProvider","BodyScanClientGate","setMounted"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
"use strict";var e=require("react/jsx-runtime"),t=require("react"),s=require("./Modal-Bo7k1S9H.js"),n=require("clsx"),a=require("react-webcam"),r=require("lucide-react"),o=require("@mui/material"),c=require("video.js");const i="DESKTOP",l="TAB",u="MOBILE",d=t.createContext(void 0);function m({children:s}){const[n,a]=t.useState([window?.innerWidth,window?.innerHeight]),[r,o]=t.useState(!1),c=()=>{a([window?.innerWidth,window?.innerHeight])};t.useEffect(()=>{c()},[]),t.useLayoutEffect(()=>(window.addEventListener("resize",c),()=>window.removeEventListener("resize",c)),[c]);let m=i;n[0]>768&&n[0]<1024&&(m=l),n[0]<768&&(m=u);const p=t.useMemo(()=>({size:n,setSize:a,clearInputs:r,setClearInputs:o,media:m}),[n,r,m]);return e.jsx(d.Provider,{value:p,children:s})}var p=t.memo(function({size:t=16}){return e.jsxs("svg",{width:t,height:t,viewBox:"0 0 25 25",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[e.jsx("path",{d:"M22.6968 14.6968C22.6968 16.8185 21.8539 18.8533 20.3536 20.3536C18.8533 21.8539 16.8185 22.6968 14.6968 22.6968",stroke:"white",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("path",{d:"M18.6968 11.6968V10.6968C18.6968 10.1663 18.4861 9.65764 18.111 9.28256C17.7359 8.90749 17.2272 8.69678 16.6968 8.69678C16.1663 8.69678 15.6576 8.90749 15.2826 9.28256C14.9075 9.65764 14.6968 10.1663 14.6968 10.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("path",{d:"M14.6968 10.6968V9.69678C14.6968 9.16634 14.4861 8.65764 14.111 8.28256C13.7359 7.90749 13.2272 7.69678 12.6968 7.69678C12.1663 7.69678 11.6576 7.90749 11.2826 8.28256C10.9075 8.65764 10.6968 9.16634 10.6968 9.69678V10.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("path",{d:"M10.6968 10.1968V4.69678C10.6968 4.16634 10.4861 3.65764 10.111 3.28256C9.73592 2.90749 9.22721 2.69678 8.69678 2.69678C8.16634 2.69678 7.65764 2.90749 7.28256 3.28256C6.90749 3.65764 6.69678 4.16634 6.69678 4.69678V14.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),e.jsx("path",{d:"M18.6969 11.6968C18.6969 11.1663 18.9076 10.6576 19.2827 10.2826C19.6577 9.90749 20.1664 9.69678 20.6969 9.69678C21.2273 9.69678 21.736 9.90749 22.1111 10.2826C22.4862 10.6576 22.6969 11.1663 22.6969 11.6968V14.6968C22.6969 16.8185 21.854 18.8533 20.3537 20.3536C18.8534 21.8539 16.8186 22.6968 14.6969 22.6968H12.6969C9.89688 22.6968 8.19688 21.8368 6.70688 20.3568L3.10688 16.7568C2.76282 16.3757 2.57847 15.8769 2.592 15.3637C2.60554 14.8505 2.81593 14.3621 3.1796 13.9997C3.54327 13.6373 4.03238 13.4287 4.54565 13.417C5.05892 13.4053 5.55704 13.5914 5.93688 13.9368L7.69688 15.6968",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})});var f=t.memo(function({angle:a,countdown:r,isScanning:o,isInTargetRange:c,stabilityScore:i,children:l}){const{translate:u}=t.useContext(s.LanguageContext)||{},d=s.useConfig(),m=t.useCallback(()=>a<80?Math.max(0,Math.min(100,10*(a-60))):a>95?Math.max(0,Math.min(100,10*(105-a))):100,[a]),f=t.useMemo(()=>{if(o)return d?.style?.angleDetector?.successAngleBackground||"#4f46e5";const e=m();if(0===e)return d?.style?.angleDetector?.successAngleLowBackground||"#ffffff";if(100===e)return d?.style?.angleDetector?.successAngleBackground||"#4f46e5";return`rgb(${Math.round(255-e/100*116)}, ${Math.round(255-e/100*163)}, ${Math.round(255-e/100*9)})`},[o,m]),h=t.useCallback((e,t)=>t>70?e?`text-[${d?.style?.angleDetector?.successAngleTextLightColor}]/70`:`text-[${d?.style?.angleDetector?.successAngleTextLightColor}]`:e?`text-[${d?.style?.angleDetector?.successAngleTextLightColor}]/40`:`text-[${d?.style?.angleDetector?.successAngleTextDarkColor}]/70`,[]),g=t.useCallback((e=!1)=>{const t=m();return o?`text-[${d?.style?.angleDetector?.successAngleTextLightColor}]`:h(e,t)},[o,m]),x=t.useCallback((e,t)=>t>70||e?d?.style?.angleDetector?.successAngleTextLightColor:d?.style?.angleDetector?.successAngleTextDarkColor,[]),S=t.useCallback((e=!1)=>{const t=m();return o?d?.style?.angleDetector?.successAngleTextLightColor:x(e,t)},[o,m]),v=r;function y(){document.documentElement.style.setProperty("--real-vh",window.innerHeight+"px")}return y(),window.addEventListener("resize",y),e.jsxs("div",{className:"flex common-ui-main w-screen flex-col items-center h-[var(--real-vh)] overflow-hidden touch-none justify-center transition-all duration-300 max-w-[28rem] mx-auto",style:{backgroundColor:f},"data-testid":"level-screen","data-angle-state":o?"locked":c?"target":"adjusting",children:[e.jsx("div",{className:"flex justify-start fixed top-[.5rem] max-w-[28rem] mx-auto w-full px-[1rem]",children:e.jsx("div",{className:"flex justify-start ",children:e.jsx(s.Header,{noTitle:!0})})}),null!==v?e.jsx("div",{className:"relative flex h-[6rem] w-[6rem] items-center justify-center rounded-[9999px] bg-[#fff]/20 transition-all duration-300",style:{border:`2px solid ${S()}`},children:e.jsx("div",{className:`text-[3rem] font-bold text-[${S()}]`,style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif",color:S()},children:v})}):o?e.jsxs("div",{className:"relative flex flex-col items-center justify-center",children:[e.jsx("div",{className:"relative flex h-16 w-16 items-center justify-center rounded-[9999px] border-2 bg-[#fff]/30 border-[#fff]",children:e.jsx("div",{className:"h-4 w-4 rounded-[9999px] animate-pulse bg-[#fff]/80"})}),l]}):e.jsxs("div",{className:n("relative flex h-[4rem] w-[4rem] items-center justify-center rounded-[9999px] ",c?"bg-[#fff]/20 border-[#fff]":"bg-[#fff]/10 border-[#fff]"),style:{transform:`translateY(${3*(90-a)}px)`,transition:"transform 0.2s ease-out",border:`2px solid ${S()}`},children:[e.jsx("div",{className:`h-[1rem] w-[1rem] rounded-[9999px] bg-[${S()}]/80`,style:{backgroundColor:`${S()}B3`}}),e.jsxs("div",{className:n("mt-[.5rem] text-center w-[180px] flex-col flex items-center absolute top-[60px]",g()),style:{color:S()},children:[e.jsx(p,{size:30}),e.jsxs("p",{style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif"},children:[" ",u?.(s.LanguageKeys.startLevelCheck),e.jsx("br",{})]})]})]}),null!==v&&e.jsx("div",{className:"absolute bottom-[8rem] text-center",children:e.jsx("div",{className:n("text-sm font-medium px-4 py-1.5 bg-black/20 rounded-[9999px] mt-8",g()),style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif",color:S()},children:u?.(s.LanguageKeys.leavePhone)})}),c&&null===v&&!o&&e.jsx("div",{className:"absolute bottom-[8rem] w-[12rem]",children:e.jsx("div",{className:`h-[.375rem] w-full bg-[${S()}]/20 rounded-[9999px] overflow-hidden`,style:{backgroundColor:`${S()}33`},children:e.jsx("div",{className:`h-full bg-[${S()}]/70 transition-all duration-300 rounded-[9999px]`,style:{width:`${a}%`,backgroundColor:`${S()}B3`}})})}),null===v&&!o&&e.jsx("div",{className:"absolute bottom-[5rem] text-center",children:e.jsxs("div",{className:n("text-[1.5rem] font-light",g()),style:{color:S()},children:[Math.round(a),"°"]})}),null===v&&!o&&e.jsx("div",{className:"absolute bottom-[2rem] text-center max-w-[20rem] mx-auto",children:e.jsx("div",{className:n("text-[.75rem] opacity-50",g()),style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif",color:S()},children:u?.(s.LanguageKeys.placePhoneUpright)})})]})});function h({resetScan:n,loadingCam:o,onUserMedia:c,onPause:i,pause:l,onReScan:m,recordingStarted:p,startSendingVideoFrames:f,showPause:h,webcamRef:g,onUserMediaError:x,resetDetector:S,videoConstraints:v,webcamKey:y,faceDone:C,isScanning:b}){const{media:w}=t.useContext(d)||{},{translate:j}=t.useContext(s.LanguageContext)||{};return e.jsxs("div",{className:"App w-screen h-[100vh] relative common-ui-main","data-testid":"camera-scan","data-scan-phase":C?"spinning":p?"pose-detecting":b?"starting":"ready",children:[e.jsx("span",{onClick:()=>{n(),S()},className:"fixed right-[20px] top-[20px] z-[999]",children:e.jsx(r.X,{className:"text-[#fff]"})}),e.jsx("div",{className:"w-full h-full overflow-hidden ",children:w===u&&e.jsx(a,{audio:!1,ref:g,screenshotQuality:1,videoConstraints:v,mirrored:!0,screenshotFormat:"image/jpeg",onUserMedia:c,onUserMediaError:x,style:{position:"fixed",top:0,bottom:0,zIndex:0,width:"100%",height:"100%",objectFit:"cover"}},y)}),e.jsxs("div",{className:"fixed bottom-[30px] w-full z-[999] flex flex-col gap-4 items-center justify-center",children:[h&&e.jsx(s.SpecificButton,{className:"!w-[180px] !h-[40px] !py-[0] mx-auto ",prefix:s.pauseIcon,buttonText:j?.(s.LanguageKeys.pause),buttonFunc:i,btnSecondary:!0}),l?e.jsx("div",{children:e.jsx(s.SpecificButton,{className:"!w-[180px] !h-[40px] !py-[0] mx-auto",buttonText:j?.(s.LanguageKeys.restart),buttonFunc:m,btnSecondary:!0})}):p?null:e.jsx(s.SpecificButton,{"data-testid":"start-scan-btn",className:"!w-[180px] !h-[40px] !py-[0] mx-auto !bg-[#ffffff] !text-[#000000] "+(o?"!opacity-50":""),buttonText:j?.(s.LanguageKeys.startScan),buttonFunc:f,disabled:o})]}),e.jsx("audio",{id:"audioElement",crossOrigin:"anonymous",preload:"auto",style:{position:"absolute",zIndex:-99999},src:`${s.voiceOverAssetsPath}scanAudioInstructions/silence.mp3`})]})}let g=null,x=null,S=null;let v,y=null;var C=t.memo(function({setIsScanLocked:n,resetDetector:a,scanID:r,setIsVideoUploaded:o,setScanFailsError:c,setScanStartTime:i,setScanUniqueKey:l,userDetails:u}){const{email:d,shopDomain:m,gender:p,heightInCm:f}=u,C=t.useRef(null),b=t.useRef(null),w=t.useRef(null),[j,k]=t.useState([]),[E,N]=t.useState(!0),[D,T]=t.useState(!1),[I,_]=t.useState(!1),[L,M]=t.useState(!0),[F,P]=t.useState(!1),R=t.useRef(null),[$,A]=t.useState(""),[U,O]=t.useState([]),[z,K]=t.useState(""),[q,B]=t.useState(!1),[V,W]=t.useState(!1),[H,G]=t.useState(!1),[J,Q]=t.useState([]),[X,Y]=t.useState(!1),Z=t.useRef(!0),{poseDetector:ee}=function(){const[e,s]=t.useState(0),[n,a]=t.useState(0),[r,o]=t.useState(!1),c=t.useRef(0),i=t.useRef(0),l=t.useRef(!0);async function u(){if("undefined"==typeof window||"undefined"==typeof navigator)return!1;try{console.log("Starting TensorFlow preload...");const[e,t,s]=await Promise.all([Promise.resolve().then(function(){return require("./pose-detection.esm-DFB9u8DH.js")}),import("@tensorflow/tfjs-core"),import("@tensorflow/tfjs-backend-webgl")]);S=e,await t.setBackend("webgl"),await t.ready(),console.log("TensorFlow backend ready (WebGL)");const n={runtime:"mediapipe",modelType:"full",solutionPath:"https://cdn.jsdelivr.net/npm/@mediapipe/pose"};try{x=await S.createDetector(S.SupportedModels.BlazePose,n),console.log("MediaPipe detector created successfully")}catch(e){console.warn("MediaPipe failed, falling back to TFJS runtime:",e),x=await S.createDetector(S.SupportedModels.BlazePose,{runtime:"tfjs",modelType:"full"}),console.log("TFJS detector created successfully")}return!0}catch(e){return console.error("Failed to load TensorFlow dependencies:",e),g=null,!1}}function d(e){return!(!e||0===e.length)&&22===e.filter(e=>e[2]>.7).length}return t.useEffect(()=>{if(l.current=!0,!x)return g||(g=u()),g.then(e=>{e&&l.current&&o(!0)}),()=>{l.current=!1};o(!0)},[]),t.useEffect(()=>{i.current=e,e>6&&c.current<2&&(a(e=>e+1),s(0))},[e]),t.useEffect(()=>{c.current=n},[n]),{poseDetector:async(e,t)=>{if(!x||!l.current||!t?.current?.video)return;const n=t.current.video;if(!(n.readyState<2))try{const t=await x.estimatePoses(n,{flipHorizontal:!1});if(!t||!t.length)return;const a=t[0].keypoints.slice(11).map(e=>[e.x>0&&e.x<720?e.x:-1,e.y>0&&e.y<1280?e.y:-1,e.score??0]);0!==c.current||d(a)||s(e=>e+1),1===c.current&&d(a)&&s(e=>e+1),2===c.current&&e()}catch(e){console.error("Pose detection error:",e)}},isLoaded:r,spinPhase:n,resetDetector:()=>{c.current=0,i.current=0,s(0),a(0)},retryLoading:async()=>{x||g||(g=u(),await g&&l.current&&o(!0))}}}(),te=t.useRef(!0),se=t.useRef(!1),ne=t.useRef(0),ae=t.useRef(0),re=t.useRef(0),oe=t.useRef(0),ce=t.useRef(!1),ie=t.useRef(!1),le=t.useRef(!1),ue=t.useRef(()=>{}),de=t.useRef(()=>{}),me=t.useRef(null),[pe,fe]=t.useState([]),[he,ge]=t.useState(0),[xe,Se]=t.useState(s.videoConstraintsExact),{setStartGyro:ve,uploadScanFile:ye,setUploadLoading:Ce,swan:be,onScanStart:we,onCaptureComplete:je}=t.useContext(s.ParamsContext),ke=t.useRef(r);t.useEffect(()=>{ke.current=r},[r]);const Ee=t.useRef(J);t.useEffect(()=>{Ee.current=J},[J]);const Ne=function({timeoutMs:e,onTimeout:s}){const n=t.useRef(null),a=t.useRef(s);t.useEffect(()=>{a.current=s},[s]);const r=t.useCallback(()=>{n.current&&(clearTimeout(n.current),n.current=null)},[]),o=t.useCallback(()=>{n.current&&clearTimeout(n.current),n.current=setTimeout(()=>{n.current=null,a.current()},e)},[e]);return t.useEffect(()=>r,[r]),{start:o,pet:o,stop:r}}({timeoutMs:12e3,onTimeout:t.useCallback(()=>{s.posthog.capture(`${m}/pose_feedback_timeout`,{scanID:ke.current,email:d,framesSent:re.current,id:me.current})},[m,d])}),De=()=>{se.current=!1,Ne.stop(),clearTimeout(v),R.current&&clearTimeout(R.current),l(s.generateUuid()),c(""),Ce?.(!1),k([]),w.current=null,N(!0),T(!1),_(!1),M(!0),P(!1),R.current=null,A(""),O([]),K(""),B(!q),ve(!1),s.speechService.stopAudio(),W(!1),G(!1),ne.current=0,ae.current=0,re.current=0,oe.current=0,ce.current=!1,le.current=!1,null!==b.current&&b.current.stop(),J.forEach(e=>{b.current&&b.current.removeEventListener("dataavailable",e)}),Q([]),Z.current=!0,Y(!1),o(!1),C.current?.stream&&N(!1)},Te=t.useCallback(()=>{setTimeout(()=>{N(!1)},1e3)},[]),Ie=t.useCallback(e=>{if(console.log("camera error",e),xe===s.videoConstraintsExact)return Se(s.videoConstraintsFallback),void ge(e=>e+1);N(!1)},[xe]),_e=t.useCallback(()=>{Z.current=!1,Ne.stop(),G(!0),Y(!1),R.current&&clearTimeout(R.current),b.current&&b.current.pause(),s.speechService.stopAudio(),be.poseDetection.disconnect(),s.posthog.capture(`${m}/pose_detection_disconnected`,{scanID:r,email:d,id:me.current,reason:"pause"}),ne.current=0,ae.current=0,ce.current=!1,le.current=!1,b.current&&b.current.stop(),J.forEach(e=>{b.current&&b.current.removeEventListener("dataavailable",e)}),Q([]),clearTimeout(v)},[b,J,s.speechService,Z]),Le=t.useMemo(()=>s.getPreferredMediaRecorderTypes(),[]),Me=s.getRecordingMimeType(pe),Fe=s.getRecordingExtension(Me),Pe=t.useCallback(async()=>{Z.current=!0,await s.speechService.playAudio(`${s.voiceOverAssetsPath}SpotOn.mp3`),G(!1),Y(!1),R.current&&clearTimeout(R.current),T(!1)},[R,s.speechService]),Re=t.useCallback(async()=>{if(!ie.current&&!me.current){ie.current=!0;try{me.current=await be.poseDetection.connect(),s.posthog.capture(`${m}/pose_detection_connected`,{scanID:ke.current,email:d,id:me.current})}catch(e){console.log(e,"while connecting websocket"),s.posthog.capture(`${m}/pose_detection_connect_failed`,{scanID:r,email:d,error:String(e)})}finally{ie.current=!1}}},[r,m,d]),$e=t.useCallback(({data:e})=>{e&&e.size>0&&Z.current&&(k(t=>t.concat(e)),!se.current&&C.current&&(se.current=!0,ee(()=>{Pe(),s.handleScanTimeCapture({eventName:`${m}/tensorFlow`,scanID:ke.current,email:d,message:"recording stopped by tensorflow "})},C)))},[Pe,s.handleScanTimeCapture,m,d,C]),Ae=t.useCallback(async()=>{const e=U[U.length-1];e&&Z.current&&(await s.speechService.playAudio(s.voiceOverAssetsPath+e),Z.current&&(v=setTimeout(Ae,2e3)))},[U,Z]),Ue=t.useCallback(()=>{b&&b.current&&b.current.stop(),T(!0);try{if(C&&C.current&&C.current.stream){const e=C.current.stream.getVideoTracks()[0];if(e){const t=e.getSettings();w.current=t.frameRate||null}const t={videoBitsPerSecond:15e5};pe[0]&&(t.mimeType=pe[0]),b.current=new MediaRecorder(C.current.stream,t),b.current.addEventListener("dataavailable",$e),b.current.addEventListener("stop",()=>{M(!0)}),Q([...J,$e]),b.current.start(1e3),M(!1)}}catch(e){console.log("error while using media recorder",e)}},[C,pe,$e,J]),Oe=t.useCallback(async()=>{R.current&&clearTimeout(R.current),Ue(),Z.current&&(R.current=setTimeout(async()=>{Z.current&&(await s.speechService.playAudio(`${s.voiceOverAssetsPath}SpotOn.mp3`),T(!1))},15e3)),_(!0),Z.current&&await s.speechService.playAudio(`${s.voiceOverAssetsPath}Spin.mp3`)},[Ue,Z,s.speechService]);t.useEffect(()=>{ue.current=Oe},[Oe]);const ze=t.useCallback(({data:e})=>{const t=be.poseDetection.connected();if(e.size>0&&t){ce.current||(ce.current=!0,be.poseDetection.poseStatus(async e=>{if(Ne.pet(),s.posthog.capture(`${m}/pose_instruction_received`,{scanID:ke.current,email:d,audio:e?.audio??"",audioEmpty:!(e?.audio&&e.audio.length>0),status:e?.status,sid:e?.sid,sidMatchesConnection:e?.sid===me.current,counter:ne.current,consecutiveFalse:ae.current,type:"body_scan_instruction"}),e&&e.audio&&e.audio.length>0){const t=document.querySelector("#audioElement");!0===e.status&&e.sid===me.current?(ae.current=0,ne.current<2?(ne.current+=1,1!==ne.current||le.current?2===ne.current&&t?.paused&&(clearTimeout(v),s.speechService.playAudio(s.voiceOverAssetsPath+e.audio)):(le.current=!0,clearTimeout(v),s.speechService.stopAudio(),await s.speechService.playAudio(s.voiceOverAssetsPath+e.audio))):(Ne.stop(),K(e.audio),clearTimeout(v),be.poseDetection.disconnect(),s.posthog.capture(`${m}/pose_detection_disconnected`,{scanID:r,email:d,id:me.current,reason:"pose_confirmed"}),ce.current=!1,setTimeout(()=>ue.current(),1e3))):(ae.current+=1,ae.current>=3&&(ne.current=0,le.current=!1),!t?.paused||y&&y?.audioName===e.audio?y?.audioName===e.audio&&t?.paused&&(y&&y.no_of_times_skipped>=y.skipCount?(y.no_of_times_skipped=0,s.speechService.playAudio(s.voiceOverAssetsPath+e.audio)):y&&(y.no_of_times_skipped+=1)):(y={skipCount:2,no_of_times_skipped:0,audioName:e.audio},s.speechService.playAudio(s.voiceOverAssetsPath+e.audio)))}}));const e=C?.current?C.current.getScreenshot():null;e?(re.current+=1,1===re.current&&Ne.start(),be.poseDetection.videoEmit({image:e,scanId:ke.current}),1!==re.current&&re.current%5!=0||s.posthog.capture(`${m}/pose_frame_sent`,{scanID:ke.current,email:d,sid:me.current,framesSent:re.current})):(oe.current+=1,1!==oe.current&&oe.current%5!=0||s.posthog.capture(`${m}/pose_frame_skipped_null`,{scanID:ke.current,email:d,framesSkipped:oe.current}))}},[C,r,be,s.speechService,m,d]),Ke=t.useCallback(async()=>{P(!0),ve(!0),we?.(),te.current&&(te.current=!1,s.handleScanTimeCapture({eventName:"scan started",scanID:ke.current,status:"success",email:d})),i(s.getCurrentTimeInSeconds()),N(!0),Z.current&&await s.speechService.playAudio(`${s.voiceOverAssetsPath}StartScan.mp3`),Z.current&&await s.speechService.playAudio(`${s.voiceOverAssetsPath}LiftArmsAndHoldAtHip.mp3`),Z.current&&(P(!1),T(!0),W(!0),Y(!0),Te());try{if(C&&C.current&&C.current.stream&&Z.current){const e=C.current.stream.getVideoTracks()[0];if(e){const t=e.getSettings();w.current=t.frameRate||null}const t={videoBitsPerSecond:15e5};pe[0]&&(t.mimeType=pe[0]),Z.current&&(b.current=new MediaRecorder(C.current.stream,t)),Z.current&&b.current&&b.current.addEventListener("dataavailable",ze),Q([...Ee.current,ze]),b.current&&b.current.start(1e3),M(!1),Z.current&&(v=setTimeout(Ae,2e3))}}catch(e){console.log("error ----------",e)}},[C,pe,ze,Ae,Z,we]);return t.useEffect(()=>{de.current=Ke},[Ke]),t.useEffect(()=>(m&&(n(!0),Re()),()=>{const e=me.current;me.current=null,e&&(be.poseDetection.disconnect(),s.posthog.capture(`${m}/pose_detection_disconnected`,{scanID:ke.current,email:d,id:e,reason:"unmount"})),J.forEach(e=>{b?.current?.removeEventListener("dataavailable",e)})}),[m]),t.useEffect(()=>{U.push($)},[$]),t.useEffect(()=>{O([])},[z]),t.useEffect(()=>{const e=j.length&&j.length>0;L||!e||D||!Z.current||H||b&&b.current&&(D||b.current.stop())},[L,D,j,H]),t.useEffect(()=>{const e=j.length&&j.length>0;if(L&&e&&!D&&b&&b.current&&Z.current&&!H&&!D){const e=new File(j,`${r}.${Fe}`,{type:Me});je?.(),Ce?.(!0),c(""),ye?.(e,w.current)}},[L,D,j,H,b,Z]),t.useEffect(()=>{de.current=Ke},[Ke]),t.useEffect(()=>{const e=s.getSupportedMediaRecorderTypes(Le,s.videoTypes);fe(e)},[Le]),t.useEffect(()=>{De()},[]),e.jsx(h,{resetDetector:a,resetScan:De,loadingCam:E,onPause:_e,showRestart:V,pause:H,onReScan:async()=>{s.rescanSupportCaptureEvent({eventName:`${m}/rescan`,email:d,scanID:ke.current,height:f,gender:p,status:!1}),be.poseDetection.disconnect(),De(),await Re()},recordingStarted:D,isScanning:F,startSendingVideoFrames:Ke,faceDone:I,stopRecording:Pe,showPause:X,webcamRef:C,onUserMedia:Te,onUserMediaError:Ie,videoConstraints:xe,webcamKey:he})});function b({scanID:s,userDetails:n,setIsVideoUploaded:a,setScanFailsError:r,setScanStartTime:o,setScanUniqueKey:c}){const[i,l]=t.useState(90),[u,d]=t.useState(0),[m,p]=t.useState(null),[h,g]=t.useState(!1),[x,S]=t.useState(0),v=t.useRef([]),[y,b]=t.useState(!1),w=i-u,j=w>=80&&w<=95;t.useEffect(()=>{if("undefined"!=typeof window&&window.DeviceOrientationEvent){const e=e=>{if(null!==e.beta){let t=Math.abs(e.beta);null!==e.gamma&&(t*=Math.cos(e.gamma*Math.PI/180)),t=Math.max(0,Math.min(180,t)),l(t)}},t=async()=>{const t=DeviceOrientationEvent;if(t&&"function"==typeof t.requestPermission)try{"granted"===await t.requestPermission()&&window.addEventListener("deviceorientation",e)}catch(e){console.error("Permission error",e)}else window.addEventListener("deviceorientation",e);return()=>{e&&window.removeEventListener("deviceorientation",e)}};document.addEventListener("click",t,{once:!0})}},[]),t.useEffect(()=>{if(v.current=[...v.current.slice(-4),w],v.current.length>=5){const e=Math.max(...v.current)-Math.min(...v.current);S(j&&e<2?e=>Math.min(100,e+10):e=>Math.max(0,e-20))}j||null===m&&!h||E()},[w,j,m,h]),t.useEffect(()=>{x>=100&&null===m&&!h&&k(),x<50&&null!==m&&E()},[x,m,h]);const k=()=>{p(3);const e=setInterval(()=>{p(t=>null===t||t<=1?(clearInterval(e),g(!0),null):t-1)},1e3)},E=()=>{p(null),y||g(!1)};return e.jsx(f,{angle:w,countdown:m,isScanning:h,isInTargetRange:j,stabilityScore:x,children:h&&e.jsx(C,{setIsScanLocked:b,resetDetector:()=>{l(90),d(0),p(null),g(!1),S(0),b(!1),v.current=[]},scanID:s,userDetails:n,setIsVideoUploaded:a,setScanFailsError:r,setScanStartTime:o,setScanUniqueKey:c})})}function w({setShowDrawer:n,config:r,loader:o}){const[c,i]=t.useState({disabled:!1,message:""}),[l,u]=t.useState(!0),d=t.useRef(null),m=t.useCallback(async()=>{const e=await s.checkCameraPermission();i(e),u(!1)},[]);return t.useEffect(()=>{m()},[]),l?e.jsx(s.LoadingScreen,{url:o,loaderType:"black"}):c?.disabled?e.jsx(s.Modal,{config:r,message:c?.message}):e.jsx(a,{audio:!1,ref:d,screenshotQuality:1,videoConstraints:s.videoConstraints,mirrored:!0,onUserMedia:()=>n?.(!0),screenshotFormat:"image/jpeg",style:{position:"relative",top:0,bottom:0,zIndex:0,width:"100%",height:"100%",objectFit:"cover"}})}function j({link:n,onReady:a,wrapperClassName:r="[&_video]:rounded-t-[20px] w-full h-full"}){const o=t.useRef(null),i=t.useRef(null);let l={autoplay:!0,controls:!1,responsive:!0,fluid:!0,muted:!0,navigationUI:"hide",preload:"metadata",poster:s.videoPoster};return t.useEffect(()=>{if(!i.current&&n&&o?.current){const e=c(o.current,{...l});e.ready(()=>{i.current=e;const t={...l,sources:[{src:n,type:"application/x-mpegURL"}]};e.autoplay(t.autoplay),e.src(t.sources),a?.(e)})}},[n,o]),t.useEffect(()=>{const e=i.current;return()=>{e&&!e.isDisposed()&&(e.dispose(),i.current=null)}},[i]),e.jsx("div",{className:r,children:e.jsx("video",{ref:o,muted:!0,className:"video-js",playsInline:!0,onDrag:e=>e.preventDefault()})})}function k({scanFailsError:n,onNext:a,gender:c,setScanUniqueKey:i,setIsVideoUploaded:l}){const{translate:u}=t.useContext(s.LanguageContext)||{},d=s.useConfig(),[m,p]=t.useState(!1),f=()=>{p(!m)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{"data-testid":"scan-error-message",className:"flex common-ui-main flex-col h-full max-w-[28rem] mx-auto w-full rounded-t-[20px] overflow-y-auto",style:{background:d?.style?.base?.backgroundColor},children:[e.jsx("div",{className:"w-full max-w-[28rem] mx-auto pt-[1rem] px-[1rem]",children:e.jsx(s.Header,{noTitle:!0})}),e.jsx("div",{className:"flex-1",children:n&&e.jsxs("div",{className:"px-[1rem]",children:[e.jsx("h2",{className:"text-center",style:{fontFamily:d?.style?.heading?.headingFontFamily||"SeriouslyNostalgic Fn",fontSize:d?.style?.heading?.headingFontSize||"32px",color:d?.style?.heading?.headingColor||"#000",fontWeight:d?.style?.heading?.headingFontWeight||"normal"},children:u?.(s.LanguageKeys.issueWithScan)}),e.jsx("p",{style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif",fontSize:d?.style?.base?.baseFontSize||"16px",color:d?.style?.base?.baseTextColor||"#1E1E1E"},children:u?.(s.LanguageKeys.reason)}),e.jsx("p",{style:{fontFamily:d?.style?.base?.baseFontFamily||"Inter, sans-serif",fontSize:d?.style?.base?.baseFontSize||"16px",color:d?.style?.base?.baseTextColor||"#1E1E1E"},children:s.handleErrorMessage(n)}),e.jsx("img",{className:"my-[0.5rem] aspect-[2/1.4] w-full object-cover",onClick:f,src:s.VIDEO_POSTER_GENDER_BASED[c],alt:"icon"})]})}),n&&e.jsx("div",{className:"p-[1rem] flex gap-[0.5rem]",children:e.jsx(s.SpecificButton,{"data-testid":"scan-again-btn",disabled:!1,buttonText:u?.(s.LanguageKeys.scanAgain),className:"!shadow-none",buttonFunc:()=>{a?a?.():i?.(s.generateUuid()),l?.(!1)}})})]}),m&&e.jsxs(o.Dialog,{className:"w-screen h-screen video-modal",onClose:f,open:m,children:[e.jsx("div",{className:"flex justifyEnd ",children:e.jsx("span",{className:"closeBtn",onClick:f,children:e.jsx(r.X,{className:"absolute right-[8px] top-[8px] text-white z-[9]"})})}),e.jsx("div",{className:"aspect-video object-cover rounded-[20px] ",children:e.jsx(j,{link:c?s.GENDER[c].PRE_LINK:s.GENDER.male.PRE_LINK,wrapperClassName:"w-screen h-screen fixed top-[0] left-[0]"})})]})]})}function E({scanId:n,userDetails:a,config:r,isVideoUploadedCorrect:c,isMeasurementAvailable:i,onComplete:l,isSuccess:u,onCustomScanSuccess:d,swan:m}){const{gender:p,shopDomain:f,heightInCm:h,deviceFocalLength:g,userName:x,email:S,scanType:v}=a||{},y=[s.CLOTHING_CUSTOM_SCAN,s.CLOTHING_BANNER_SCAN,s.CLOTHING_CUSTOM_FIT_SCAN].includes(v??""),[C,b]=t.useState(!0),j=t.useCallback(async()=>{try{(y||v===s.CLOTHING_TRYON_CONSUMER_SCAN)&&(await(m?.auth.addUser({scanId:n,email:S,name:x,gender:p,height:h})),d?.()),s.posthog.capture(f??"",{scanID:n,email:S,height:h,focalLength:g,clothesFit:"0",gender:p})}catch(e){console.log(e)}},[y]),{translate:k}=t.useContext(s.LanguageContext)||{};t.useEffect(()=>{(c||i)&&j()},[c,i]);const E=(u||c||i)&&!y;return e.jsx(o.Box,{className:"flex h-full w-full flex-col common-ui-main",children:e.jsxs("div",{className:"h-full w-full flex-col items-center justify-center flex",children:[e.jsx(w,{loader:r?.loader,setShowDrawer:b}),e.jsx(o.Drawer,{open:C,onClose:(e,t)=>{},className:"camera-drawer",anchor:"bottom",children:e.jsx("div",{className:"max-w-[28rem] mx-auto w-full h-full flex text-center flex-col justify-between items-center bg-primary rounded-t-[30px] p-[1rem]",style:{background:r?.style?.base?.backgroundColor},children:e.jsxs("div",{className:"w-full h-full flex flex-col",children:[e.jsx(s.Header,{title:k?.(s.LanguageKeys.measurementsBeingTaken)}),e.jsx("div",{className:"flex items-center justify-center flex-1",children:e.jsx("video",{preload:"auto",className:"max-h-[calc(100vh-450px)] mx-auto w-full object-contain border-none",muted:!0,loop:!0,autoPlay:!0,playsInline:!0,children:e.jsx("source",{src:p===s.GenderType.Male?s.maleMeasurementProgress:s.measurementProgress,type:"video/mp4"})})}),E&&e.jsx(s.SpecificButton,{className:"!w-[180px] mx-auto",buttonText:k?.(s.LanguageKeys.next),buttonFunc:()=>{l?.()}})]})})})]})})}const N=()=>{const{userDetails:n,onRetry:a,isError:r,isSuccess:c,gender:i,scanUniqueKey:l,scanFailsError:u,setScanUniqueKey:d,setIsVideoUploaded:m,isMeasurementAvailable:p,isVideoUploadedCorrect:f,loading:h,showDeniedModal:g,uploadLoading:x,setScanFailsError:S,setScanStartTime:v,resetScan:y,onCustomScanSuccess:C,swan:j,onComplete:N}=t.useContext(s.ParamsContext),D=s.useConfig();return r?e.jsxs("div",{"data-scan-state":"error",children:[e.jsx(w,{}),e.jsx(o.Drawer,{anchor:"bottom",open:!0,className:"camera-drawer",onClose:(e,t)=>{},children:e.jsx(k,{scanFailsError:!0,onNext:a,gender:i})})]}):c?e.jsx("div",{"data-scan-state":"success",children:e.jsx(E,{isSuccess:!0,onComplete:N})}):h?e.jsx("div",{"data-scan-state":"loading",className:"flex top-0 !mt-0 left-0 z-[999] absolute justify-center items-center w-full h-full",style:{background:D?.style?.base?.backgroundColor},children:e.jsx(s.LoadingScreen,{url:D?.loader,loaderType:"black"})}):g.disabled?e.jsx("div",{"data-scan-state":"camera-denied",children:e.jsx(s.Modal,{})}):x||u||p||f?(x||p||f)&&!u?e.jsx("div",{"data-scan-state":"processing",children:e.jsx(E,{scanId:l,isMeasurementAvailable:p,userDetails:n,isVideoUploadedCorrect:f,onCustomScanSuccess:C,swan:j,onComplete:N,config:D})}):e.jsxs("div",{"data-scan-state":"error-retry",children:[e.jsx(w,{}),e.jsx(o.Drawer,{anchor:"bottom",open:!0,className:"camera-drawer",onClose:(e,t)=>{},children:e.jsx(k,{scanFailsError:u,onNext:()=>{a?.(),y()},setScanUniqueKey:d,gender:i,setIsVideoUploaded:m})})]}):e.jsx("div",{"data-scan-state":"scanning",children:e.jsx(b,{scanID:l,userDetails:n,setIsVideoUploaded:m,setScanFailsError:S,setScanStartTime:v,setScanUniqueKey:d})})},D=n=>{const{config:a,onRetry:r,onScanStart:o,onCaptureComplete:c,onUploadStart:i,onUploadEnd:l,onMeasurementSocketStart:u,onMeasurementSocketClose:d,onIntermediateScanSuccess:p,onScanError:f,onScanSuccess:h,onCustomScanSuccess:g,onComplete:x,isError:S,isSuccess:v}=n,y={email:"",shopDomain:"",gender:"male",heightInCm:0,scanType:"",deviceFocalLength:0,deviceModelName:"",sourceTag:"",callbackUrl:null,...n.userDetails??{}},C=n.token??"",{gender:b,scanType:w,shopDomain:j,heightInCm:k,email:E,deviceFocalLength:D,deviceModelName:T,callbackUrl:I,sourceTag:_}=y,L=t.useMemo(()=>s.getSwanService(C),[C]),[M,F]=t.useState(!1),[P,R]=t.useState(!1),[$,A]=t.useState(!1),[U,O]=t.useState({disabled:!1,message:""}),[z,K]=t.useState(""),[q,B]=t.useState(!1),[V,W]=t.useState(!0),[H,G]=t.useState(!1),[J,Q]=t.useState(""),[X,Y]=t.useState(s.getCurrentTimeInSeconds()),{gyroData:Z}=function(e){const[s,n]=t.useState([]),[a,r]=t.useState(!1),o=t.useCallback(e=>{try{const{alpha:t,beta:s,gamma:a}=e;n(e=>[...e,{alpha:t?.toString()||void 0,beta:s?.toString()||void 0,gamma:a?.toString()||void 0,timestamp:(new Date).toISOString()}])}catch(e){console.log(e)}},[]),c=t.useCallback(async()=>{const e=DeviceOrientationEvent;if(void 0!==e&&"function"==typeof e.requestPermission)try{"granted"===await e.requestPermission()?r(!0):console.warn("Device orientation permission denied.")}catch(e){console.error("Error requesting device orientation permission:",e)}else r(!0)},[]);return t.useEffect(()=>(e&&a?window.addEventListener("deviceorientation",o):n([]),()=>{window.removeEventListener("deviceorientation",o)}),[e,a,o]),t.useEffect(()=>{e&&c()},[e,c]),{gyroData:s}}(H);s.usePosthogPageview();const ee=t.useCallback(()=>{Q(s.generateUuid()),K(""),F(!1),B(!1)},[]),te=()=>{Y(null),Q("")},se=t.useCallback(async e=>{if(e&&"success"===e?.scanStatus&&"intermediate"===e?.resultType&&200===e?.code)return s.handleScanTimeCapture({eventName:`${j}/measurement_success/intermediate`,scanID:J,status:"success",email:E}),R(!0),void p?.(e);h?.(e),R(!0),te();const t=J;A(!0),s.handleScanTimeCapture({eventName:`${j}/measurement_success/fit-view`,scanID:t,status:"success",email:E}),X&&s.handleScanTimeCapture({eventName:`${j}/scan_completion_time`,scanID:t,status:"success",completionTime:s.getCurrentTimeInSeconds()-X,email:E})},[w,j,J]),ne=e=>{A(!1),K(""),R(!1),L.measurement.handleMeasurementSocket({scanId:J,onPreopen:()=>{s.handleWebSocketCapture({eventName:`${j}/webSocket`,scanID:J,connection:"pre_open",type:"measurement_recommendation",email:E})},onOpen:()=>{u?.(),s.handleWebSocketCapture({eventName:`${j}/webSocket`,scanID:J,connection:"open",type:"measurement_recommendation",email:E})},onClose:()=>{d?.(),s.handleWebSocketCapture({eventName:`${j}/webSocket`,scanID:J,connection:"close",type:"measurement_recommendation",email:E})},onError:e=>{var t;t=e,f?.({...t,message:s.handleErrorMessage(t)}),te(),A(!1),K(t),R(!1),s.handleScanTimeCapture({eventName:`${j}/measurement_failed/fit-view`,scanID:J,status:"failed",email:E,message:s.handleErrorMessage(t)}),X&&s.handleScanTimeCapture({eventName:`${j}/scan_completion_time`,scanID:J,status:"failed",completionTime:s.getCurrentTimeInSeconds()-X,email:E}),s.handleWebSocketCapture({eventName:`${j}/webSocket`,scanID:J,connection:"error",type:"measurement_recommendation",email:E})},onSuccess:e=>{s.handleWebSocketCapture({eventName:`${j}/webSocket`,scanID:J,connection:"success",type:"measurement_recommendation",email:E}),se(e)}})},ae=t.useCallback(async(e,t)=>{const n=Number((e.size/1048576).toFixed(2)),a=null!=t?t:await s.getVideoFPS(e),r=s.createObjectMetadataArray({gender:b,focal_length:`${D}`,height:`${k}`,customer_store_url:j,clothes_fit:"0",scan_type:s.getScanType(w),callback_url:I,source_tag:_});s.handleScanTimeCapture({eventName:`${j}/body_scan_meta_data`,scanID:J,email:E,data:JSON.stringify([...r,{fileSizeInMB:n,video_fps:a||0}])}),i?.();try{await L.fileUpload.uploadFileFrontend({file:e,arrayMetaData:r,scanId:J,email:E}),await L.fileUpload.setDeviceInfo({model:T,detection:"manual",gyro:Z,scanId:J}),console.log("video successfully uploaded"),B(!1),s.handleScanTimeCapture({eventName:`${j}/scan_success`,scanID:J,status:"success",email:E,data:JSON.stringify(r)}),s.handleScanTimeCapture({eventName:"scan finished",scanID:J,status:"success",email:E}),Y(s.getCurrentTimeInSeconds()),setTimeout(()=>{B(!0)},3e3)}catch(e){s.handleScanTimeCapture({eventName:"scan finished",scanID:J,status:"failed",email:E,message:s.handleErrorMessage(e)}),s.handleScanTimeCapture({eventName:`${j}/scan_failed`,scanID:J,status:"failed",email:E,message:s.handleErrorMessage(e),data:JSON.stringify(r)}),K(s.handleErrorMessage(e)),B(!1),f?.({...e,message:s.handleErrorMessage(e)}),te()}finally{G(!1),l?.()}},[J,Z,i,l]),re=t.useCallback(async()=>{if(+k<152.4||+k>213.36)return void f?.({message:"Height must be between 152.4cm (5ft) and 213.36cm (7ft)"});const e=await s.checkCameraPermission();e.disabled?(s.handleScanTimeCapture({eventName:`${j}/camera_activation`,scanID:J,status:"failed",email:E}),W(!1)):(W(!1),s.handleScanTimeCapture({eventName:`${j}/camera_activation`,scanID:J,status:"success",email:E})),O(e),K(""),B(!1)},[E,J,Q,j]);return t.useEffect(()=>{S||v||j&&re()},[j,k,S,v]),t.useEffect(()=>{S||v||q&&j&&J&&ne()},[q,j,J,S,v]),e.jsx("div",{"data-testid":"body-scan-root",children:e.jsx(s.LanguageContextProvider,{children:e.jsx(s.ConfigProvider,{config:a,children:e.jsx(m,{children:e.jsx(s.ParamsContext.Provider,{value:{userDetails:y,onRetry:r,onScanError:f,isError:S,isSuccess:v,onScanSuccess:h,gender:b,scanUniqueKey:J,scanFailsError:z,setScanUniqueKey:Q,setIsVideoUploaded:B,isMeasurementAvailable:$,isVideoUploadedCorrect:P,loading:V,showDeniedModal:U,uploadLoading:M,setStartGyro:G,uploadScanFile:ae,setUploadLoading:F,resetScan:ee,setScanFailsError:K,setScanStartTime:Y,onScanStart:o,onCaptureComplete:c,onCustomScanSuccess:g,onComplete:x,swan:L},children:e.jsx(N,{})})})})})})};exports.BodyScanClientGate=s=>{const[n,a]=t.useState(!1);return t.useEffect(()=>{a(!0)},[]),n?e.jsx(D,{...s}):e.jsx("div",{"data-testid":"body-scan-root"})};
|
|
3
|
+
//# sourceMappingURL=BodyScan-CAAzIoLb.js.map
|