Skip to content
Closed
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,41 @@ interface SetupFormProps {
onCancel: () => void;
}

const POLL_INTERVAL_GITHUB_MS = 3_000;
const POLL_TIMEOUT_GITHUB_MS = 300_000;

function GitHubSetup({ onComplete, onCancel }: SetupFormProps) {
const projectId = useAuthStore((s) => s.projectId);
const cloudRegion = useAuthStore((s) => s.cloudRegion);
const client = useAuthStore((s) => s.client);
const { githubIntegration, repositories, isLoadingRepos } =
useRepositoryIntegration();
const [repo, setRepo] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
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]);

// Stop polling once integration appears
useEffect(() => {
if (githubIntegration && connecting) {
stopPolling();
setConnecting(false);
}
}, [githubIntegration, connecting, stopPolling]);

// Auto-select the first repo once loaded
useEffect(() => {
Expand All @@ -66,6 +94,47 @@ function GitHubSetup({ onComplete, onCancel }: SetupFormProps) {
}
}, [repo, repositories]);

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

pollTimerRef.current = setInterval(async () => {
try {
if (!client) return;
// Trigger a refetch of integrations
const integrations =
await client.getIntegrationsForProject(projectId);
const hasGithub = integrations.some(
(i: { kind: string }) => i.kind === "github",
);
if (hasGithub) {
stopPolling();
setConnecting(false);
toast.success("GitHub connected");
}
} catch {
// Ignore individual poll failures
}
}, POLL_INTERVAL_GITHUB_MS);

pollTimeoutRef.current = setTimeout(() => {
stopPolling();
setConnecting(false);
toast.error("Connection timed out. Please try again.");
}, POLL_TIMEOUT_GITHUB_MS);
} catch (error) {
setConnecting(false);
toast.error(
error instanceof Error ? error.message : "Failed to start GitHub flow",
);
}
}, [cloudRegion, projectId, client, stopPolling]);

const handleSubmit = useCallback(async () => {
if (!projectId || !client || !repo || !githubIntegration) return;

Expand Down Expand Up @@ -96,10 +165,28 @@ function GitHubSetup({ onComplete, onCancel }: SetupFormProps) {
if (!githubIntegration) {
return (
<SetupFormContainer title="Connect GitHub">
<Text size="2" style={{ color: "var(--gray-11)" }}>
No GitHub integration found. Please connect GitHub during onboarding
first.
</Text>
<Flex direction="column" gap="3">
<Text size="2" style={{ color: "var(--gray-11)" }}>
Connect your GitHub account to import issues as signals.
</Text>
<Flex gap="2" justify="end">
<Button
size="2"
variant="soft"
onClick={onCancel}
disabled={connecting}
>
Cancel
</Button>
<Button
size="2"
onClick={() => void handleConnectGitHub()}
disabled={connecting}
>
{connecting ? "Waiting for authorization..." : "Connect GitHub"}
</Button>
</Flex>
</Flex>
</SetupFormContainer>
);
}
Expand Down
169 changes: 108 additions & 61 deletions apps/code/src/renderer/features/inbox/components/InboxSignalsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from "@features/inbox/utils/filterReports";
import { INBOX_REFETCH_INTERVAL_MS } from "@features/inbox/utils/inboxConstants";
import { useDraftStore } from "@features/message-editor/stores/draftStore";
import { useSettingsDialogStore } from "@features/settings/stores/settingsDialogStore";
import { SignalSourcesSettings } from "@features/settings/components/sections/SignalSourcesSettings";
import { useCreateTask } from "@features/tasks/hooks/useTasks";
import { useFeatureFlag } from "@hooks/useFeatureFlag";
import { useRepositoryIntegration } from "@hooks/useIntegrations";
Expand All @@ -35,10 +35,12 @@ import {
Badge,
Box,
Button,
Dialog,
Flex,
ScrollArea,
Select,
Text,
Tooltip,
} from "@radix-ui/themes";
import graphsHog from "@renderer/assets/images/graphs-hog.png";
import { getCloudUrlFromRegion } from "@shared/constants/oauth";
Expand Down Expand Up @@ -124,7 +126,7 @@ export function InboxSignalsTab() {
const statusFilter = useInboxSignalsFilterStore((s) => s.statusFilter);
const { data: signalSourceConfigs } = useSignalSourceConfigs();
const hasSignalSources = signalSourceConfigs?.some((c) => c.enabled) ?? false;
const openSettings = useSettingsDialogStore((s) => s.open);
const [sourcesDialogOpen, setSourcesDialogOpen] = useState(false);

const windowFocused = useRendererWindowFocusStore((s) => s.focused);
const isInboxView = useNavigationStore((s) => s.view.type === "inbox");
Expand Down Expand Up @@ -324,81 +326,126 @@ export function InboxSignalsTab() {
);
}

const sourcesDialog = (
<Dialog.Root open={sourcesDialogOpen} onOpenChange={setSourcesDialogOpen}>
<Dialog.Content maxWidth="520px">
<Flex align="center" justify="between" mb="3">
<Dialog.Title size="3" mb="0">
Signal sources
</Dialog.Title>
<Dialog.Close>
<button
type="button"
className="rounded p-1 text-gray-11 hover:bg-gray-3 hover:text-gray-12"
aria-label="Close"
>
<XIcon size={16} />
</button>
</Dialog.Close>
</Flex>
<SignalSourcesSettings />
<Flex justify="end" mt="4">
{hasSignalSources ? (
<Dialog.Close>
<Button size="2">Back to Inbox</Button>
</Dialog.Close>
) : (
<Tooltip content="You haven't enabled any signal source yet!">
<Button size="2" disabled>
Back to Inbox
</Button>
</Tooltip>
)}
</Flex>
</Dialog.Content>
</Dialog.Root>
);

if (allReports.length === 0) {
if (!hasSignalSources) {
return (
<Flex
direction="column"
align="center"
justify="center"
height="100%"
px="5"
style={{ margin: "0 auto" }}
>
<Flex direction="column" align="center" style={{ maxWidth: 420 }}>
<img
src={graphsHog}
alt=""
style={{ width: 128, marginBottom: 20 }}
/>

<Text
size="4"
weight="bold"
align="center"
style={{ color: "var(--gray-12)" }}
>
Welcome to your Inbox
</Text>
<>
<Flex
direction="column"
align="center"
justify="center"
height="100%"
px="5"
style={{ margin: "0 auto" }}
>
<Flex direction="column" align="center" style={{ maxWidth: 420 }}>
<img
src={graphsHog}
alt=""
style={{ width: 128, marginBottom: 20 }}
/>

<Flex
direction="column"
align="center"
gap="3"
mt="3"
style={{ maxWidth: 360 }}
>
<Text
size="1"
size="4"
weight="bold"
align="center"
style={{ color: "var(--gray-11)", lineHeight: 1.35 }}
style={{ color: "var(--gray-12)" }}
>
<Text weight="medium" style={{ color: "var(--gray-12)" }}>
Background analysis of your data — while you sleep.
</Text>
<br />
Session recordings watched automatically. Issues, tickets, and
evals analyzed around the clock.
Welcome to your Inbox
</Text>

<ArrowDownIcon size={14} style={{ color: "var(--gray-8)" }} />

<Text
size="1"
<Flex
direction="column"
align="center"
style={{ color: "var(--gray-11)", lineHeight: 1.35 }}
gap="3"
mt="3"
style={{ maxWidth: 360 }}
>
<Text weight="medium" style={{ color: "var(--gray-12)" }}>
Ready-to-run fixes for real user problems.
<Text
size="1"
align="center"
style={{ color: "var(--gray-11)", lineHeight: 1.35 }}
>
<Text weight="medium" style={{ color: "var(--gray-12)" }}>
Background analysis of your data — while you sleep.
</Text>
<br />
Session recordings watched automatically. Issues, tickets, and
evals analyzed around the clock.
</Text>
<br />
Each report includes evidence and impact numbers — just execute
the prompt in your agent.
</Text>
</Flex>

<Button
size="2"
style={{ marginTop: 20 }}
onClick={() => openSettings("signals")}
>
Enable Inbox
</Button>
<ArrowDownIcon size={14} style={{ color: "var(--gray-8)" }} />

<Text
size="1"
align="center"
style={{ color: "var(--gray-11)", lineHeight: 1.35 }}
>
<Text weight="medium" style={{ color: "var(--gray-12)" }}>
Ready-to-run fixes for real user problems.
</Text>
<br />
Each report includes evidence and impact numbers — just
execute the prompt in your agent.
</Text>
</Flex>

<Button
size="2"
style={{ marginTop: 20 }}
onClick={() => setSourcesDialogOpen(true)}
>
Enable Inbox
</Button>
</Flex>
</Flex>
</Flex>
{sourcesDialog}
</>
);
}
return <InboxWarmingUpState />;
return (
<>
<InboxWarmingUpState
onConfigureSources={() => setSourcesDialogOpen(true)}
/>
{sourcesDialog}
</>
);
}

return (
Expand Down
Loading
Loading