import React, { useContext, useEffect } from 'react'; import { StyleSheet, View, Animated, Easing } from 'react-native'; import Svg, { Mask, Rect } from 'react-native-svg'; import LottieView from 'lottie-react-native'; import { GetIDContext, type Theme } from '../../contexts/getid-context'; import captureAnimationLottie from './assets/capturing.json'; import { throttle } from '../../utils/throttle'; interface InFrame { right: boolean; left: boolean; top: boolean; bottom: boolean; } export type DocumentOverlayProps = { inFrame?: InFrame; rect: { width: number; left: number; right: number; top: number; bottom: number; height: number; }; isLoading?: boolean; isSaving?: boolean; AnimatedFrame?: boolean; }; export type RectangleProps = { width: number; height: number; right: number; left: number; top: number; bottom: number; status: 'error' | 'success'; }; type AnimatedFrameProps = { theme?: Theme; inFrame?: InFrame; isSaving?: boolean; }; enum KeyPath { TopLeft = 'Top Left', TopRight = 'Top Right', BottomLeft = 'Bottom Left', BottomRight = 'Bottom Right', } const AnimatedFrame = ({ theme, inFrame, isSaving }: AnimatedFrameProps) => { const themeColor = theme?.cameraFrameColor as string; const baseColor = themeColor; const errorColor = theme?.errorColor as string; const successColor = theme?.successColor as string; const captureAnimationLottieRef = React.useRef(null); const [shouldPlay, setShouldPlay] = React.useState(false); const shouldPlayRef = React.useRef(shouldPlay); const isPlaying = React.useRef(false); const animationProgress = React.useRef(new Animated.Value(0)); const animationControl = React.useRef( Animated.loop( Animated.timing(animationProgress.current, { toValue: 1, duration: 800, easing: Easing.linear, useNativeDriver: false, }) ) ); const [colorFilters, setColorFilters] = React.useState([ { keypath: '**', color: baseColor, }, ]); useEffect(() => { if (isSaving) return; const topLeft = !!(inFrame?.top && inFrame?.left); const topRight = !!(inFrame?.top && inFrame?.right); const bottomLeft = !!(inFrame?.bottom && inFrame?.left); const bottomRight = !!(inFrame?.bottom && inFrame?.right); const allInframe = topLeft && topRight && bottomLeft && bottomRight; setShouldPlay(!allInframe); setColorFilters([ { keypath: KeyPath.TopLeft, color: topLeft ? successColor : errorColor, }, { keypath: KeyPath.TopRight, color: topRight ? successColor : errorColor, }, { keypath: KeyPath.BottomLeft, color: bottomLeft ? successColor : errorColor, }, { keypath: KeyPath.BottomRight, color: bottomRight ? successColor : errorColor, }, ]); }, [errorColor, inFrame, isSaving, successColor]); useEffect(() => { setShouldPlay(!isSaving); if (isSaving) { setColorFilters( Object.values(KeyPath).map((value) => ({ keypath: value, color: successColor, })) ); } }, [isSaving, successColor]); const playAnimation = () => { animationControl.current.start(); isPlaying.current = true; }; const stopAnimation = () => { animationProgress.current.stopAnimation(); isPlaying.current = false; }; const toggleAnimation = throttle((play: boolean) => { if (play && !isPlaying.current) playAnimation(); if (!play && isPlaying.current) stopAnimation(); }, 1000); useEffect(() => { shouldPlayRef.current = shouldPlay; shouldPlay && toggleAnimation(true); }, [shouldPlay, toggleAnimation]); useEffect(() => { animationProgress.current.addListener(({ value }) => { if (value === 1 && !shouldPlayRef.current) toggleAnimation(false); }); }, [toggleAnimation]); return ( ); }; export type Point = { x: number; y: number }; export type QuadranglesProps = { quadrangles: Point[][]; }; const quadrangleColors = ['red', 'green', 'blue']; // Draws the points of quadrangles on the screen export const Quadrangles = (props: QuadranglesProps) => { return ( {props.quadrangles.flatMap((quadrangle, i) => quadrangle.map((point) => ( )) )} ); }; export const DocumentOverlay = (props: DocumentOverlayProps) => { const { width, height, top, left, right } = props.rect; const fullWidth = right + left; const widthPercent = (width * 100) / fullWidth; const context = useContext(GetIDContext); const overlayColor = context?.theme.cameraOverlayBackgroundColor; return ( {props.isLoading && ( )} ); }; const styles = StyleSheet.create({ container: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', zIndex: 2, }, frameWrapper: { position: 'absolute', top: 0, bottom: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', width: '100%', height: '100%', }, frame: { justifyContent: 'center', borderStyle: 'solid', borderRadius: 22, alignItems: 'center', }, scanningAnimationWrapper: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', borderRadius: 18, overflow: 'hidden', }, scanningAnimation: { flex: 1, }, animatedFrameWrapper: { transform: [{ scale: 1.35 }], width: '100%', height: '100%', }, });