feat(recipe): edit opens the prefilled create form; fields select their value on tap

This commit is contained in:
2026-09-10 21:34:32 +07:00
parent b154e50191
commit 868ebfe6e2
4 changed files with 103 additions and 96 deletions
+30 -13
View File
@@ -26,6 +26,10 @@ interface RecipeCreateModalProps {
// Id of the currently applied film sim (or bundled recipe whose look should
// prefill the SIMULATION row); undefined → default to PROVIA.
currentBase?: BaseFilter;
// When set the form opens as an EDITOR: every row (name included) is
// prefilled from this recipe instead of the sim's standard values, and
// SAVE hands the edited copy back for the caller to store.
initial?: Recipe | null;
onClose: () => void;
onSave: (recipe: Omit<Recipe, 'id' | 'isCustom'>) => void;
}
@@ -127,6 +131,7 @@ const NumRow = ({ label, value, onChange, placeholder, width = 58 }: NumRowProps
<TextInput
value={value}
onChangeText={onChange}
selectTextOnFocus
keyboardType="numbers-and-punctuation"
placeholder={placeholder ?? '0'}
placeholderTextColor="#52525b"
@@ -139,6 +144,7 @@ const NumRow = ({ label, value, onChange, placeholder, width = 58 }: NumRowProps
export default function RecipeCreateModal({
visible,
currentBase,
initial,
onClose,
onSave,
}: RecipeCreateModalProps) {
@@ -151,8 +157,8 @@ export default function RecipeCreateModal({
// Every sim's standard values as form state — used to seed the modal and to
// reload the form when the SIMULATION chip changes.
const valuesOf = (b: BaseFilter) => {
const s = FILM_SIMS.find((x) => x.baseFilter === b)?.adjustments ?? DEFAULT_ADJUSTMENTS;
const valuesOf = (b: BaseFilter, src?: ColorAdjustments) => {
const s = src ?? FILM_SIMS.find((x) => x.baseFilter === b)?.adjustments ?? DEFAULT_ADJUSTMENTS;
return {
dr: String(s.dynamicRange ?? 'auto'),
grain: String(s.grain === 0 ? 0 : s.grain >= 6 ? 6 : 3),
@@ -199,15 +205,18 @@ export default function RecipeCreateModal({
// quick-input always starts from the current simulation.
useEffect(() => {
if (!visible) return;
const v = valuesOf(seedBase);
setSim(seedBase);
// Editing a stored recipe wins over the live sim seed: the form must show
// that recipe's own values, not the sim's standard ones.
const base = initial?.baseFilter ?? seedBase;
const v = valuesOf(base, initial?.adjustments);
setSim(base);
setDr(v.dr);
setGrain(v.grain);
setCc(v.cc);
setCcb(v.ccb);
setWbKey(v.wbKey);
setWbRed('0');
setWbBlue('0');
setWbRed(String(initial?.adjustments.wbRed ?? 0));
setWbBlue(String(initial?.adjustments.wbBlue ?? 0));
setFHigh(v.highlight);
setFShadow(v.shadow);
setFColor(v.color);
@@ -215,9 +224,9 @@ export default function RecipeCreateModal({
setFSharp(v.sharp);
setFClarity(v.clarity);
setFEv(v.ev);
setName('');
setName(initial?.name ?? '');
setIso(null);
}, [visible, currentBase]);
}, [visible, currentBase, initial]);
// Switching the simulation reloads its standard values into the form.
const selectSim = (b: BaseFilter) => {
@@ -260,11 +269,14 @@ export default function RecipeCreateModal({
exposureCompensation: int(fEv, simAdj.exposureCompensation, -3, 3),
};
onSave({
name: name.trim() || 'NEW RECIPE',
name: name.trim() || initial?.name || 'NEW RECIPE',
baseFilter: sim,
adjustments: adj,
frameId: 'none',
useGeotag: true,
// An edit keeps the recipe's own frame and geotag choice; a fresh
// CREATE keeps the old defaults.
frameId: initial?.frameId ?? 'none',
useGeotag: initial?.useGeotag ?? true,
...(initial?.cinema ? { cinema: initial.cinema } : {}),
});
setName('');
onClose();
@@ -289,7 +301,9 @@ export default function RecipeCreateModal({
className="bg-titan border-b border-zinc-800 rounded-b-3xl px-4 pt-1"
>
<View className="flex-row items-center justify-between mb-2">
<Text className="text-white font-mono text-sm font-bold tracking-wider">CREATE RECIPE</Text>
<Text className="text-white font-mono text-sm font-bold tracking-wider">
{initial ? 'EDIT RECIPE' : 'CREATE RECIPE'}
</Text>
<TouchableOpacity onPress={onClose} className="px-2 py-1" activeOpacity={0.7}>
<Text className="text-zinc-400 font-mono text-xs font-bold">✕</Text>
</TouchableOpacity>
@@ -301,6 +315,7 @@ export default function RecipeCreateModal({
<TextInput
value={name}
onChangeText={setName}
selectTextOnFocus
placeholder="Recipe name (e.g. Fuji Retro)"
placeholderTextColor="#71717a"
className="bg-carbon border border-zinc-800 rounded-lg p-2 text-white font-mono text-sm mb-2"
@@ -366,6 +381,7 @@ export default function RecipeCreateModal({
<TextInput
value={wbRed}
onChangeText={setWbRed}
selectTextOnFocus
keyboardType="numbers-and-punctuation"
placeholder="-9..9"
placeholderTextColor="#52525b"
@@ -376,6 +392,7 @@ export default function RecipeCreateModal({
<TextInput
value={wbBlue}
onChangeText={setWbBlue}
selectTextOnFocus
keyboardType="numbers-and-punctuation"
placeholder="-9..9"
placeholderTextColor="#52525b"
@@ -407,7 +424,7 @@ export default function RecipeCreateModal({
<Text className="text-zinc-300 font-mono text-xs font-semibold">CANCEL</Text>
</TouchableOpacity>
<TouchableOpacity onPress={handleSave} className="flex-1 bg-amber-500 py-3 rounded-lg items-center" activeOpacity={0.7}>
<Text className="text-black font-mono text-xs font-bold">SAVE RECIPE</Text>
<Text className="text-black font-mono text-xs font-bold">{initial ? 'SAVE' : 'SAVE RECIPE'}</Text>
</TouchableOpacity>
</View>
</View>