diff --git a/src/renderer/src/components/TaskPage.tsx b/src/renderer/src/components/TaskPage.tsx index 32d84a80179..83841cc3d88 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 3922b471ad9..f1b16f6eeba 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 791ea2948bd..2dc991907cc 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 65b84969ed2..70499698df1 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'] }