From 115eccdba6b3fb2fda09b172115dc451793aedf8 Mon Sep 17 00:00:00 2001 From: Jonathan Avila Date: Wed, 20 May 2026 21:10:35 -0400 Subject: [PATCH] Fix task source fallback for Linear integration (#2440) * Fix task source fallback for Linear integration * Ignore malformed saved task provider defaults Co-authored-by: Orca --------- Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Co-authored-by: Orca --- src/renderer/src/components/TaskPage.tsx | 21 +++++--- .../src/components/sidebar/SidebarNav.tsx | 19 +++++-- src/shared/task-providers.test.ts | 52 +++++++++++++++++++ src/shared/task-providers.ts | 23 ++++++-- 4 files changed, 101 insertions(+), 14 deletions(-) diff --git a/src/renderer/src/components/TaskPage.tsx b/src/renderer/src/components/TaskPage.tsx index 32d84a801..83841cc3d 100644 --- a/src/renderer/src/components/TaskPage.tsx +++ b/src/renderer/src/components/TaskPage.tsx @@ -1798,13 +1798,23 @@ export default function TaskPage(): React.JSX.Element { () => normalizeVisibleTaskProviders(settings?.visibleTaskProviders), [settings?.visibleTaskProviders] ) + const defaultTaskSource = settings?.defaultTaskSource ?? 'github' const visibleTaskProviders = useMemo( () => - filterAvailableTaskProviders(preferredVisibleTaskProviders, { - gitlabInstalled: preflightStatus?.glab?.installed === true, - linearConnected: linearStatus.connected === true - }), - [linearStatus.connected, preferredVisibleTaskProviders, preflightStatus?.glab?.installed] + filterAvailableTaskProviders( + preferredVisibleTaskProviders, + { + gitlabInstalled: preflightStatus?.glab?.installed === true, + linearConnected: linearStatus.connected === true + }, + defaultTaskSource + ), + [ + defaultTaskSource, + linearStatus.connected, + preferredVisibleTaskProviders, + preflightStatus?.glab?.installed + ] ) const visibleSourceOptions = useMemo( () => SOURCE_OPTIONS.filter((source) => visibleTaskProviders.includes(source.id)), @@ -1819,7 +1829,6 @@ export default function TaskPage(): React.JSX.Element { const defaultTaskViewPreset = normalizeGitHubTaskPreset(settings?.defaultTaskViewPreset ?? 'all') const initialTaskQuery = getTaskPresetQuery(defaultTaskViewPreset) - const defaultTaskSource = settings?.defaultTaskSource ?? 'github' const preferredTaskSource = pageData.taskSource ?? defaultTaskSource const [taskSource, setTaskSource] = useState( resolveVisibleTaskProvider(preferredTaskSource, visibleTaskProviders) diff --git a/src/renderer/src/components/sidebar/SidebarNav.tsx b/src/renderer/src/components/sidebar/SidebarNav.tsx index 3922b471a..f1b16f6ee 100644 --- a/src/renderer/src/components/sidebar/SidebarNav.tsx +++ b/src/renderer/src/components/sidebar/SidebarNav.tsx @@ -49,11 +49,20 @@ const SidebarNav = React.memo(function SidebarNav() { ) const visibleTaskProviders = React.useMemo( () => - filterAvailableTaskProviders(preferredVisibleTaskProviders, { - gitlabInstalled: preflightStatus?.glab?.installed === true, - linearConnected: linearStatus.connected === true - }), - [linearStatus.connected, preferredVisibleTaskProviders, preflightStatus?.glab?.installed] + filterAvailableTaskProviders( + preferredVisibleTaskProviders, + { + gitlabInstalled: preflightStatus?.glab?.installed === true, + linearConnected: linearStatus.connected === true + }, + defaultTaskSource + ), + [ + defaultTaskSource, + linearStatus.connected, + preferredVisibleTaskProviders, + preflightStatus?.glab?.installed + ] ) const resolvedDefaultTaskSource = React.useMemo( () => resolveVisibleTaskProvider(defaultTaskSource, visibleTaskProviders), diff --git a/src/shared/task-providers.test.ts b/src/shared/task-providers.test.ts index 791ea2948..2dc991907 100644 --- a/src/shared/task-providers.test.ts +++ b/src/shared/task-providers.test.ts @@ -30,6 +30,58 @@ describe('task providers', () => { ).toEqual(['github', 'linear']) }) + it('keeps an available saved default visible when provider visibility drifted', () => { + expect( + filterAvailableTaskProviders( + ['linear'], + { + gitlabInstalled: false, + linearConnected: true + }, + 'github' + ) + ).toEqual(['github', 'linear']) + }) + + it('preserves intentionally narrowed providers when the saved default matches them', () => { + expect( + filterAvailableTaskProviders( + ['linear'], + { + gitlabInstalled: false, + linearConnected: true + }, + 'linear' + ) + ).toEqual(['linear']) + }) + + it('does not restore an unavailable saved default', () => { + expect( + filterAvailableTaskProviders( + ['linear'], + { + gitlabInstalled: false, + linearConnected: true + }, + 'gitlab' + ) + ).toEqual(['linear']) + }) + + it('ignores malformed saved defaults when every preferred provider is unavailable', () => { + expect( + filterAvailableTaskProviders( + ['gitlab'], + { + gitlabInstalled: false, + linearConnected: true + }, + 'jira' as never + ) + ).toEqual(['github']) + }) + it('falls back to GitHub when every preferred provider is unavailable', () => { expect( filterAvailableTaskProviders(['gitlab', 'linear'], { diff --git a/src/shared/task-providers.ts b/src/shared/task-providers.ts index 65b84969e..70499698d 100644 --- a/src/shared/task-providers.ts +++ b/src/shared/task-providers.ts @@ -31,9 +31,12 @@ export type TaskProviderAvailability = { export function filterAvailableTaskProviders( visibleProviders: readonly TaskProvider[], - availability: TaskProviderAvailability + availability: TaskProviderAvailability, + preferredProvider?: TaskProvider | null ): TaskProvider[] { - const available = visibleProviders.filter((provider) => { + const normalizedPreferredProvider = + preferredProvider && TASK_PROVIDER_SET.has(preferredProvider) ? preferredProvider : null + const isProviderAvailable = (provider: TaskProvider): boolean => { if (provider === 'github') { return true } @@ -41,7 +44,21 @@ export function filterAvailableTaskProviders( return availability.gitlabInstalled } return availability.linearConnected - }) + } + + const available = visibleProviders.filter(isProviderAvailable) + + // Why: older or drifted settings can hide the saved default while another + // provider becomes available. Keep that default reachable after hydration. + if ( + normalizedPreferredProvider && + isProviderAvailable(normalizedPreferredProvider) && + !available.includes(normalizedPreferredProvider) + ) { + return TASK_PROVIDERS.filter( + (provider) => provider === normalizedPreferredProvider || available.includes(provider) + ) + } return available.length > 0 ? available : ['github'] }