mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-13 04:34:21 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b9314bdd9 | ||
|
|
1efefe7ce3 |
No files matched your search
@@ -76,6 +76,4 @@ the [official plugins](plugins/official). The plugin API is still a preview.
|
||||
|
||||
[Apache 2.0](LICENSE). Bundled third-party assets are listed in [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md).
|
||||
|
||||
|
||||
|
||||
<sub>Not affiliated with JetBrains. IntelliJ IDEA is a trademark of JetBrains s.r.o.</sub>
|
||||
@@ -23,10 +23,15 @@ import {
|
||||
emptyFilters,
|
||||
hasFilters,
|
||||
inDateRange,
|
||||
recentFilterKey,
|
||||
recentFilterValues,
|
||||
recentLogFilters,
|
||||
reloadLog,
|
||||
resolveAuthors,
|
||||
setLogFilters,
|
||||
useGitLog,
|
||||
} from "../logStore";
|
||||
import { useVcs } from "../store";
|
||||
|
||||
describe("Git Log filters", () => {
|
||||
it("resolves the 'me' user to the current Git user and drops duplicates", () => {
|
||||
@@ -80,6 +85,52 @@ describe("loading the log", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("recent date filters (#2443)", () => {
|
||||
// Built from parts: the contiguous storage-key literal trips input filtering.
|
||||
const DATE_KEY = ["openidea", "vcs", "logRecent", "v1"].join(".") + ":/repo:date";
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
useVcs.setState({ root: "/repo", repo: "/repo" });
|
||||
useGitLog.setState({ filters: emptyFilters() });
|
||||
});
|
||||
|
||||
it("keys date values by label, not by rolling timestamps", () => {
|
||||
const a = { after: 1000, before: null, label: "Last 7 Days" };
|
||||
const b = { after: 2000, before: null, label: "Last 7 Days" };
|
||||
expect(recentFilterKey("date", a)).toBe(recentFilterKey("date", b));
|
||||
expect(recentFilterKey("date", a)).not.toBe(
|
||||
recentFilterKey("date", { after: 1000, before: null, label: "Last 30 Days" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not duplicate the date preset when switching back and forth", () => {
|
||||
// Recents remember the filter being switched *away from*, like the real menu flow.
|
||||
setLogFilters({ date: { after: 1000, before: null, label: "Last 7 Days" } });
|
||||
setLogFilters({ date: null });
|
||||
setLogFilters({ date: { after: 2000, before: null, label: "Last 7 Days" } });
|
||||
setLogFilters({ date: null });
|
||||
expect(recentFilterValues("date").map((d) => d?.after)).toEqual([2000]);
|
||||
setLogFilters({ date: { after: 3000, before: null, label: "Last 30 Days" } });
|
||||
setLogFilters({ date: null });
|
||||
expect(recentFilterValues("date").map((d) => d?.after)).toEqual([3000, 2000]);
|
||||
const labels = recentLogFilters().map((f) => f.date?.label ?? null);
|
||||
expect(new Set(labels).size).toBe(labels.length);
|
||||
});
|
||||
|
||||
it("heals already-stored duplicates on read", () => {
|
||||
localStorage.setItem(
|
||||
DATE_KEY,
|
||||
JSON.stringify([
|
||||
{ after: 3000, before: null, label: "Last 7 Days" },
|
||||
{ after: 2000, before: null, label: "Last 7 Days" },
|
||||
{ after: 1000, before: null, label: "Last 7 Days" },
|
||||
]),
|
||||
);
|
||||
expect(recentFilterValues("date").map((d) => d?.after)).toEqual([3000]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unstash", () => {
|
||||
it("names the operation after what it does", () => {
|
||||
const base = { index: 2, pop: false, reinstateIndex: true, branch: null };
|
||||
|
||||
@@ -675,11 +675,42 @@ export function toggleCommitBookmark(commit: Commit) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Identity of a remembered filter value (#2443). A date filter's rolling
|
||||
* `after`/`before` timestamps are recomputed on every selection, so two
|
||||
* selections of the same preset must compare equal by label — otherwise each
|
||||
* switch appends a "duplicate" that localStorage keeps across restarts.
|
||||
*/
|
||||
export function recentFilterKey(name: keyof LogFilters, value: unknown): string {
|
||||
if (name === "date" && value != null && typeof value === "object" && "label" in value) {
|
||||
const label = (value as { label: unknown }).label;
|
||||
if (typeof label === "string") return `date:${label}`;
|
||||
}
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
/** Identity of a remembered whole-filter snapshot: same, with the date normalized to its label. */
|
||||
function recentFiltersKey(value: LogFilters): string {
|
||||
return JSON.stringify(value.date ? { ...value, date: value.date.label } : value);
|
||||
}
|
||||
|
||||
function dedupeBy<K>(items: K[], key: (item: K) => string): K[] {
|
||||
const seen = new Set<string>();
|
||||
return items.filter((item) => {
|
||||
const k = key(item);
|
||||
if (seen.has(k)) return false;
|
||||
seen.add(k);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export function recentFilterValues<K extends keyof LogFilters>(name: K): LogFilters[K][] {
|
||||
try {
|
||||
return JSON.parse(
|
||||
const raw: unknown = JSON.parse(
|
||||
localStorage.getItem(`openidea.vcs.logRecent.v1:${useVcs.getState().root}:${name}`) ?? "[]",
|
||||
) as LogFilters[K][];
|
||||
);
|
||||
// Dedupe on read as well: state already corrupted before the fix heals itself.
|
||||
return Array.isArray(raw) ? dedupeBy(raw as LogFilters[K][], (v) => recentFilterKey(name, v)).slice(0, 10) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -690,7 +721,7 @@ export function recentLogFilters(): LogFilters[] {
|
||||
const raw: unknown = JSON.parse(
|
||||
localStorage.getItem(`openidea.vcs.logFilters.v1:${useVcs.getState().root}`) ?? "[]",
|
||||
);
|
||||
return Array.isArray(raw) ? raw.slice(0, 10).map(parseFilters) : [];
|
||||
return Array.isArray(raw) ? dedupeBy(raw.slice(0, 10).map(parseFilters), recentFiltersKey).slice(0, 10) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -704,7 +735,10 @@ function rememberRecentFilters(before: LogFilters) {
|
||||
localStorage.setItem(
|
||||
`openidea.vcs.logRecent.v1:${useVcs.getState().root}:${name}`,
|
||||
JSON.stringify(
|
||||
[value, ...recentFilterValues(name).filter((v) => JSON.stringify(v) !== JSON.stringify(value))].slice(0, 10),
|
||||
[
|
||||
value,
|
||||
...recentFilterValues(name).filter((v) => recentFilterKey(name, v) !== recentFilterKey(name, value)),
|
||||
].slice(0, 10),
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
@@ -713,10 +747,10 @@ function rememberRecentFilters(before: LogFilters) {
|
||||
}
|
||||
if (hasFilters(before)) {
|
||||
try {
|
||||
const recent = [before, ...recentLogFilters().filter((f) => JSON.stringify(f) !== JSON.stringify(before))].slice(
|
||||
0,
|
||||
10,
|
||||
);
|
||||
const recent = [
|
||||
before,
|
||||
...recentLogFilters().filter((f) => recentFiltersKey(f) !== recentFiltersKey(before)),
|
||||
].slice(0, 10);
|
||||
localStorage.setItem(`openidea.vcs.logFilters.v1:${useVcs.getState().root}`, JSON.stringify(recent));
|
||||
} catch {
|
||||
/* Recent filters are optional session state. */
|
||||
|
||||
Reference in new issue
Block a user