import AnimatedProgressCheckmarkView from "@components/AnimatedProgressCheckmarkView"; import Banner, { AlertContainer, BannerType, TopBannerContainer, } from "@components/Banner"; import CameraViewfinder from "@components/CameraViewfinder"; import { CenteredComponentContainer } from "@components/CenteredComponentContainer"; import ScreenContainer from "@components/ScreenContainer"; import { DetectionFailure, DetectionFeedback, DetectionFeedbackType, PrismSession, PrismSessionState, } from "@prismlabs/web-scan-core"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { resolveSvg } from "../../assets/assetRegistry"; import { getDetectionFeedbackLocalization } from "../../localization/detection-feedback-localization"; import { YellowIcon, YellowIconType } from "../components"; import CameraOutline, { OutlineState } from "../components/CameraOutline"; import ErrorView from "../components/ErrorView"; import { PositionSpeechManager } from "./position-speech-manager"; import { PositioningState, PositioningStateDistributor, PositioningStateType, } from "./positioning-state-distributor"; interface BannerFeedbackProps { detectionFeedback: DetectionFeedback; } const BannerFeedback: React.FC = ({ detectionFeedback, }) => { const getPositionTitle = (feedback: DetectionFeedback): string => { return getDetectionFeedbackLocalization(feedback); }; const getPositionIcon = ( feedback: DetectionFeedback ): React.ReactNode | undefined => { switch (feedback.type) { case DetectionFeedbackType.EmptyFrame: case DetectionFeedbackType.IncompleteData: case DetectionFeedbackType.MultiplePeople: return ; case DetectionFeedbackType.Failed: switch (feedback.detectionFailure) { case DetectionFailure.TOO_FAR: return ; case DetectionFailure.TOO_CLOSE: return ; case DetectionFailure.TOO_FAR_LEFT: return ; case DetectionFailure.TOO_FAR_RIGHT: return ; case DetectionFailure.BACKWARD: return ; case DetectionFailure.CAMERA_TOO_HIGH: case DetectionFailure.EMPTY_SPACE_TOP: return ; case DetectionFailure.CAMERA_TOO_LOW: case DetectionFailure.EMPTY_SPACE_BOTTOM: return ; default: return ; } case DetectionFeedbackType.Approved: return ; default: return undefined; // Replace with actual icon component } }; return ( ); }; interface PositionScreenProps { prismSession: PrismSession; } const PositionScreen: React.FC = ({ prismSession }) => { const { t } = useTranslation(); const distributorRef = React.useRef(null); if (!distributorRef.current) { distributorRef.current = new PositioningStateDistributor( prismSession.captureSession.positionReadinessDistributor ); } const distributor = distributorRef.current!; const positionSpeechManagerRef = React.useRef( null ); if (!positionSpeechManagerRef.current) { positionSpeechManagerRef.current = new PositionSpeechManager( distributor, t ); } const positionSpeechManager = positionSpeechManagerRef.current!; const [positioningState, setPositioningState] = useState( PositioningState.starting ); const [isBannerVisible, setBannerVisible] = useState(true); const [isFeedbackVisible, setFeedbackVisible] = useState(false); const [isCheckmarkVisible, setCheckmarkVisible] = useState(false); // Determine outline state based on detection feedback const getOutlineState = (): OutlineState => { if (!positioningState.detectionFeedback) { return OutlineState.Clear; } const feedbackType = positioningState.detectionFeedback.type; // Clear state for no person detected if ( feedbackType === DetectionFeedbackType.EmptyFrame || feedbackType === DetectionFeedbackType.IncompleteData ) { return OutlineState.Error; } // Success state for approved if (feedbackType === DetectionFeedbackType.Approved) { return OutlineState.Success; } // Error state for all other cases (MultiplePeople, Failed) return OutlineState.Error; }; useEffect(() => { const subscription = distributor.positioningState.subscribe({ next: (state) => { setPositioningState(state); }, error: (error) => { console.error("Error in positioning state distributor:", error); }, }); distributor.start(); // start the state distributor positionSpeechManager.start(); // Start the speech manager return () => { positionSpeechManager.stop(); // Stop the speech manager on unmount distributor.dispose(); subscription.unsubscribe(); // Cleanup on unmount }; }, []); useEffect(() => { const isBannerVisible: boolean = positioningState.type === PositioningStateType.Starting; setBannerVisible(isBannerVisible); const isFeedbackVisible: boolean = positioningState.type === PositioningStateType.Assisting || positioningState.type === PositioningStateType.VerifyingConsistency; setFeedbackVisible(isFeedbackVisible); const isCheckmarkVisible: boolean = positioningState.type === PositioningStateType.VerifyingConsistency; setCheckmarkVisible(isCheckmarkVisible); }, [positioningState.type]); const handleAnimationFinished = () => { prismSession.continueFrom(PrismSessionState.POSITIONING); }; return ( {isBannerVisible && ( User Position } /> )} {isFeedbackVisible && positioningState.detectionFeedback && ( )} {isCheckmarkVisible && ( )} ); }; export default PositionScreen;