diff --git a/src/components/RenamePanel.tsx b/src/components/RenamePanel.tsx index fe9bef2..d580b28 100644 --- a/src/components/RenamePanel.tsx +++ b/src/components/RenamePanel.tsx @@ -572,6 +572,19 @@ export default function RenamePanel() { expressionRef.current = expression; }, [expression]); + // Set true when a real persisted scheme is restored from disk below. The three + // "showcase the next field" auto-upgrade checkpoints further down (addRealFiles, + // runMatching, runFetch) each compare expressionRef.current against an exact + // milestone-constant string to infer "the user hasn't customized past this point + // yet" - a heuristic that only holds for a fresh in-memory session. A restored + // scheme can legitimately equal one of those constants (e.g. a user whose saved + // scheme IS the sample/fetched expression), and without this guard the very next + // checkpoint would silently stomp their correctly-restored scheme back to a + // "simpler" milestone the moment files are loaded/matched/fetched. Once a real + // value has been restored, none of the onboarding auto-upgrades should ever fire + // again for this session, regardless of what the restored text happens to equal. + const restoredCustomSchemeRef = useRef(false); + // Restores the naming-scheme editor's text from the last time the app was open (see // `src-tauri/src/settings.rs`'s `last_naming_scheme`) - without this, the editor // always reset to SAMPLE_EXPRESSION on every launch, silently discarding whatever @@ -582,7 +595,10 @@ export default function RenamePanel() { useEffect(() => { invoke("get_last_naming_scheme") .then((saved) => { - if (saved && saved.trim()) setExpression(saved); + if (saved && saved.trim()) { + setExpression(saved); + restoredCustomSchemeRef.current = true; + } }) .catch(() => {}) .finally(() => setSchemeLoaded(true)); @@ -642,8 +658,9 @@ export default function RenamePanel() { if (entries.length === 0) return; // Only auto-switch the naming scheme the first time real files replace the demo, - // and only if the user hasn't already customized it. - if (expressionRef.current === SAMPLE_EXPRESSION) { + // and only if the user hasn't already customized it (including a scheme restored + // from a previous session - see restoredCustomSchemeRef above). + if (expressionRef.current === SAMPLE_EXPRESSION && !restoredCustomSchemeRef.current) { setExpression(REAL_FILE_EXPRESSION); } @@ -877,9 +894,11 @@ export default function RenamePanel() { // Showcase the newly-populated fields, but only the first time and only if the // user hasn't already customized the naming scheme away from the plain-filename - // default. Movies deliberately don't drive this auto-upgrade - a single "best - // expression" heuristic doesn't generalize across a batch mixing TV and movies. - if (expressionRef.current === REAL_FILE_EXPRESSION && matchedTv > 0) { + // default (including a scheme restored from a previous session - see + // restoredCustomSchemeRef above). Movies deliberately don't drive this + // auto-upgrade - a single "best expression" heuristic doesn't generalize across a + // batch mixing TV and movies. + if (expressionRef.current === REAL_FILE_EXPRESSION && matchedTv > 0 && !restoredCustomSchemeRef.current) { setExpression(MATCHED_EXPRESSION); } @@ -1340,8 +1359,10 @@ export default function RenamePanel() { ); // Showcase the episode title field, but only the first time and only if the user - // hasn't already customized the naming scheme away from the Match-set default. - if (expressionRef.current === MATCHED_EXPRESSION && found > 0) { + // hasn't already customized the naming scheme away from the Match-set default + // (including a scheme restored from a previous session - see + // restoredCustomSchemeRef above). + if (expressionRef.current === MATCHED_EXPRESSION && found > 0 && !restoredCustomSchemeRef.current) { setExpression(FETCHED_EXPRESSION); }