Organize imports: persist undo across sessions, translate UI to 23 languages
This commit is contained in:
1 parent
3a4089a5fa
commit
0f01ebf48b
24 files changed
+1717
-1662
No files matched your search
@@ -60,6 +60,7 @@ const QUICK_PICKS = 40;
|
||||
const DAYS_OPTIONS = [30, 60, 90, 180] as const;
|
||||
|
||||
const LS_KEY = "epicnexst:organizePrefs";
|
||||
const HIST_KEY = "epicnexst:organizeHistory";
|
||||
|
||||
interface OrganizePrefs {
|
||||
moveMode?: boolean;
|
||||
@@ -126,6 +127,41 @@ type MoveEntry =
|
||||
to: string;
|
||||
};
|
||||
|
||||
/** Restore a previously persisted undo history, dropping corrupted entries. */
|
||||
function readHistory(): MoveEntry[] {
|
||||
if (typeof window === "undefined") return [];
|
||||
try {
|
||||
const raw = window.localStorage.getItem(HIST_KEY);
|
||||
if (!raw) return [];
|
||||
const parsed: unknown = JSON.parse(raw);
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed.filter((entry): entry is MoveEntry => {
|
||||
if (!entry || typeof entry !== "object") return false;
|
||||
const e = entry as Partial<MoveEntry>;
|
||||
if (e.kind === "item") {
|
||||
return (
|
||||
typeof e.from === "string" &&
|
||||
typeof e.to === "string" &&
|
||||
e.item != null &&
|
||||
typeof e.item.itemId === "number"
|
||||
);
|
||||
}
|
||||
if (e.kind === "batch") {
|
||||
return (
|
||||
typeof e.to === "string" &&
|
||||
e.from != null &&
|
||||
typeof e.from === "object" &&
|
||||
Array.isArray(e.items)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} catch {
|
||||
// ignore corrupt storage
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
interface OrganizeImportsDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
@@ -252,7 +288,7 @@ export function OrganizeImportsDialog({
|
||||
setApproved({});
|
||||
setOverrides({});
|
||||
setSelection(new Set());
|
||||
setHistory([]);
|
||||
setHistory(readHistory());
|
||||
setDrag(null);
|
||||
setHoverGroup(null);
|
||||
setParentId(prefs.parentId ?? -1);
|
||||
@@ -310,6 +346,16 @@ export function OrganizeImportsDialog({
|
||||
destinationCaption,
|
||||
]);
|
||||
|
||||
// Persist the undo history so moves survive closing and reopening the dialog.
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
try {
|
||||
window.localStorage.setItem(HIST_KEY, JSON.stringify(history));
|
||||
} catch {
|
||||
// ignore quota / privacy-mode write errors
|
||||
}
|
||||
}, [history]);
|
||||
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => setDestDebounced(destQuery.trim()), 300);
|
||||
return () => clearTimeout(t);
|
||||
@@ -680,6 +726,15 @@ export function OrganizeImportsDialog({
|
||||
}
|
||||
const moved = created.reduce((sum, c) => sum + c.moved, 0);
|
||||
const added = created.reduce((sum, c) => sum + c.added, 0);
|
||||
if (created.length > 0) {
|
||||
// The server state changed, so client-side undo is no longer valid.
|
||||
setHistory([]);
|
||||
try {
|
||||
window.localStorage.removeItem(HIST_KEY);
|
||||
} catch {
|
||||
// ignore storage errors
|
||||
}
|
||||
}
|
||||
if (failedRuns > 0) {
|
||||
toast.error(
|
||||
t("errorPartial", { failed: failedRuns, total: runs.length }),
|
||||
|
||||
Reference in new issue
Block a user