# fix\(mobile\): header menu and Git menu reach a thread's linked pull request · PR \#17717 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/17717)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: open

Source repository: Unavailable fork

Source branch: fix/mobile-git-open-linked-pr

Target branch: main

Head commit: a193851f631543e3cf54a011ed931150a2434fd4

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/17717)

[Commits](https://git.cafe/pingdotgg/t3code/pull/17717/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/17717/diffs?format=markdown)

## Description

---

## Problem

On mobile there is no obvious way to reach a thread's linked pull requests.

- On Android in portrait the header shows one action and folds the rest into the three-dots menu. That menu has no linked-PR entry, so the list is only reachable from the bottom of the Git sheet.
- The Git sheet and its quick action read only the PR derived from the checked-out ref. A linked PR on another ref (a fork branch, a stacked layer) gets no open-PR action. Web had the same gap (#17527).

## Change

- The header menu gets a linked-PR entry. "Open pull request #N" opens a single linked PR directly, through the same `useOpenPullRequest` opener the Git controls and sheet use. "Linked pull requests (N)" opens the Git sheet's list when several are linked. It is absent when none are.
- `withLinkedPullRequest` folds the thread's current open linked PR into the git status when the ref-derived status has no open PR, so "View PR" appears in the Git sheet and the header's git controls. A link that has not synced yet has no snapshot and counts as open, as in the thread list. A ref-derived open PR still wins, and merged or closed links are ignored.

## Scope and approval

Fixes #17716.

## Verification

Tests and types (re-run after rebasing onto `7d5a42912a`):

- `vp test run src/features/threads/git/linkedPullRequestStatus.test.ts src/features/threads/useThreadHeaderOptions.test.tsx` in `apps/mobile`: 11 pass. The new tests cover the linked PR being offered, an unsynced link counting as open, a ref-derived open PR winning, closed or merged links being ignored, null/empty passthrough, and the header action for one, several or no links.
- `tsc-rs --noEmit` in `apps/mobile` clean; `vp fmt --check` clean on the touched files.

Manual checks use the same fixture on both platforms: one thread whose checked-out branch is `main` and which has one open linked PR (`#42` in the fixture). "Before" is the same dev-client build with `ThreadHeader.tsx`, `ThreadRouteScreen.tsx` and `GitOverviewSheet.tsx` reverted to the base commit `4ea735614b` (Metro restarted with `--clear`, and I checked that the served bundle no longer contained the new code); "after" is this branch. Images live on the fork's `pr-17717-evidence` branch, not in this PR.

**Android, portrait** (Pixel 8 emulator, Android 16, 1080x2400 at 420 dpi, so about 411 dp wide: one direct header action, the rest in the three-dots menu):

| | Before | After |
| --- | --- | --- |
| Three-dots menu | <img src="https://raw.githubusercontent.com/kaovilai/t3code/pr-17717-evidence/android-pixel8/before-overflow-menu.png" width="260"> | <img src="https://raw.githubusercontent.com/kaovilai/t3code/pr-17717-evidence/android-pixel8/after-overflow-menu.png" width="260"> |

- Before: the menu has "Open terminal" and "Open git controls" only.
- After: the menu also has "Open pull request `#42`". At the time, tapping it handed off to Chrome at `github.com/example/fixture/pull/42` ([screenshot](https://raw.githubusercontent.com/kaovilai/t3code/pr-17717-evidence/android-pixel8/after-tap-opens-pr-url.png)); that page is GitHub's 404 because the fixture repository is made up, so it shows the right URL was opened, not that a real PR page loads.

**iOS** (iPhone 17 simulator, iOS 26.5; the iOS header uses native menus, so only the Git menu and Git sheet change):

| | Before | After |
| --- | --- | --- |
| Header Git menu | <img src="https://raw.githubusercontent.com/kaovilai/t3code/pr-17717-evidence/ios-iphone17/before-git-menu.png" width="260"> | <img src="https://raw.githubusercontent.com/kaovilai/t3code/pr-17717-evidence/ios-iphone17/after-git-menu.png" width="260"> |
| Git sheet | <img src="https://raw.githubusercontent.com/kaovilai/t3code/pr-17717-evidence/ios-iphone17/before-git-more-sheet.png" width="260"> | <img src="https://raw.githubusercontent.com/kaovilai/t3code/pr-17717-evidence/ios-iphone17/after-git-more-sheet.png" width="260"> |

- Before: the header Git menu shows "Clean" with no PR, and the Git sheet shows a disabled "Create PR – No local commits to include in a PR" even though the thread has a linked PR.
- After: the header Git menu shows "Clean · PR `#42`" and a "View PR" item; the Git sheet shows "View PR – PR `#42` open".

Not verified:

- All screenshots were taken before the rebase onto current main. The rebase had one conflict, in `GitOverviewSheet.tsx` (main now passes `useOpenPullRequest` into `openExistingPr`'s dependencies), and I changed the header entry to use that same opener instead of the browser. I did not re-capture screenshots, and did not tap the Android entry again, so the native PR pane (or browser fallback) opened from the header is covered by tests and types only.
- The several-links variant ("Linked pull requests (N)") was not exercised on a device; it is covered only by the unit tests. The Android menu was checked in portrait only, not at the wider breakpoint where three actions sit in the header.
- I did not tap "View PR" in the iOS Git menu or sheet.
- The linked PR is a row written into a throwaway local server's database (`example/fixture#42`, state open), not a real GitHub PR.
- The iOS simulator build needed a local, uncommitted workaround in `T3LayoutMetricsView.swift` (iOS 27.1-only APIs against an iOS 27.0 SDK). It is not part of this PR.

Claude Sonnet 5.5 (`claude-sonnet-5-5`) via Claude Code in T3 Code

> [!Note]
> Responses generated with Claude (model: Claude Sonnet 5.5)
