diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1747ec6..e555b42 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -13,6 +13,7 @@ "windows": [ { "label": "main", + "dragDropEnabled": false, "title": "Gitty", "width": 1200, "height": 800, diff --git a/src/lib/components/IntegrationBoardView.svelte b/src/lib/components/IntegrationBoardView.svelte index 50ac53b..be3993e 100644 --- a/src/lib/components/IntegrationBoardView.svelte +++ b/src/lib/components/IntegrationBoardView.svelte @@ -6,6 +6,7 @@ import { onMount, onDestroy } from "svelte"; import { Columns3, ExternalLink, RefreshCw, Search, X } from "@lucide/svelte"; import SelectMenu from "./SelectMenu.svelte"; + import { readWorkspacePreferences, writeWorkspacePreferences } from "../workspacePreferences"; import IssueLabels from "./IssueLabels.svelte"; import IssueAssignees from "./IssueAssignees.svelte"; import { issueTone } from "../issuePresentation"; @@ -33,6 +34,7 @@ let error = ""; let query = ""; let repositoryFilter = ""; + $: if (activeUrl) writeWorkspacePreferences(`${preferenceKey}:filters:${activeUrl}`, { repositoryFilter, query }); let selected: IntegrationBoardCard | null = null; let selectedColumnId = ""; let generation = 0; @@ -115,7 +117,9 @@ const requestGeneration = ++generation; if (requestedUrl !== activeUrl) { selected = null; - repositoryFilter = ""; + const saved = readWorkspacePreferences(`${preferenceKey}:filters:${requestedUrl}`); + repositoryFilter = saved.repositoryFilter ?? ""; + query = saved.query ?? ""; board = boardCache.get(`${preferenceKey}:${requestedUrl}`) ?? null; } activeUrl = requestedUrl; diff --git a/src/lib/components/IssueCenter.svelte b/src/lib/components/IssueCenter.svelte index e9f2277..030be3b 100644 --- a/src/lib/components/IssueCenter.svelte +++ b/src/lib/components/IssueCenter.svelte @@ -14,6 +14,7 @@ import { issueTone, issueStateLabel } from "../issuePresentation"; import IntegrationBoardView from "./IntegrationBoardView.svelte"; import SelectMenu from "./SelectMenu.svelte"; + import { readWorkspacePreferences, writeWorkspacePreferences } from "../workspacePreferences"; import { listIntegrationIssues, closeIntegrationIssue, openInBrowser } from "../git"; import { configuredIntegrationSources, integrationCredentialKey } from "../integrations"; import type { GitIntegrationSettings, IntegrationIssue, StoredCredential } from "../types"; @@ -44,6 +45,7 @@ let mounted = false; let generation = 0; let lastKey = ""; + const preferenceKey = "gitty.issues.v1"; $: de = language === "de"; $: sources = configuredIntegrationSources(integrations); $: source = sources.find(item => item.id === sourceId) ?? sources[0]; @@ -56,11 +58,14 @@ issues = saved?.issues ?? []; nextCursor = saved?.nextCursor ?? null; selectedId = ""; - stateFilter = ""; - repositoryFilter = ""; + restoreFilters(sourceKey); error = ""; if (source) void loadIssues(); } + $: if (mounted && sourceKey && sourceKey === lastKey) { + writeWorkspacePreferences(preferenceKey, { sourceId: source?.id ?? "", viewMode }); + writeWorkspacePreferences(`${preferenceKey}:${sourceKey}`, { stateFilter, repositoryFilter, query }); + } $: normalizedQuery = query.trim().toLocaleLowerCase(); $: filtered = issues.filter(issue => (!repositoryFilter || issue.repositoryName === repositoryFilter) && (!stateFilter || issue.state === stateFilter) && `${issue.title} ${issue.number} ${issue.repositoryName} ${issue.author} ${issue.labels.join(" ")} ${issue.assignees.join(" ")}`.toLocaleLowerCase().includes(normalizedQuery)); @@ -71,6 +76,9 @@ $: description = selected ? plainDescription(selected) : ""; onMount(() => { + const saved = readWorkspacePreferences(preferenceKey); + sourceId = saved.sourceId ?? ""; + viewMode = saved.viewMode === "board" ? "board" : "list"; mounted = true; reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const closeDetailsOutside = (event: PointerEvent) => { @@ -87,6 +95,13 @@ }); onDestroy(() => { generation++; }); + function restoreFilters(key: string) { + const saved = readWorkspacePreferences(`${preferenceKey}:${key}`); + stateFilter = saved.stateFilter ?? ""; + repositoryFilter = saved.repositoryFilter ?? ""; + query = saved.query ?? ""; + } + async function withTimeout(promise: Promise, ms: number): Promise { let timer: ReturnType; try { diff --git a/src/lib/workspacePreferences.ts b/src/lib/workspacePreferences.ts new file mode 100644 index 0000000..2890262 --- /dev/null +++ b/src/lib/workspacePreferences.ts @@ -0,0 +1,12 @@ +// Optional UI preferences must never prevent the workspace from opening. +export function readWorkspacePreferences(key: string): Record { + try { + const value: unknown = JSON.parse(localStorage.getItem(key) ?? "{}"); + if (!value || typeof value !== "object" || Array.isArray(value)) return {}; + return Object.fromEntries(Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === "string")); + } catch { return {}; } +} + +export function writeWorkspacePreferences(key: string, value: Record) { + try { localStorage.setItem(key, JSON.stringify(value)); } catch { /* Storage may be unavailable or full. */ } +}