feat(recipe): edit button on the top bar, with overwrite-or-save-as on save
This commit is contained in:
+99
-12
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, TouchableOpacity, TextInput, Modal, KeyboardAvoidingView, Platform } from 'react-native';
|
||||
import { View, Text, TouchableOpacity, TextInput, Modal, KeyboardAvoidingView, Platform, Alert } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Plus } from 'lucide-react-native';
|
||||
import { Plus, Pencil } from 'lucide-react-native';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
|
||||
interface TopBarProps {
|
||||
@@ -16,6 +16,8 @@ export default function TopBar({ mode, recipeName, onSaveRecipe, onOverwriteReci
|
||||
const insets = useSafeAreaInsets();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [newRecipeName, setNewRecipeName] = useState('');
|
||||
const [editVisible, setEditVisible] = useState(false);
|
||||
const [editName, setEditName] = useState('');
|
||||
|
||||
const openModal = () => {
|
||||
// Bundled recipes are read-only -> Save As only, blank name. Custom ones
|
||||
@@ -36,6 +38,35 @@ export default function TopBar({ mode, recipeName, onSaveRecipe, onOverwriteReci
|
||||
const handleSavePress = () => run(onSaveRecipe);
|
||||
const handleOverwritePress = () => onOverwriteRecipe && run(onOverwriteRecipe);
|
||||
|
||||
const openEdit = () => {
|
||||
setEditName(recipeName);
|
||||
setEditVisible(true);
|
||||
};
|
||||
|
||||
// Edit keeps the name as the switch: same name = overwrite this recipe, a new
|
||||
// name = keep the original and store the edit as a separate recipe. Both ask
|
||||
// for confirmation first.
|
||||
const handleEditSave = () => {
|
||||
const name = editName.trim();
|
||||
if (!name || !onOverwriteRecipe) return;
|
||||
const unchanged = name === recipeName.trim();
|
||||
setEditVisible(false);
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||
Alert.alert(
|
||||
unchanged ? 'Overwrite recipe?' : 'Save as new recipe?',
|
||||
unchanged
|
||||
? `Replace "${recipeName}" with the current settings?`
|
||||
: `Keep "${recipeName}" and save these settings as "${name}"?`,
|
||||
[
|
||||
{ text: 'CANCEL', style: 'cancel' },
|
||||
{
|
||||
text: unchanged ? 'OVERWRITE' : 'SAVE',
|
||||
onPress: () => (unchanged ? onOverwriteRecipe(name) : onSaveRecipe(name)),
|
||||
},
|
||||
],
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
@@ -54,16 +85,27 @@ export default function TopBar({ mode, recipeName, onSaveRecipe, onOverwriteReci
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
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}
|
||||
>
|
||||
<Plus size={14} color="#f59e0b" />
|
||||
<Text className="ml-1 font-mono text-xs font-bold tracking-widest text-amber-500">
|
||||
SAVE
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View className="flex-row items-center space-x-2">
|
||||
{onOverwriteRecipe && (
|
||||
<TouchableOpacity
|
||||
onPress={openEdit}
|
||||
className="rounded-full border border-zinc-700/80 bg-black/45 p-2.5"
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Pencil size={13} color="#f59e0b" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
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}
|
||||
>
|
||||
<Plus size={14} color="#f59e0b" />
|
||||
<Text className="ml-1 font-mono text-xs font-bold tracking-widest text-amber-500">
|
||||
SAVE
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Modal
|
||||
@@ -124,6 +166,51 @@ export default function TopBar({ mode, recipeName, onSaveRecipe, onOverwriteReci
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
transparent
|
||||
animationType="slide"
|
||||
visible={editVisible}
|
||||
onRequestClose={() => setEditVisible(false)}
|
||||
>
|
||||
<View className="absolute inset-0 bg-black/60" />
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1 }}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
>
|
||||
<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">
|
||||
EDIT RECIPE
|
||||
</Text>
|
||||
<TextInput
|
||||
value={editName}
|
||||
onChangeText={setEditName}
|
||||
placeholder="Recipe name"
|
||||
placeholderTextColor="#71717a"
|
||||
className="bg-carbon border border-zinc-800 rounded-lg p-3 text-white font-mono text-sm mb-2"
|
||||
autoFocus
|
||||
onSubmitEditing={handleEditSave}
|
||||
/>
|
||||
<Text className="text-zinc-500 font-mono text-[10px] mb-4">
|
||||
New name saves a copy · same name overwrites
|
||||
</Text>
|
||||
<View className="flex-row space-x-3">
|
||||
<TouchableOpacity
|
||||
onPress={() => setEditVisible(false)}
|
||||
className="flex-1 bg-zinc-800 py-3 rounded-lg items-center"
|
||||
>
|
||||
<Text className="text-zinc-300 font-mono text-xs font-semibold">CANCEL</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={handleEditSave}
|
||||
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>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user