import React, { useState } from 'react'; import { View, Text, TouchableOpacity, TextInput, Modal, KeyboardAvoidingView, Platform, Alert } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Plus, Pencil, Undo2, Zap, ZapOff } from 'lucide-react-native'; import * as Haptics from 'expo-haptics'; import type { FlashMode } from '../types'; // "A" badge tucked into the corner of the flash button's AUTO state. const FLASH_A_BADGE = { position: 'absolute' as const, bottom: 3, right: 5, fontSize: 8, fontWeight: '700' as const, color: '#f4f4f5', }; interface TopBarProps { mode: 'camera' | 'library'; recipeName: string; onSaveRecipe: (name: string) => void; // Present only when the active recipe is user-created: enables Save (overwrite). onOverwriteRecipe?: (name: string) => void; // Also custom-only: opens the recipe editor — the CREATE form prefilled // with this recipe's parameters and name. onEditRecipe?: () => void; // SAVE & SHARE -> EXPORT: write this look to an encrypted recipe file and // hand it to the system share sheet so it can reach another phone. onExportShare: (name: string) => void; // BACK (undo): one step back through the look's history. PRO only — App // refuses and offers the paywall when a LITE build taps it. No count is // drawn: the icon turns amber while there is a step to go back to. onUndo: () => void; undoDepth: number; // LED flash state for the next still, cycled off -> auto -> on. Absent = // no flash control to show (library mode, or a lens with no flash unit), // so the front camera shows no dead control. flashMode?: FlashMode; onCycleFlash?: () => void; // Height of the floating top strip (its own onLayout): App keeps the library // photo — and with it the ratio band — out from under it. onHeight?: (height: number) => void; } export default function TopBar({ mode, recipeName, onSaveRecipe, onOverwriteRecipe, onEditRecipe, onExportShare, onUndo, undoDepth, flashMode, onCycleFlash, onHeight, }: TopBarProps) { const insets = useSafeAreaInsets(); const [modalVisible, setModalVisible] = useState(false); const [newRecipeName, setNewRecipeName] = useState(''); const openModal = () => { // Bundled recipes are read-only -> Save As only, blank name. Custom ones // prefill with the current name so Save (overwrite) is one tap. setNewRecipeName(onOverwriteRecipe ? recipeName : ''); setModalVisible(true); }; // SAVE & SHARE: two ways out of the same look — keep it as a recipe on this // phone, or export it as a file another phone can import. const openSaveMenu = () => { Alert.alert( 'SAVE & SHARE', 'Save the look on this screen as a recipe, or export it as a file to share.', [ { text: 'CANCEL', style: 'cancel' }, { text: 'EXPORT & SHARE', onPress: () => onExportShare(recipeName) }, { text: 'SAVE RECIPE', onPress: openModal }, ], ); }; const run = (fn: (name: string) => void) => { const name = newRecipeName.trim(); if (!name) return; fn(name); setNewRecipeName(''); setModalVisible(false); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); }; const handleSavePress = () => run(onSaveRecipe); const handleOverwritePress = () => onOverwriteRecipe && run(onOverwriteRecipe); return ( <> onHeight?.(e.nativeEvent.layout.height)} className="absolute top-0 left-0 right-0 z-20 flex-row items-start justify-between px-4" > {mode === 'camera' ? 'LIVE' : 'LIBRARY'} {recipeName} {/* Capture-time LED flash, left of BACK: a still taken on the 0.5x native lens goes through its own Camera2 request, so the state has to be reachable while the chip row is off screen. */} {flashMode && onCycleFlash ? ( {flashMode === 'off' ? ( ) : ( // AUTO keeps the bolt dim and adds the badge below — a lit // bolt alone would read as ON. )} {flashMode === 'auto' ? A : null} ) : null} {/* BACK: the history control the chip row used to carry, sitting between the status block and SAVE & SHARE. Lucide (ISC) vector — no icon font to ship or license. */} 0 ? '#f59e0b' : '#52525b'} /> {onEditRecipe && ( )} SAVE & SHARE setModalVisible(false)} > {onOverwriteRecipe ? 'SAVE RECIPE' : 'SAVE CURRENT AS RECIPE'} setModalVisible(false)} className="flex-1 bg-zinc-800 py-3 rounded-lg items-center" > CANCEL {onOverwriteRecipe ? ( <> SAVE AS SAVE ) : ( SAVE RECIPE )} ); }