orca/docs/design/persist-task-source-prefere...

5.5 KiB

Design: Persist Task Source Preference (GitHub / Linear)

Problem

The Tasks button in the sidebar always defaults to GitHub when clicked without an explicit source. If a user switches to Linear inside the Task page, that choice is lost when they navigate away and return. Users who primarily use Linear must switch every time.

Goal

Remember which task source (GitHub or Linear) the user last selected and default to it the next time they open the Tasks page without an explicit source override.

Current Behavior

  1. SidebarNav.tsx — The main Tasks button calls openTaskPage() with no arguments. The small GitHub/Linear icons call openTaskPage({ taskSource: 'github' }) or openTaskPage({ taskSource: 'linear' }) respectively.

  2. ui.ts (store)openTaskPage(data = {}) stores data into taskPageData. No taskSource field is persisted.

  3. TaskPage.tsx (line 597) — Initializes source with useState<TaskSource>(pageData.taskSource ?? 'github'). The hardcoded 'github' fallback is the root cause.

  4. TaskPage.tsx (line 1259) — In-page source toggle calls setTaskSource(source.id) — local state only, never persisted.

Proposed Solution

Piggyback on the existing GlobalSettings persistence pattern already used by defaultTaskViewPreset and defaultRepoSelection.

Changes

1. src/shared/types.ts — Add setting field

Add defaultTaskSource: 'github' | 'linear' to the GlobalSettings type, alongside the existing defaultTaskViewPreset field (~line 818). Use the inline union (matching the existing taskPageData pattern in ui.ts) rather than importing the file-local TaskSource alias from TaskPage.

1b. src/shared/constants.ts — Add default value

Add defaultTaskSource: 'github' to the GlobalSettings defaults object (adjacent to defaultTaskViewPreset: 'all' ~line 158).

2. src/renderer/src/components/TaskPage.tsx — Read and write the preference

Read (line 597): Change the fallback from the hardcoded 'github' to the persisted setting:

// Before
const [taskSource, setTaskSource] = useState<TaskSource>(pageData.taskSource ?? 'github')

// After
const defaultTaskSource = settings?.defaultTaskSource ?? 'github'
const [taskSource, setTaskSource] = useState<TaskSource>(pageData.taskSource ?? defaultTaskSource)

Sync effect (after line 606): settings may be null on first render (async hydration). Add a sync effect so the persisted preference applies once settings arrive, mirroring the existing pageData.taskSource sync pattern on lines 602-606:

// Why: settings load asynchronously — the useState initializer may
// capture null settings on fast navigation. Sync once settings arrive,
// but only when no explicit source was passed via sidebar icon click.
useEffect(() => {
  if (!pageData.taskSource && settings?.defaultTaskSource) {
    setTaskSource(settings.defaultTaskSource)
  }
}, [settings?.defaultTaskSource, pageData.taskSource])

Write (line 1259): When the user toggles the source inside the page, persist the choice:

// Before
onClick={() => setTaskSource(source.id)}

// After
onClick={() => {
  setTaskSource(source.id)
  void updateSettings({ defaultTaskSource: source.id }).catch(() => {
    toast.error('Failed to save default task source.')
  })
}}

This mirrors the exact pattern used by handleSetDefaultTaskPreset (line 859-868).

3. Settings initialization / migration

Wherever GlobalSettings defaults are constructed (the main process settings loader), add defaultTaskSource: 'github' so existing users get the current behavior with no migration needed. The field is optional in the type — missing means 'github'.

No changes needed

  • SidebarNav.tsx — No changes. The main Tasks button continues to call openTaskPage() with no taskSource. The explicit GitHub/Linear icons continue to pass their source. TaskPage handles the default resolution.
  • ui.ts store — No changes. taskPageData remains ephemeral. The preference is a setting, not UI state.
  • Prefetch logic — The prefetch in openTaskPage and SidebarNav only warms GitHub work items. This is fine — Linear fetches are fast and don't benefit from the same prefetch pattern. Optionally, we could skip the GitHub prefetch when the saved source is Linear, but that's a separate optimization.

Behavior Matrix

Action Result
Click main Tasks button (no saved pref) Opens GitHub (backward-compatible default)
Click main Tasks button (saved pref = linear) Opens Linear
Click GitHub icon in sidebar Opens GitHub (does not change saved preference)
Click Linear icon in sidebar Opens Linear (does not change saved preference)
Toggle source inside TaskPage Switches view, saves new preference
Fresh install / no setting Defaults to 'github'

Scope

  • ~20 lines of code across 3 files (types.ts, constants.ts, TaskPage.tsx)
  • No new IPC channels
  • No migrations
  • No UI changes — only behavior change is remembering the last choice

Resolved Questions

  1. Should clicking the sidebar GitHub/Linear icons also persist the preference? No — sidebar icons are pure navigation ("go to GitHub tasks" / "go to Linear tasks"). Persisting from a one-off exploratory click would surprise users. Only the in-page toggle persists, keeping the mental model clean: sidebar icons = navigate, in-page toggle = set preference.

  2. Should we skip GitHub prefetch when saved source is Linear? Deferred to a follow-up. The prefetch is cheap and doesn't hurt.