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 DAYS_OPTIONS = [30, 60, 90, 180] as const;
|
||||||
|
|
||||||
const LS_KEY = "epicnexst:organizePrefs";
|
const LS_KEY = "epicnexst:organizePrefs";
|
||||||
|
const HIST_KEY = "epicnexst:organizeHistory";
|
||||||
|
|
||||||
interface OrganizePrefs {
|
interface OrganizePrefs {
|
||||||
moveMode?: boolean;
|
moveMode?: boolean;
|
||||||
@@ -126,6 +127,41 @@ type MoveEntry =
|
|||||||
to: string;
|
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 {
|
interface OrganizeImportsDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
@@ -252,7 +288,7 @@ export function OrganizeImportsDialog({
|
|||||||
setApproved({});
|
setApproved({});
|
||||||
setOverrides({});
|
setOverrides({});
|
||||||
setSelection(new Set());
|
setSelection(new Set());
|
||||||
setHistory([]);
|
setHistory(readHistory());
|
||||||
setDrag(null);
|
setDrag(null);
|
||||||
setHoverGroup(null);
|
setHoverGroup(null);
|
||||||
setParentId(prefs.parentId ?? -1);
|
setParentId(prefs.parentId ?? -1);
|
||||||
@@ -310,6 +346,16 @@ export function OrganizeImportsDialog({
|
|||||||
destinationCaption,
|
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(() => {
|
useEffect(() => {
|
||||||
const t = setTimeout(() => setDestDebounced(destQuery.trim()), 300);
|
const t = setTimeout(() => setDestDebounced(destQuery.trim()), 300);
|
||||||
return () => clearTimeout(t);
|
return () => clearTimeout(t);
|
||||||
@@ -680,6 +726,15 @@ export function OrganizeImportsDialog({
|
|||||||
}
|
}
|
||||||
const moved = created.reduce((sum, c) => sum + c.moved, 0);
|
const moved = created.reduce((sum, c) => sum + c.moved, 0);
|
||||||
const added = created.reduce((sum, c) => sum + c.added, 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) {
|
if (failedRuns > 0) {
|
||||||
toast.error(
|
toast.error(
|
||||||
t("errorPartial", { failed: failedRuns, total: runs.length }),
|
t("errorPartial", { failed: failedRuns, total: runs.length }),
|
||||||
|
|||||||
+74
-74
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+70
-70
File diff suppressed because it is too large.
Load diff
+69
-69
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+70
-70
File diff suppressed because it is too large.
Load diff
+73
-73
File diff suppressed because it is too large.
Load diff
+66
-66
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+70
-70
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+71
-71
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+69
-69
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
+71
-71
File diff suppressed because it is too large.
Load diff
+70
-70
File diff suppressed because it is too large.
Load diff
+74
-74
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user