fix(mobile): load the repo list in GitHub Project mode

loadTasks returned before loadRepos() when the GitHub mode was `project`,
so `repos` stayed empty, `hostedRepos` was empty, and
filterGitHubProjectRowsForRepos dropped every board row. Visiting any
other mode populated `repos` as a side effect, which is why tapping
Issues once and coming back appeared to fix it. The active mode syncs
from the desktop, so a fresh launch can land straight in Projects and
show "No project items" for a board that has items.

Move the project-mode short-circuit below the repo list load. Project
mode still fetches no issues/PRs; it just no longer skips the repo list
it needs to match rows against.

Fixes #12966
This commit is contained in:
Jinwoo-H
2026-08-06 22:02:33 -04:00
parent e68831f32c
commit 0ab4e4f2b9
2 changed files with 58 additions and 4 deletions
+6 -4
View File
@@ -3257,10 +3257,6 @@ export default function MobileTasksScreen() {
setGithubSourceErrors([])
setGithubSourceFallbacks([])
}
if (provider === 'github' && githubMode === 'project') {
setItems([])
return
}
if (provider === 'linear' && !linearConnected) {
setItems([])
return
@@ -3269,6 +3265,12 @@ export default function MobileTasksScreen() {
if (!isCurrent()) {
return
}
// Why: project mode fetches no work items, but its rows are matched against
// the repo list, so it must not return before loadRepos() has run.
if (provider === 'github' && githubMode === 'project') {
setItems([])
return
}
if (provider === 'github' || provider === 'gitlab') {
const supportedRepos = currentRepos.filter(isHostedTaskRepo)
const queriedRepos =
@@ -0,0 +1,52 @@
import { readFileSync } from 'node:fs'
import { describe, expect, it } from 'vitest'
const source = readFileSync(new URL('../../app/h/[hostId]/tasks.tsx', import.meta.url), 'utf8')
// loadTasks is the only path that calls loadRepos, so the ordering inside it is
// what decides whether Project rows have repos to match against.
function loadTasksBody(): string {
const start = source.indexOf('const loadTasks = useCallback(')
expect(start, 'loadTasks must exist in the tasks route').toBeGreaterThan(-1)
const end = source.indexOf('const connectLinearAccount = useCallback(', start)
expect(end, 'loadTasks must be followed by connectLinearAccount').toBeGreaterThan(start)
return source.slice(start, end)
}
function offsets(): {
repoLoad: number
projectReturn: number
workItemFetch: number
} {
const body = loadTasksBody()
return {
repoLoad: body.indexOf('await loadRepos()'),
projectReturn: body.indexOf("provider === 'github' && githubMode === 'project'"),
workItemFetch: body.indexOf("provider === 'github' || provider === 'gitlab'")
}
}
describe('mobile GitHub Project repo list loading', () => {
it('keeps loadRepos on the loadTasks path', () => {
expect(offsets().repoLoad, 'loadTasks must still load the repo list').toBeGreaterThan(-1)
})
// Regression: project mode returned before loadRepos(), so hostedRepos stayed
// empty, filterGitHubProjectRowsForRepos matched nothing, and the board read
// "No project items" until another mode populated repos as a side effect.
it('loads the repo list before project mode bails out', () => {
const { repoLoad, projectReturn } = offsets()
expect(projectReturn, 'project mode must still short-circuit loadTasks').toBeGreaterThan(-1)
expect(projectReturn, 'project mode must bail out after the repo list load').toBeGreaterThan(
repoLoad
)
})
it('still bails out before any work-item fetch in project mode', () => {
const { projectReturn, workItemFetch } = offsets()
expect(workItemFetch, 'the hosted work-item fetch must still exist').toBeGreaterThan(-1)
expect(workItemFetch, 'project mode must not reach the issue/PR fetch').toBeGreaterThan(
projectReturn
)
})
})