From 7d0103e8a6e14063d149afa54397f92f765f5ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?hnk=C6=B6?= Date: Sun, 25 Oct 2026 12:43:08 +0900 Subject: [PATCH] fix(jira): keep the Tasks Board view selected after you leave the page (#30588) Co-authored-by: Claude Opus 5 (1M context) Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> --- .../src/components/jira-view-mode-storage.ts | 25 +++++++++++++++++++ .../task-page-jira-view-controls.tsx | 5 ++-- ...e-task-page-jira-list-state.owner.test.tsx | 22 ++++++++++++++++ .../use-task-page-jira-list-state.ts | 7 +++++- .../wsl-move/wsl-move-storage-dispositions.ts | 1 + 5 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 src/renderer/src/components/jira-view-mode-storage.ts diff --git a/src/renderer/src/components/jira-view-mode-storage.ts b/src/renderer/src/components/jira-view-mode-storage.ts new file mode 100644 index 00000000000..b6a9a3e8cf3 --- /dev/null +++ b/src/renderer/src/components/jira-view-mode-storage.ts @@ -0,0 +1,25 @@ +export type JiraViewMode = 'list' | 'board' + +// Device-local, like the Jira list columns, rather than synced through ui.set. +const STORAGE_KEY = 'orca.jira.view-mode.v1' + +export function loadJiraViewMode(): JiraViewMode { + try { + return localStorage.getItem(STORAGE_KEY) === 'board' ? 'board' : 'list' + } catch { + return 'list' + } +} + +export function saveJiraViewMode(mode: JiraViewMode): void { + try { + // Storing only the non-default keeps a future default change from being pinned by old devices. + if (mode === 'board') { + localStorage.setItem(STORAGE_KEY, 'board') + } else { + localStorage.removeItem(STORAGE_KEY) + } + } catch { + // The live view stays usable when browser storage is unavailable or full. + } +} diff --git a/src/renderer/src/components/task-page-jira-view-controls.tsx b/src/renderer/src/components/task-page-jira-view-controls.tsx index f880da94593..c341711fbbe 100644 --- a/src/renderer/src/components/task-page-jira-view-controls.tsx +++ b/src/renderer/src/components/task-page-jira-view-controls.tsx @@ -3,10 +3,11 @@ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' import { Switch } from '@/components/ui/switch' import { Label } from '@/components/ui/label' import { translate } from '@/i18n/i18n' +import type { JiraViewMode } from './jira-view-mode-storage' type Props = { - mode: 'list' | 'board' - onModeChange: (mode: 'list' | 'board') => void + mode: JiraViewMode + onModeChange: (mode: JiraViewMode) => void currentSprint: boolean onCurrentSprintChange: (checked: boolean) => void sprintDisabled?: boolean diff --git a/src/renderer/src/components/use-task-page-jira-list-state.owner.test.tsx b/src/renderer/src/components/use-task-page-jira-list-state.owner.test.tsx index 75bc5d04da8..966ad1b2190 100644 --- a/src/renderer/src/components/use-task-page-jira-list-state.owner.test.tsx +++ b/src/renderer/src/components/use-task-page-jira-list-state.owner.test.tsx @@ -165,3 +165,25 @@ describe('Jira priority list source owner', () => { } ) }) + +describe('Jira Tasks view mode', () => { + // Leaving Tasks unmounts the hook, so each case reopens it with a fresh render. + function openTasks() { + return renderHook(() => useTaskPageJiraListState(fixture())).result + } + + it('reopens on the board after the view mode was switched', async () => { + const first = openTasks() + await act(async () => first.current.setJiraViewMode('board')) + cleanup() + expect(openTasks().current.jiraViewMode).toBe('board') + }) + + it('reopens on the list once the view mode is switched back', async () => { + const first = openTasks() + await act(async () => first.current.setJiraViewMode('board')) + await act(async () => first.current.setJiraViewMode('list')) + cleanup() + expect(openTasks().current.jiraViewMode).toBe('list') + }) +}) diff --git a/src/renderer/src/components/use-task-page-jira-list-state.ts b/src/renderer/src/components/use-task-page-jira-list-state.ts index 97eaff52f23..b1e877c62cd 100644 --- a/src/renderer/src/components/use-task-page-jira-list-state.ts +++ b/src/renderer/src/components/use-task-page-jira-list-state.ts @@ -21,6 +21,7 @@ import type { } from './jira-issue-sorter' import { jiraListPrioritiesForTarget } from '@/runtime/runtime-jira-client' import { loadJiraLinkedView, saveJiraLinkedView } from './jira-linked-view-storage' +import { loadJiraViewMode, saveJiraViewMode, type JiraViewMode } from './jira-view-mode-storage' import { fallbackJiraSortColumn, loadJiraListColumnIds, @@ -103,7 +104,11 @@ export function useTaskPageJiraListState(model: TaskPageLinearViewStateModel) { items: JiraStatusCatalogItem[] } | null>(null) const [activeJiraPreset, setActiveJiraPreset] = useState('assigned') - const [jiraViewMode, setJiraViewMode] = useState<'list' | 'board'>('list') + const [jiraViewMode, setJiraViewModeState] = useState(loadJiraViewMode) + const setJiraViewMode = useCallback((mode: JiraViewMode) => { + setJiraViewModeState(mode) + saveJiraViewMode(mode) + }, []) const [jiraCurrentSprint, setJiraCurrentSprint] = useState(false) const [jiraRefreshNonce, setJiraRefreshNonce] = useState(0) const [jiraProjectStatusOrder, setJiraProjectStatusOrder] = useState<{ diff --git a/src/shared/wsl-move/wsl-move-storage-dispositions.ts b/src/shared/wsl-move/wsl-move-storage-dispositions.ts index cbb9be96672..f62e38626e2 100644 --- a/src/shared/wsl-move/wsl-move-storage-dispositions.ts +++ b/src/shared/wsl-move/wsl-move-storage-dispositions.ts @@ -59,6 +59,7 @@ export const LOCAL_STORAGE_MODULE_DISPOSITIONS: Record