feat(panel): one RESET on every tab, back to the app's defaults
RESET was look-relative: it only put the sliders back on the values the active recipe ships with, so the look itself (a saved recipe, a film sim) survived the tap. It now restores the state a clean start leaves — the PROVIA startup look, every parameter neutral, frame off, the custom and GPS watermarks back to their defaults — and it sits on every tab, pinned outside the scrolling chip row so it can never scroll out of reach. Mode and aspect ratio stay put: they frame the shot being worked on, not the look.
This commit is contained in:
@@ -499,6 +499,32 @@ export default function App() {
|
||||
setGpsWm({ ...DEFAULT_GPS_WM });
|
||||
};
|
||||
|
||||
// RESET (panel chip, any tab): the state a default start would leave — the
|
||||
// startup look, every parameter back to neutral, frame off and both
|
||||
// watermarks back to their defaults. Same pieces as resetEditsToRecipe, but
|
||||
// the look goes back to the app's own default rather than the recipe's.
|
||||
const handleResetAll = () => {
|
||||
const provia = FILM_SIMS.find((f) => f.id === 'sim-provia');
|
||||
if (provia) {
|
||||
applyRecipe(filmSimToRecipe(provia));
|
||||
} else {
|
||||
setSelectedRecipe(null);
|
||||
setAdjustments({ ...DEFAULT_ADJUSTMENTS });
|
||||
setSelectedFrame('none');
|
||||
}
|
||||
setCustomWm({ ...DEFAULT_CUSTOM_WM });
|
||||
setCustomWmApplied({ ...DEFAULT_CUSTOM_WM });
|
||||
setGpsWm({ ...DEFAULT_GPS_WM });
|
||||
// The rest of the look's state, each back to the value a clean start ships
|
||||
// with: the GPS stamp off, both watermark rotations upright, the library
|
||||
// photo and its wall frame back to portrait. ponytail: mode + aspect stay
|
||||
// put — they frame the shot you are working on, not the look.
|
||||
setUseGeotag(false);
|
||||
setWmRotation(0);
|
||||
setPhotoRotation(0);
|
||||
setWallLandscape(false);
|
||||
};
|
||||
|
||||
const handleUpdateAdjustments = (updates: Partial<ColorAdjustments>) => {
|
||||
// Storm guard, generic across every param: same-value writes (slider
|
||||
// micro-jitter / repeated pointer-move events) still build a fresh
|
||||
@@ -1249,9 +1275,7 @@ export default function App() {
|
||||
recipes={recipes}
|
||||
currentRecipeId={selectedRecipe?.id || ''}
|
||||
adjustments={adjustments}
|
||||
lookDefaults={
|
||||
(selectedRecipe && pristineRecipe(selectedRecipe)?.adjustments) || DEFAULT_ADJUSTMENTS
|
||||
}
|
||||
onResetAll={handleResetAll}
|
||||
selectedFrame={selectedFrame}
|
||||
cropRatio={cropRatio}
|
||||
onUpdateCropRatio={setCropRatio}
|
||||
|
||||
@@ -36,9 +36,9 @@ interface AdjustmentPanelProps {
|
||||
recipes: Recipe[];
|
||||
currentRecipeId: string;
|
||||
adjustments: ColorAdjustments;
|
||||
// The values the active look ships with — a saved recipe's own set, a film
|
||||
// sim's canonical set, or the neutral defaults. RESET puts them back.
|
||||
lookDefaults: ColorAdjustments;
|
||||
// RESET: the app's own defaults, from any tab — the startup look, every
|
||||
// parameter, the frame and the watermarks.
|
||||
onResetAll: () => void;
|
||||
selectedFrame: FrameId;
|
||||
// FRAME tab CROP. Only meaningful on the library's plain frames (the camera
|
||||
// composes through its ratio band, polaroid/wall through their own window),
|
||||
@@ -166,7 +166,7 @@ export default function AdjustmentPanel({
|
||||
recipes,
|
||||
currentRecipeId,
|
||||
adjustments,
|
||||
lookDefaults,
|
||||
onResetAll,
|
||||
selectedFrame,
|
||||
cropRatio,
|
||||
onUpdateCropRatio,
|
||||
@@ -250,17 +250,6 @@ export default function AdjustmentPanel({
|
||||
|
||||
const a = adjustments;
|
||||
|
||||
// RESET (parameter tabs): every number of the active look back on the value
|
||||
// it ships with. Only the parameters count as "changed" — the groups below
|
||||
// are part of the same look, so they light it too.
|
||||
const lookDirty =
|
||||
[...PARAM_DEFS.iq, ...PARAM_DEFS.wb, ...PARAM_DEFS.filters].some(
|
||||
(d) => d.get(a) !== d.get(lookDefaults)
|
||||
) ||
|
||||
(a.colorChrome || 'none') !== (lookDefaults.colorChrome || 'none') ||
|
||||
(a.colorChromeBlue || 'none') !== (lookDefaults.colorChromeBlue || 'none') ||
|
||||
String(a.dynamicRange ?? 'auto') !== String(lookDefaults.dynamicRange ?? 'auto');
|
||||
|
||||
const renderChip = (c: ChipDef) => {
|
||||
const tone = c.active
|
||||
? 'border-amber-500/70 bg-amber-500/15'
|
||||
@@ -317,19 +306,16 @@ export default function AdjustmentPanel({
|
||||
})
|
||||
);
|
||||
|
||||
// Every tab carries the same RESET. It is pinned OUTSIDE the scroll row on
|
||||
// purpose: the rows keep their scroll offset across tabs, so a chip inside
|
||||
// one can sit off-screen just when it is wanted.
|
||||
const resetChip = (): ChipDef => ({
|
||||
key: 'reset-params',
|
||||
key: 'reset-all',
|
||||
label: 'RESET',
|
||||
active: false,
|
||||
onPress: () => onUpdateAdjustments({ ...lookDefaults }),
|
||||
onPress: onResetAll,
|
||||
});
|
||||
|
||||
// Pinned OUTSIDE the scroll row: the rows keep their scroll offset across
|
||||
// tabs, so a RESET sitting inside one can be scrolled off just when it is
|
||||
// wanted — after a tweak halfway along the strip.
|
||||
const resetChipFixed = () =>
|
||||
lookDirty ? <View className="pl-2">{renderChip(resetChip())}</View> : null;
|
||||
|
||||
const choiceChips = (
|
||||
keyPrefix: string,
|
||||
options: { v: string; d: string }[],
|
||||
@@ -549,28 +535,22 @@ export default function AdjustmentPanel({
|
||||
// opens its AUTO/DR100/DR200/DR400 strip above (same as the chrome
|
||||
// groups on WB).
|
||||
return (
|
||||
<View className="flex-row items-center">
|
||||
{resetChipFixed()}
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={{ flex: 1 }}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 6 }}
|
||||
>
|
||||
{paramChips(paramDefs.iq)}
|
||||
{renderChip(groupChip('dr'))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 6 }}
|
||||
>
|
||||
{paramChips(paramDefs.iq)}
|
||||
{renderChip(groupChip('dr'))}
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
case 'wb':
|
||||
return (
|
||||
<View className="flex-row items-center">
|
||||
{resetChipFixed()}
|
||||
<View>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={{ flex: 1 }}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 6 }}
|
||||
>
|
||||
{choiceChips(
|
||||
@@ -595,17 +575,13 @@ export default function AdjustmentPanel({
|
||||
|
||||
case 'filters':
|
||||
return (
|
||||
<View className="flex-row items-center">
|
||||
{resetChipFixed()}
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={{ flex: 1 }}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 8 }}
|
||||
>
|
||||
{paramChips(paramDefs.filters)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{ alignItems: 'center', paddingHorizontal: 8, paddingVertical: 8 }}
|
||||
>
|
||||
{paramChips(paramDefs.filters)}
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
case 'frame':
|
||||
@@ -798,7 +774,10 @@ export default function AdjustmentPanel({
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{chipsRow}
|
||||
<View className="flex-row items-center">
|
||||
{renderChip(resetChip())}
|
||||
<View className="flex-1">{chipsRow}</View>
|
||||
</View>
|
||||
<RecipeCreateModal
|
||||
visible={createVisible || !!editingRecipe}
|
||||
currentBase={currentBaseFilter}
|
||||
|
||||
Reference in New Issue
Block a user