feat(recipe): Save and Save As for recipes

Custom recipes can be overwritten in place (TopBar SAVE now offers CANCEL /
SAVE AS / SAVE and prefills the current name); bundled recipes stay read-only
and only offer Save As. Adds updateCustomRecipe to storageUtils.
This commit is contained in:
2026-09-10 20:57:19 +07:00
parent 4bb2bda5c8
commit 91109cba54
3 changed files with 71 additions and 13 deletions
+40 -11
View File
@@ -8,22 +8,34 @@ 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;
}
export default function TopBar({ mode, recipeName, onSaveRecipe }: TopBarProps) {
export default function TopBar({ mode, recipeName, onSaveRecipe, onOverwriteRecipe }: TopBarProps) {
const insets = useSafeAreaInsets();
const [modalVisible, setModalVisible] = useState(false);
const [newRecipeName, setNewRecipeName] = useState('');
const handleSavePress = () => {
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);
};
const run = (fn: (name: string) => void) => {
const name = newRecipeName.trim();
if (!name) return;
onSaveRecipe(name);
fn(name);
setNewRecipeName('');
setModalVisible(false);
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
};
const handleSavePress = () => run(onSaveRecipe);
const handleOverwritePress = () => onOverwriteRecipe && run(onOverwriteRecipe);
return (
<>
<View
@@ -43,7 +55,7 @@ export default function TopBar({ mode, recipeName, onSaveRecipe }: TopBarProps)
</View>
<TouchableOpacity
onPress={() => setModalVisible(true)}
onPress={openModal}
className="flex-row items-center rounded-full border border-zinc-700/80 bg-black/45 px-4 py-2.5"
activeOpacity={0.7}
>
@@ -67,7 +79,7 @@ export default function TopBar({ mode, recipeName, onSaveRecipe }: TopBarProps)
>
<View style={{ paddingBottom: insets.bottom + 16 }} className="mt-auto bg-titan border-t border-zinc-800 rounded-t-3xl p-6">
<Text className="text-white font-mono text-sm font-bold tracking-wider mb-4">
SAVE CURRENT AS RECIPE
{onOverwriteRecipe ? 'SAVE RECIPE' : 'SAVE CURRENT AS RECIPE'}
</Text>
<TextInput
value={newRecipeName}
@@ -85,12 +97,29 @@ export default function TopBar({ mode, recipeName, onSaveRecipe }: TopBarProps)
>
<Text className="text-zinc-300 font-mono text-xs font-semibold">CANCEL</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={handleSavePress}
className="flex-1 bg-amber-500 py-3 rounded-lg items-center"
>
<Text className="text-black font-mono text-xs font-bold">SAVE RECIPE</Text>
</TouchableOpacity>
{onOverwriteRecipe ? (
<>
<TouchableOpacity
onPress={handleSavePress}
className="flex-1 border border-amber-500 py-3 rounded-lg items-center"
>
<Text className="text-amber-500 font-mono text-xs font-bold">SAVE AS</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={handleOverwritePress}
className="flex-1 bg-amber-500 py-3 rounded-lg items-center"
>
<Text className="text-black font-mono text-xs font-bold">SAVE</Text>
</TouchableOpacity>
</>
) : (
<TouchableOpacity
onPress={handleSavePress}
className="flex-1 bg-amber-500 py-3 rounded-lg items-center"
>
<Text className="text-black font-mono text-xs font-bold">SAVE RECIPE</Text>
</TouchableOpacity>
)}
</View>
</View>
</KeyboardAvoidingView>