All files / app/(tabs)/scanner/components/camera PhotoPreview.tsx

100% Statements 4/4
100% Branches 2/2
100% Functions 1/1
100% Lines 3/3

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128                                                                                                      5x   5x   4x                                                                                                                                                
import React from 'react';
import { Image, Modal } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Text, XStack, YStack } from 'tamagui';
import { Button } from '@/src/design-system/components';
import { IconSymbol } from '@/src/components/ui/IconSymbol';
 
interface PhotoPreviewProps {
  /** 照片 URI */
  photoUri: string | null;
  /** 是否显示 */
  visible: boolean;
  /** 确认使用照片 */
  onConfirm: () => void;
  /** 重新拍照 */
  onRetake: () => void;
  /** 取消 */
  onCancel: () => void;
}
 
/**
 * 照片预览组件
 *
 * 功能:
 * - 显示刚拍摄的照片预览
 * - 提供确认使用、重拍、取消三个操作
 * - 全屏显示,提供更好的查看体验
 *
 * 设计原则:
 * - Modal 使用 React Native 原生组件
 * - 布局使用 Tamagui 组件保持一致性
 * - 提供清晰的操作按钮和视觉反馈
 *
 * @example
 * ```tsx
 * <PhotoPreview
 *   photoUri={photoUri}
 *   visible={showPreview}
 *   onConfirm={handleConfirm}
 *   onRetake={handleRetake}
 *   onCancel={handleCancel}
 * />
 * ```
 */
export function PhotoPreview({
  photoUri,
  visible,
  onConfirm,
  onRetake,
  onCancel,
}: PhotoPreviewProps) {
  const insets = useSafeAreaInsets();
 
  if (!photoUri) return null;
 
  return (
    <Modal visible={visible} animationType="fade" statusBarTranslucent>
      <YStack flex={1} backgroundColor="black">
        {/* 顶部操作栏 */}
        <XStack
          paddingTop={insets.top + 10}
          paddingHorizontal="$4"
          paddingBottom="$3"
          justifyContent="space-between"
          alignItems="center"
          backgroundColor="rgba(0, 0, 0, 0.7)"
        >
          <Button size="sm" variant="ghost" rounded onPress={onCancel}>
            <IconSymbol name="xmark" size={24} color="white" />
          </Button>
          <Text fontSize="$6" fontWeight="600" color="white">
            照片预览
          </Text>
          <YStack width={40} height={40} />
        </XStack>
 
        {/* 照片预览区域 */}
        <YStack flex={1} justifyContent="center" alignItems="center" padding="$4">
          <Image
            source={{ uri: photoUri }}
            style={{ width: '100%', height: '100%' }}
            resizeMode="contain"
          />
        </YStack>
 
        {/* 底部操作按钮 */}
        <YStack
          paddingHorizontal="$4"
          paddingBottom={insets.bottom + 20}
          paddingTop="$4"
          backgroundColor="rgba(0, 0, 0, 0.7)"
          gap="$3"
        >
          {/* 提示文本 */}
          <Text fontSize="$3" color="$gray10" textAlign="center">
            请确认照片清晰可见,配料表信息完整
          </Text>
 
          {/* 操作按钮组 */}
          <XStack gap="$3">
            {/* 重拍按钮 */}
            <Button
              flex={1}
              size="lg"
              variant="secondary"
              onPress={onRetake}
              leftIcon={<IconSymbol name="camera.rotate" size={20} color="white" />}
            >
              重新拍照
            </Button>
 
            {/* 确认按钮 */}
            <Button
              flex={1}
              size="lg"
              variant="primary"
              onPress={onConfirm}
              leftIcon={<IconSymbol name="checkmark.circle.fill" size={20} color="white" />}
            >
              确认使用
            </Button>
          </XStack>
        </YStack>
      </YStack>
    </Modal>
  );
}