Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/code/src/renderer/api/posthogClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ function normalizeAvailableSuggestedReviewer(
uuid: normalizedUuid,
name: optionalString(value.name) ?? "",
email: optionalString(value.email) ?? "",
github_login: optionalString(value.github_login) ?? "",
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
filterReportsBySearch,
} from "@features/inbox/utils/filterReports";
import { INBOX_REFETCH_INTERVAL_MS } from "@features/inbox/utils/inboxConstants";
import { useRepositoryIntegration } from "@hooks/useIntegrations";
import { Box, Flex, ScrollArea } from "@radix-ui/themes";
import type { SignalReportsQueryParams } from "@shared/types";
import { useNavigationStore } from "@stores/navigationStore";
Expand All @@ -48,6 +49,9 @@ export function InboxSignalsTab() {
(s) => s.suggestedReviewerFilter,
);

// ── GitHub integration ───────────────────────────────────────────────
const { hasGithubIntegration } = useRepositoryIntegration();

// ── Signal source configs ───────────────────────────────────────────────
const { data: signalSourceConfigs } = useSignalSourceConfigs();
const hasSignalSources = signalSourceConfigs?.some((c) => c.enabled) ?? false;
Expand Down Expand Up @@ -576,7 +580,7 @@ export function InboxSignalsTab() {
}}
>
<Box style={{ pointerEvents: "auto" }}>
{!hasSignalSources ? (
{!hasSignalSources || !hasGithubIntegration ? (
<WelcomePane onEnableInbox={() => setSourcesDialogOpen(true)} />
) : (
<WarmingUpPane
Expand All @@ -594,6 +598,7 @@ export function InboxSignalsTab() {
open={sourcesDialogOpen}
onOpenChange={setSourcesDialogOpen}
hasSignalSources={hasSignalSources}
hasGithubIntegration={hasGithubIntegration}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ interface InboxSourcesDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
hasSignalSources: boolean;
hasGithubIntegration: boolean;
}

export function InboxSourcesDialog({
open,
onOpenChange,
hasSignalSources,
hasGithubIntegration,
}: InboxSourcesDialogProps) {
return (
<Dialog.Root open={open} onOpenChange={onOpenChange}>
Expand All @@ -32,12 +34,18 @@ export function InboxSourcesDialog({
</Flex>
<SignalSourcesSettings />
<Flex justify="end" mt="4">
{hasSignalSources ? (
{hasSignalSources && hasGithubIntegration ? (
<Dialog.Close>
<Button size="2">Back to Inbox</Button>
</Dialog.Close>
) : (
<Tooltip content="You haven't enabled any signal source yet!">
<Tooltip
content={
!hasGithubIntegration
? "Connect GitHub to get started!"
: "You haven't enabled any signal source yet!"
}
>
<Button size="2" disabled>
Back to Inbox
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ export function GitHubConnectionBanner() {
PostHog Code suggests report ownership using cutting-edge{" "}
<code>git blame</code> technology.
<br />
For this, connect your GitHub profile (different from connecting
repositories).
For relevant reports, connect your GitHub profile.
</div>
</>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,19 +146,32 @@ export function SuggestedReviewerFilterMenu() {
className="flex w-full items-start justify-between rounded-sm px-1 py-1 text-left text-[13px] text-gray-12 transition-colors hover:bg-gray-3 focus-visible:bg-gray-3 focus-visible:outline-none"
onClick={() => toggleSuggestedReviewer(reviewer.uuid)}
>
<Flex direction="column" gap="0" className="min-w-0">
<Text size="1" className="truncate text-[12px]">
{displayName}
</Text>
{reviewer.email ? (
<Text
size="1"
color="gray"
className="truncate text-[11px]"
>
{reviewer.email}
</Text>
<Flex align="center" gap="2" className="min-w-0">
{reviewer.github_login ? (
<img
src={`https://github.com/${reviewer.github_login}.png?size=32`}
alt=""
className="github-avatar shrink-0 rounded-full"
style={{ width: 20, height: 20 }}
onLoad={(e) =>
e.currentTarget.classList.add("loaded")
}
/>
) : null}
<Flex direction="column" gap="0" className="min-w-0">
<Text size="1" className="truncate text-[12px]">
{displayName}
</Text>
{reviewer.email ? (
<Text
size="1"
color="gray"
className="truncate text-[11px]"
>
{reviewer.email}
</Text>
) : null}
</Flex>
</Flex>
<span
className="flex h-4 w-4 shrink-0 items-center justify-center text-gray-12"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function makeReviewer(
uuid: "reviewer-1",
name: "Alice Jones",
email: "alice@example.com",
github_login: "alicejones",
...overrides,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface SuggestedReviewerFilterOption {
uuid: string;
name: string;
email: string;
github_login: string;
isMe: boolean;
showSeparatorBelow: boolean;
}
Expand Down Expand Up @@ -71,6 +72,7 @@ export function buildSuggestedReviewerFilterOptions(
uuid,
name: normalizeString(reviewer.name),
email: normalizeString(reviewer.email),
github_login: normalizeString(reviewer.github_login),
isMe: false,
showSeparatorBelow: false,
});
Expand All @@ -83,6 +85,7 @@ export function buildSuggestedReviewerFilterOptions(
uuid: currentUserUuid,
name: buildCurrentUserName(currentUser) || existing?.name || "",
email: normalizeString(currentUser?.email) || existing?.email || "",
github_login: existing?.github_login || "",
isMe: true,
showSeparatorBelow: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export function GitIntegrationStep({

const handleConnectGitHub = async () => {
if (!cloudRegion || !selectedProjectId || !client) return;
stopPolling();
setConnectingGithub(true);
try {
await trpcClient.githubIntegration.startFlow.mutate({
Expand Down Expand Up @@ -365,12 +366,8 @@ export function GitIntegrationStep({
alignItems: "center",
}}
>
<Button
size="2"
onClick={handleConnectGitHub}
loading={isConnecting}
>
Connect GitHub
<Button size="2" onClick={handleConnectGitHub}>
{isConnecting ? "Retry connection" : "Connect GitHub"}
<ArrowSquareOut size={16} />
</Button>
<Text
Expand All @@ -380,7 +377,9 @@ export function GitIntegrationStep({
opacity: 0.5,
}}
>
Opens GitHub to authorize the PostHog app
{isConnecting
? "Waiting for authorization\u2026 Click again if the browser tab was closed."
: "Opens GitHub to authorize the PostHog app"}
</Text>
<Button
size="1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { useAuthStateValue } from "@features/auth/hooks/authQueries";
import { useRepositoryIntegration } from "@hooks/useIntegrations";
import {
ArrowSquareOutIcon,
CheckCircleIcon,
GitBranchIcon,
InfoIcon,
} from "@phosphor-icons/react";
import { Box, Button, Flex, Spinner, Text, Tooltip } from "@radix-ui/themes";
import { trpcClient } from "@renderer/trpc/client";
import { useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect, useRef, useState } from "react";

const POLL_INTERVAL_MS = 3_000;
const POLL_TIMEOUT_MS = 300_000; // 5 minutes

export function GitHubIntegrationSection({
hasGithubIntegration,
}: {
hasGithubIntegration: boolean;
}) {
const { repositories, isLoadingRepos } = useRepositoryIntegration();
const projectId = useAuthStateValue((state) => state.projectId);
const cloudRegion = useAuthStateValue((state) => state.cloudRegion);
const queryClient = useQueryClient();
const [connecting, setConnecting] = useState(false);
const pollTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
const pollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

const stopPolling = useCallback(() => {
if (pollTimerRef.current) {
clearInterval(pollTimerRef.current);
pollTimerRef.current = null;
}
if (pollTimeoutRef.current) {
clearTimeout(pollTimeoutRef.current);
pollTimeoutRef.current = null;
}
}, []);

useEffect(() => stopPolling, [stopPolling]);

useEffect(() => {
if (hasGithubIntegration && connecting) {
stopPolling();
setConnecting(false);
}
}, [hasGithubIntegration, connecting, stopPolling]);

const handleConnect = useCallback(async () => {
if (!cloudRegion || !projectId) return;
setConnecting(true);
try {
await trpcClient.githubIntegration.startFlow.mutate({
region: cloudRegion,
projectId,
});

pollTimerRef.current = setInterval(() => {
void queryClient.invalidateQueries({
queryKey: ["integrations"],
});
}, POLL_INTERVAL_MS);

pollTimeoutRef.current = setTimeout(() => {
stopPolling();
setConnecting(false);
}, POLL_TIMEOUT_MS);
} catch {
setConnecting(false);
}
}, [cloudRegion, projectId, queryClient, stopPolling]);

return (
<Flex
align="center"
justify="between"
gap="4"
pb="3"
style={{ borderBottom: "1px dashed var(--gray-5)" }}
>
<Flex align="center" gap="3">
<Box style={{ color: "var(--gray-11)", flexShrink: 0 }}>
<GitBranchIcon size={20} />
</Box>
<Flex direction="column">
<Text size="2" weight="medium" style={{ color: "var(--gray-12)" }}>
Code access
</Text>
{hasGithubIntegration &&
!isLoadingRepos &&
repositories.length > 0 ? (
<Tooltip
content={
<Flex direction="column" gap="1">
{repositories.map((repo) => (
<Text key={repo} size="1">
{repo}
</Text>
))}
</Flex>
}
side="bottom"
>
<Flex align="center" gap="1" style={{ cursor: "help" }}>
<Text size="1" style={{ color: "var(--gray-11)" }}>
Connected and active ({repositories.length}{" "}
{repositories.length === 1 ? "repo" : "repos"})
</Text>
<InfoIcon
size={13}
style={{ color: "var(--gray-9)", flexShrink: 0 }}
/>
</Flex>
</Tooltip>
) : (
<Text size="1" style={{ color: "var(--gray-11)" }}>
{hasGithubIntegration
? "Connected and active"
: "Required for the Inbox pipeline to work"}
</Text>
)}
</Flex>
</Flex>
{connecting ? (
<Spinner size="2" />
) : hasGithubIntegration ? (
<Flex align="center" gap="2">
<CheckCircleIcon
size={16}
weight="fill"
style={{ color: "var(--green-9)" }}
/>
<Button size="1" variant="soft" onClick={() => void handleConnect()}>
Update in GitHub
<ArrowSquareOutIcon size={12} />
</Button>
</Flex>
) : (
<Button size="1" onClick={() => void handleConnect()}>
Connect GitHub
<ArrowSquareOutIcon size={12} />
</Button>
)}
</Flex>
);
}
Loading
Loading