Skip to content

feat(mobile): show live PR status on inbox report cards and detail (port #2695, #2694)#2698

Merged
Gilbert09 merged 1 commit into
mainfrom
posthog-code/mobile-inbox-pr-status
Jun 16, 2026
Merged

feat(mobile): show live PR status on inbox report cards and detail (port #2695, #2694)#2698
Gilbert09 merged 1 commit into
mainfrom
posthog-code/mobile-inbox-pr-status

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Summary

Ports two desktop PRs to the React Native / Expo mobile app:

Inbox signal reports that carry an implementation_pr_url now render a small live PR-status badge (open / merged / closed / draft) on:

  • the inbox report list rows (ReportListRow)
  • the swipeable tinder card (SwipeableReportCard)
  • the report detail screen (app/inbox/[...id].tsx)

It reuses the existing mobile pieces rather than reinventing them:

  • usePrStatus — fetches live PR state from GitHub's public REST API, returning null for private/unresolvable PRs.
  • PrStatusBadge — the color-coded open/merged/closed/draft badge.

Behavior (matches desktop)

  • Only renders for a canonical GitHub PR URL.
  • While loading, shows nothing (no misleading state).
  • If the PR state can't be resolved (private repo / 404 / unparseable), renders nothing rather than a default "open" badge.

This is driven by a new hideWhenUnresolved prop on PrStatusBadge. The existing task-header usage is unchanged — it keeps its always-tappable neutral badge. A size="sm" variant keeps the badge compact on dense list rows.

The desktop GraphQL batching of PR state is intentionally not ported; mobile relies on react-query caching over the public GitHub REST API.

Tests

Added PrStatusBadge.test.tsx covering: open / merged / closed / draft rendering, and rendering nothing when hideWhenUnresolved is set and the status is loading or unresolved.

Scope

No desktop code changed. Changes are confined to apps/mobile.

…ort #2695, #2694)

Inbox signal reports that have an `implementation_pr_url` now render a live
PR-status badge (open / merged / closed / draft) on the report list rows, the
tinder card, and the report detail screen — reusing the existing
`usePrStatus` hook and `PrStatusBadge` component from the tasks feature.

The badge only renders for a canonical GitHub PR URL and renders nothing
while the state is loading or unresolvable (private repo / 404 / unparseable),
rather than showing a misleading default "open" badge. This is gated by a new
`hideWhenUnresolved` prop on `PrStatusBadge`; the existing task-header usage
keeps its always-tappable neutral badge. A `size="sm"` variant keeps the badge
compact on dense list rows.

Ports the desktop behavior from #2695 (list cards) and #2694 (detail) to the
mobile app. The desktop GraphQL batching is intentionally not ported; mobile
relies on react-query caching of the public GitHub REST API.

Generated-By: PostHog Code
Task-Id: ba8e678f-1c24-4c21-abe3-5dc204adec1a
@Gilbert09 Gilbert09 requested a review from a team June 16, 2026 10:52
@github-actions

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 5db91e5.

@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/mobile/src/features/tasks/components/PrStatusBadge.test.tsx:55-87
**Non-parameterised state rendering tests**

The four individual state cases (open, merged, closed, draft) repeat the same structure — call `setStatus`, render with an identical `prUrl`, assert on `label()`. Per the team's convention we always prefer parameterised tests; these are a natural fit for `it.each`. The merged case has one extra icon assertion that can be captured as an optional field in the test-case object (e.g. `expectedIcon?: string` defaulting to `"GitPullRequest"`), keeping the whole suite in one table.

### Issue 2 of 2
apps/mobile/src/features/tasks/components/PrStatusBadge.test.tsx:55-111
**New `size` prop is untested**

The PR adds a `size` prop with distinct `box` class and `iconSize` values for `"sm"` vs `"md"`, but no test asserts on either. The `iconSize` logic (`16` vs `20`) is passed as a prop to the icon, so it is observable in the rendered tree. Without a test, a future refactor could silently regress the compact badge on list rows.

Reviews (1): Last reviewed commit: "feat(mobile): show live PR status on inb..." | Re-trigger Greptile

Comment on lines +55 to +87
state: "open",
merged: false,
draft: false,
additions: 0,
deletions: 0,
};

describe("PrStatusBadge", () => {
it("renders an open PR badge", () => {
setStatus({ ...base, state: "open" });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open PR");
expect(iconCount(r, "GitPullRequest")).toBe(1);
});

it("renders a merged PR badge with the merge icon", () => {
setStatus({ ...base, state: "closed", merged: true });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open merged PR");
expect(iconCount(r, "GitMerge")).toBe(1);
expect(iconCount(r, "GitPullRequest")).toBe(0);
});

it("renders a closed PR badge", () => {
setStatus({ ...base, state: "closed" });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open closed PR");
});

it("renders a draft PR badge", () => {
setStatus({ ...base, state: "open", draft: true });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open draft PR");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Non-parameterised state rendering tests

The four individual state cases (open, merged, closed, draft) repeat the same structure — call setStatus, render with an identical prUrl, assert on label(). Per the team's convention we always prefer parameterised tests; these are a natural fit for it.each. The merged case has one extra icon assertion that can be captured as an optional field in the test-case object (e.g. expectedIcon?: string defaulting to "GitPullRequest"), keeping the whole suite in one table.

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/mobile/src/features/tasks/components/PrStatusBadge.test.tsx
Line: 55-87

Comment:
**Non-parameterised state rendering tests**

The four individual state cases (open, merged, closed, draft) repeat the same structure — call `setStatus`, render with an identical `prUrl`, assert on `label()`. Per the team's convention we always prefer parameterised tests; these are a natural fit for `it.each`. The merged case has one extra icon assertion that can be captured as an optional field in the test-case object (e.g. `expectedIcon?: string` defaulting to `"GitPullRequest"`), keeping the whole suite in one table.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +55 to +111
state: "open",
merged: false,
draft: false,
additions: 0,
deletions: 0,
};

describe("PrStatusBadge", () => {
it("renders an open PR badge", () => {
setStatus({ ...base, state: "open" });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open PR");
expect(iconCount(r, "GitPullRequest")).toBe(1);
});

it("renders a merged PR badge with the merge icon", () => {
setStatus({ ...base, state: "closed", merged: true });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open merged PR");
expect(iconCount(r, "GitMerge")).toBe(1);
expect(iconCount(r, "GitPullRequest")).toBe(0);
});

it("renders a closed PR badge", () => {
setStatus({ ...base, state: "closed" });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open closed PR");
});

it("renders a draft PR badge", () => {
setStatus({ ...base, state: "open", draft: true });
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(label(r)).toBe("Open draft PR");
});

it.each([
{ data: undefined, label: "loading" },
{ data: null, label: "unresolved (private/404/non-GitHub)" },
])(
"renders nothing when hideWhenUnresolved is set and status is $label",
({ data }) => {
setStatus(data);
const r = render({
prUrl: "https://github.com/a/b/pull/1",
hideWhenUnresolved: true,
});
expect(r.toJSON()).toBeNull();
},
);

it("still renders a neutral badge for an unresolved PR by default", () => {
setStatus(null);
const r = render({ prUrl: "https://github.com/a/b/pull/1" });
expect(r.toJSON()).not.toBeNull();
expect(label(r)).toBe("Open PR");
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 New size prop is untested

The PR adds a size prop with distinct box class and iconSize values for "sm" vs "md", but no test asserts on either. The iconSize logic (16 vs 20) is passed as a prop to the icon, so it is observable in the rendered tree. Without a test, a future refactor could silently regress the compact badge on list rows.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/mobile/src/features/tasks/components/PrStatusBadge.test.tsx
Line: 55-111

Comment:
**New `size` prop is untested**

The PR adds a `size` prop with distinct `box` class and `iconSize` values for `"sm"` vs `"md"`, but no test asserts on either. The `iconSize` logic (`16` vs `20`) is passed as a prop to the icon, so it is observable in the rendered tree. Without a test, a future refactor could silently regress the compact badge on list rows.

How can I resolve this? If you propose a fix, please make it concise.

@Gilbert09 Gilbert09 added the Stamphog This will request an autostamp by stamphog on small changes label Jun 16, 2026
@Gilbert09 Gilbert09 enabled auto-merge (squash) June 16, 2026 12:24

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low-risk additive UI change in the mobile app layer — new optional props with safe defaults, no business logic, no data model or API contract changes. The two bot comments are P2 style/coverage notes (non-parameterised tests and untested size prop); neither represents a production risk or violates a hard rule.

@Gilbert09 Gilbert09 merged commit 67cccf2 into main Jun 16, 2026
23 checks passed
@Gilbert09 Gilbert09 deleted the posthog-code/mobile-inbox-pr-status branch June 16, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant