From d9e2aaf8eb077bcfa057b2312306f8bb29b8a439 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Wed, 8 Apr 2026 19:26:10 +0200 Subject: [PATCH 1/6] feat(sig): Add GitHub connection banner to inbox report list Show a floating banner at the bottom of the report list pane indicating whether the user has linked their GitHub account. When connected, shows the GitHub handle. When not, provides a button to start the OAuth flow and refetches user data when the window regains focus. Generated-By: PostHog Code Task-Id: 3089dc72-e26f-4715-8999-1a194fc7030c --- apps/code/src/renderer/api/generated.ts | 2 + .../inbox/components/InboxSignalsTab.tsx | 3 + .../list/GitHubConnectionBanner.tsx | 86 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 apps/code/src/renderer/features/inbox/components/list/GitHubConnectionBanner.tsx diff --git a/apps/code/src/renderer/api/generated.ts b/apps/code/src/renderer/api/generated.ts index 3ff508285..8be8e0a15 100644 --- a/apps/code/src/renderer/api/generated.ts +++ b/apps/code/src/renderer/api/generated.ts @@ -10906,6 +10906,7 @@ export namespace Schemas { events_column_config?: unknown | undefined; is_2fa_enabled: boolean; has_social_auth: boolean; + github_login: string | null; has_sso_enforcement: boolean; has_seen_product_intro_for?: null | undefined; scene_personalisation: Array; @@ -12487,6 +12488,7 @@ export namespace Schemas { events_column_config: unknown; is_2fa_enabled: boolean; has_social_auth: boolean; + github_login: string | null; has_sso_enforcement: boolean; has_seen_product_intro_for: null; scene_personalisation: Array; diff --git a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx index e8c7e5b5d..33fd26406 100644 --- a/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx +++ b/apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx @@ -29,6 +29,7 @@ import { useRendererWindowFocusStore } from "@stores/rendererWindowFocusStore"; import { useCallback, useEffect, useMemo, useRef } from "react"; import { MultiSelectStack } from "./detail/MultiSelectStack"; import { ReportDetailPane } from "./detail/ReportDetailPane"; +import { GitHubConnectionBanner } from "./list/GitHubConnectionBanner"; import { ReportListPane } from "./list/ReportListPane"; import { SignalsToolbar } from "./list/SignalsToolbar"; @@ -475,6 +476,8 @@ export function InboxSignalsTab() { + + {/* Resize handle */} s.cloudRegion); + const awaitingLink = useRef(false); + + // After the user clicks connect and returns to the app, refetch to pick up the new github_login + useEffect(() => { + const onFocus = () => { + if (awaitingLink.current) { + awaitingLink.current = false; + void queryClient.invalidateQueries({ queryKey: ["me"] }); + } + }; + window.addEventListener("focus", onFocus); + return () => window.removeEventListener("focus", onFocus); + }, []); + + if (isLoading || !user) { + return null; + } + + const githubLogin = user.github_login; + + if (githubLogin) { + return ( + + ); + } + + const connectUrl = cloudRegion + ? `${getCloudUrlFromRegion(cloudRegion)}/login/github/` + : null; + + if (!connectUrl) { + return null; + } + + return ( + + ); +} From c6a41771a3e01d659f4528acb0b674c03318952e Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 9 Apr 2026 16:45:51 +0200 Subject: [PATCH 2/6] Tweak flow --- .../list/GitHubConnectionBanner.tsx | 57 ++++++++++--------- .../inbox/components/list/SignalsToolbar.tsx | 2 +- .../features/inbox/utils/inboxConstants.ts | 2 +- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/apps/code/src/renderer/features/inbox/components/list/GitHubConnectionBanner.tsx b/apps/code/src/renderer/features/inbox/components/list/GitHubConnectionBanner.tsx index 86978e9f8..5504c60bf 100644 --- a/apps/code/src/renderer/features/inbox/components/list/GitHubConnectionBanner.tsx +++ b/apps/code/src/renderer/features/inbox/components/list/GitHubConnectionBanner.tsx @@ -1,12 +1,24 @@ import { Button } from "@components/ui/Button"; import { useAuthStateValue } from "@features/auth/hooks/authQueries"; import { useMeQuery } from "@hooks/useMeQuery"; -import { ArrowSquareOutIcon, GithubLogoIcon } from "@phosphor-icons/react"; +import { + ArrowSquareOutIcon, + GithubLogoIcon, + InfoIcon, +} from "@phosphor-icons/react"; import { trpcClient } from "@renderer/trpc/client"; import { getCloudUrlFromRegion } from "@shared/constants/oauth"; +import type { CloudRegion } from "@shared/types/oauth"; import { queryClient } from "@utils/queryClient"; import { useEffect, useRef } from "react"; +/** PostHog Cloud OAuth URL to attach GitHub (`connect_from` is handled by PostHog web after redirect). */ +function posthogCloudGithubAccountLinkUrl(region: CloudRegion): string { + const url = new URL("/login/github/", getCloudUrlFromRegion(region)); + url.searchParams.set("connect_from", "posthog_code"); + return url.toString(); +} + export function GitHubConnectionBanner() { const { data: user, isLoading } = useMeQuery(); const cloudRegion = useAuthStateValue((s) => s.cloudRegion); @@ -28,37 +40,16 @@ export function GitHubConnectionBanner() { return null; } - const githubLogin = user.github_login; - - if (githubLogin) { - return ( - - ); + if (user.github_login) { + return null; } - const connectUrl = cloudRegion - ? `${getCloudUrlFromRegion(cloudRegion)}/login/github/` - : null; - - if (!connectUrl) { + if (!cloudRegion) { return null; } + const connectUrl = posthogCloudGithubAccountLinkUrl(cloudRegion); + return ( +
+ +
); } From 398dad65625b35c81d50cdcf2419725c1e4ca221 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Fri, 10 Apr 2026 14:18:17 +0200 Subject: [PATCH 6/6] Move GH login retrieval to /github_login --- apps/code/src/renderer/api/posthogClient.ts | 8 ++++++++ .../inbox/components/detail/ReportDetailPane.tsx | 13 +++---------- .../components/list/GitHubConnectionBanner.tsx | 14 +++++++++----- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/code/src/renderer/api/posthogClient.ts b/apps/code/src/renderer/api/posthogClient.ts index 913308d60..b356638b7 100644 --- a/apps/code/src/renderer/api/posthogClient.ts +++ b/apps/code/src/renderer/api/posthogClient.ts @@ -382,6 +382,14 @@ export class PostHogAPIClient { return data; } + async getGithubLogin(): Promise { + // @ts-expect-error this is not in the generated client YET + const data = (await this.api.get("/api/users/{uuid}/github_login/", { + path: { uuid: "@me" }, + })) as { github_login: string | null }; + return data.github_login; + } + async switchOrganization(orgId: string): Promise { await this.api.patch("/api/users/{uuid}/", { path: { uuid: "@me" }, diff --git a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx index 1c5da046c..405235164 100644 --- a/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx +++ b/apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx @@ -63,16 +63,9 @@ import { SignalCard } from "./SignalCard"; function isSuggestedReviewerRowMe( reviewer: SuggestedReviewer, - me: { uuid: string; github_login: string | null } | undefined | null, + meUuid: string | undefined, ): boolean { - if (!me) return false; - if (reviewer.user?.uuid && me.uuid === reviewer.user.uuid) return true; - if (me.github_login && reviewer.github_login) { - return ( - me.github_login.toLowerCase() === reviewer.github_login.toLowerCase() - ); - } - return false; + return !!reviewer.user?.uuid && !!meUuid && meUuid === reviewer.user.uuid; } // ── Helpers ───────────────────────────────────────────────────────────────── @@ -468,7 +461,7 @@ export function ReportDetailPane({ report, onClose }: ReportDetailPaneProps) { {suggestedReviewers.map((reviewer) => { - const isMe = isSuggestedReviewerRowMe(reviewer, me); + const isMe = isSuggestedReviewerRowMe(reviewer, me?.uuid); return ( client.getGithubLogin(), + { staleTime: 5 * 60 * 1000 }, + ); const cloudRegion = useAuthStateValue((s) => s.cloudRegion); const awaitingLink = useRef(false); @@ -29,18 +33,18 @@ export function GitHubConnectionBanner() { const onFocus = () => { if (awaitingLink.current) { awaitingLink.current = false; - void queryClient.invalidateQueries({ queryKey: ["me"] }); + void queryClient.invalidateQueries({ queryKey: ["github_login"] }); } }; window.addEventListener("focus", onFocus); return () => window.removeEventListener("focus", onFocus); }, []); - if (isLoading || !user) { + if (isLoading) { return null; } - if (user.github_login) { + if (githubLogin) { return null; }