import { formatBandFreqDisplay, formatBandGainDisplay, formatBandQDisplay, eqInterp, } from "./utils"; import type { PeqEqUi } from "./constants"; import type { PeqUndoBand, PeqUndoSnapshot } from "./hooks/usePeqUndoHistory"; export type PeqUndoDiffItem = { /** Display label already localized where possible */ text: string; }; function formatAuto(on: boolean, eqUi: PeqEqUi): string { return on ? (eqUi.undoAutoOn ?? "ON") : (eqUi.undoAutoOff ?? "OFF"); } function bandParamLabel( param: "freq" | "gain" | "q" | "type", eqUi: PeqEqUi, ): string { switch (param) { case "freq": return eqUi.undoParamFreq ?? "FREQ"; case "gain": return eqUi.undoParamGain ?? "GAIN"; case "q": return eqUi.undoParamQ ?? "Q"; case "type": return eqUi.undoParamType ?? "Filter"; } } function formatBandValue( param: "freq" | "gain" | "q" | "type", band: PeqUndoBand, ): string { switch (param) { case "freq": return formatBandFreqDisplay(band.freq); case "gain": return formatBandGainDisplay(band.gain); case "q": return formatBandQDisplay(band.q); case "type": return String(band.type); } } function diffBands( fromBands: PeqUndoBand[], toBands: PeqUndoBand[], mode: "A" | "B", eqUi: PeqEqUi, ): PeqUndoDiffItem[] { const items: PeqUndoDiffItem[] = []; const len = Math.max(fromBands.length, toBands.length); for (let i = 0; i < len; i++) { const from = fromBands[i]; const to = toBands[i]; if (!from || !to) continue; const params = ["freq", "gain", "q", "type"] as const; for (const param of params) { const fromVal = formatBandValue(param, from); const toVal = formatBandValue(param, to); if (fromVal === toVal) continue; const text = eqInterp(eqUi.undoBandParam, { mode, band: i + 1, param: bandParamLabel(param, eqUi), from: fromVal, to: toVal, }) || `${mode} Band ${i + 1} ${bandParamLabel(param, eqUi)} ${fromVal} → ${toVal}`; items.push({ text }); } } return items; } /** * Diff current state (before clicking Undo) vs restored snapshot (after Undo). * `from` is the value user just undid away from; `to` is the restored value. */ export function diffPeqUndoSnapshots( current: PeqUndoSnapshot, restored: PeqUndoSnapshot, eqUi: PeqEqUi, ): PeqUndoDiffItem[] { const items: PeqUndoDiffItem[] = []; if (current.abMode !== restored.abMode) { items.push({ text: eqInterp(eqUi.undoSimpleParam, { param: eqUi.undoParamAbMode ?? "A/B", from: current.abMode, to: restored.abMode, }) || `A/B ${current.abMode} → ${restored.abMode}`, }); } if (current.preamp !== restored.preamp) { items.push({ text: eqInterp(eqUi.undoSimpleParam, { param: eqUi.undoParamPreamp ?? "Preamp", from: formatBandGainDisplay(current.preamp), to: formatBandGainDisplay(restored.preamp), }) || `Preamp ${formatBandGainDisplay(current.preamp)} → ${formatBandGainDisplay(restored.preamp)}`, }); } if (current.autoPre !== restored.autoPre) { items.push({ text: eqInterp(eqUi.undoSimpleParam, { param: eqUi.undoParamAuto ?? "AUTO", from: formatAuto(current.autoPre === 1, eqUi), to: formatAuto(restored.autoPre === 1, eqUi), }) || `AUTO ${formatAuto(current.autoPre === 1, eqUi)} → ${formatAuto(restored.autoPre === 1, eqUi)}`, }); } items.push( ...diffBands(current.bandsByMode.A, restored.bandsByMode.A, "A", eqUi), ); items.push( ...diffBands(current.bandsByMode.B, restored.bandsByMode.B, "B", eqUi), ); return items; } /** Build toast body for an undo action. */ export function formatPeqUndoToast( current: PeqUndoSnapshot, restored: PeqUndoSnapshot, eqUi: PeqEqUi, ): string { const diffs = diffPeqUndoSnapshots(current, restored, eqUi); if (diffs.length === 0) { return eqUi.toastUndoEmpty ?? eqUi.undo ?? "Undo"; } if (diffs.length === 1) { return ( eqInterp(eqUi.toastUndo, { detail: diffs[0]!.text }) || `Undone: ${diffs[0]!.text}` ); } const summary = diffs .slice(0, 3) .map(d => d.text) .join(eqUi.undoDetailSep ?? "; "); const extra = diffs.length > 3 ? eqInterp(eqUi.undoMoreSuffix, { count: diffs.length - 3 }) || ` (+${diffs.length - 3})` : ""; const detail = eqInterp(eqUi.undoMultiple, { count: diffs.length, summary: `${summary}${extra}`, }) || `${diffs.length} changes: ${summary}${extra}`; return eqInterp(eqUi.toastUndo, { detail }) || `Undone: ${detail}`; }