Skip to content
Draft
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
16 changes: 7 additions & 9 deletions apps/mobile/src/app/task/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Text } from "@components/text";
import {
findRepositoryOption,
isRepositorySelectionComplete,
type RepositorySelection,
toRepositorySelection,
} from "@posthog/core/integrations/repositories";
import {
DEFAULT_CLAUDE_EXECUTION_MODE,
getAvailableModes,
Expand Down Expand Up @@ -73,15 +79,7 @@ import {
pendingTaskPromptStoreApi,
} from "@/features/tasks/stores/pendingTaskPromptStore";
import { useTaskStore } from "@/features/tasks/stores/taskStore";
import type {
CreateTaskOptions,
RepositorySelection,
} from "@/features/tasks/types";
import {
findRepositoryOption,
isRepositorySelectionComplete,
toRepositorySelection,
} from "@/features/tasks/utils/repositorySelection";
import type { CreateTaskOptions } from "@/features/tasks/types";
import { useScreenInsets } from "@/hooks/useScreenInsets";
import { logger } from "@/lib/logger";
import { getPostHogApiClient } from "@/lib/posthogApiClient";
Expand Down
6 changes: 2 additions & 4 deletions apps/mobile/src/features/inbox/components/TinderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
formatSignalReportSummaryMarkdown,
inboxStatusLabel,
} from "@posthog/core/inbox/reportPresentation";
import type { RepositoryOption } from "@posthog/core/integrations/repositories";
import type {
SignalReport,
SignalReportPriority,
Expand All @@ -28,10 +29,7 @@ import { MarkdownText } from "@/features/chat/components/MarkdownText";
import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore";
import { getModelConfigOption } from "@/features/tasks/composer/options";
import { useCloudTaskConfigOptions } from "@/features/tasks/hooks/useCloudTaskConfigOptions";
import type {
CreateTaskOptions,
RepositoryOption,
} from "@/features/tasks/types";
import type { CreateTaskOptions } from "@/features/tasks/types";
import {
ANALYTICS_EVENTS,
computeReportAgeHours,
Expand Down
12 changes: 6 additions & 6 deletions apps/mobile/src/features/tasks/components/AutomationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import {
deriveAutomationName,
parseCronExpression,
} from "@posthog/core/automations/automationSchedule";
import {
findRepositoryOption,
isRepositorySelectionComplete,
type RepositorySelection,
toRepositorySelection,
} from "@posthog/core/integrations/repositories";
import type { CreateTaskAutomationOptions } from "@posthog/shared";
import { CaretDown, GithubLogo } from "phosphor-react-native";
import { type MutableRefObject, useEffect, useMemo, useState } from "react";
Expand All @@ -20,12 +26,6 @@ import { MarkdownText } from "@/features/chat/components/MarkdownText";
import { useThemeColors } from "@/lib/theme";
import { RepositoryPickerInline } from "../composer/RepositoryPickerInline";
import { useIntegrations } from "../hooks/useIntegrations";
import type { RepositorySelection } from "../types";
import {
findRepositoryOption,
isRepositorySelectionComplete,
toRepositorySelection,
} from "../utils/repositorySelection";
import { GitHubConnectionPrompt } from "./GitHubConnectionPrompt";
import { GitHubLoadNotice } from "./GitHubLoadNotice";
import { ScheduleEditor } from "./ScheduleEditor";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text } from "@components/text";
import type { RepositoryOption } from "@posthog/core/integrations/repositories";
import { ArrowsClockwise, Check, MagnifyingGlass } from "phosphor-react-native";
import { useEffect, useMemo, useRef, useState } from "react";
import {
Expand All @@ -15,7 +16,6 @@ import Animated, {
useSharedValue,
withTiming,
} from "react-native-reanimated";
import type { RepositoryOption } from "@/features/tasks/types";
import { useThemeColors } from "@/lib/theme";

// Tuning for the nested (ScrollView) path's progressive mount. The first
Expand Down
53 changes: 15 additions & 38 deletions apps/mobile/src/features/tasks/hooks/useIntegrations.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
import {
buildTeamRepositoryOptions,
normalizeRepositoryNames,
repositoryLoadWarning,
repositoryOptionsEqual,
} from "@posthog/core/integrations/repositories";
import { useQuery } from "@tanstack/react-query";
import { useEffect, useMemo } from "react";
import { useAuthStore } from "@/features/auth";
import { getPostHogApiClient } from "@/lib/posthogApiClient";
import { useRepositoryCacheStore } from "../stores/repositoryCacheStore";
import type { RepositoryOption } from "../types";
import { buildRepositoryOptions } from "../utils/repositorySelection";

/** Cheap content-equality check for repository option lists. Lets the cache
* write effect skip no-op updates, which is what kept retriggering renders
* before — `buildRepositoryOptions` always returns a fresh array, so the
* effect's dep array churned every render. */
function repositoryOptionsEqual(
a: RepositoryOption[],
b: RepositoryOption[],
): boolean {
if (a === b) return true;
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
const left = a[i];
const right = b[i];
if (
left.integrationId !== right.integrationId ||
left.repository !== right.repository ||
left.integrationLabel !== right.integrationLabel
) {
return false;
}
}
return true;
}

export const integrationKeys = {
all: ["integrations"] as const,
Expand Down Expand Up @@ -78,11 +58,9 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) {
const results = await Promise.allSettled(
githubIntegrations.map(async (integration) => ({
integrationId: integration.id,
repositories: (
await getPostHogApiClient().getGithubRepositories(integration.id)
)
.map((repository) => repository.toLowerCase())
.filter((repository) => repository.length > 0),
repositories: normalizeRepositoryNames(
await getPostHogApiClient().getGithubRepositories(integration.id),
),
})),
);

Expand All @@ -100,12 +78,10 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) {

return {
repositoriesByIntegration,
partialError:
failedCount === 0
? null
: failedCount === githubIntegrations.length
? "Could not load GitHub repositories. Pull to retry."
: "Some GitHub repositories could not be loaded. Pull to retry.",
partialError: repositoryLoadWarning(
failedCount,
githubIntegrations.length,
),
};
},
enabled: enabled && githubIntegrations.length > 0,
Expand All @@ -121,7 +97,8 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) {
// churns the cache-write effect below AND defeats downstream React.memo /
// useMemo callers that depend on `repositoryOptions`.
const liveRepositoryOptions = useMemo(
() => buildRepositoryOptions(githubIntegrations, repositoriesByIntegration),
() =>
buildTeamRepositoryOptions(githubIntegrations, repositoriesByIntegration),
[githubIntegrations, repositoriesByIntegration],
);

Expand Down
43 changes: 14 additions & 29 deletions apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import {
buildUserRepositoryOptions,
normalizeRepositoryNames,
type RepositoryOption,
repositoryLoadWarning,
} from "@posthog/core/integrations/repositories";
import { useQuery } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";
import { useAuthStore } from "@/features/auth";
import { getPostHogApiClient } from "@/lib/posthogApiClient";
import type { RepositoryOption, UserGithubIntegration } from "../types";

/**
* User-scoped sibling of {@link useIntegrations}. Reads the authenticated
Expand All @@ -29,10 +34,6 @@ interface UseUserIntegrationsOptions {
enabled?: boolean;
}

function integrationLabel(integration: UserGithubIntegration): string {
return integration.account?.name ?? `GitHub ${integration.installation_id}`;
}

export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) {
const { enabled = true } = options;
const { oauthAccessToken } = useAuthStore();
Expand Down Expand Up @@ -66,13 +67,11 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) {
const results = await Promise.allSettled(
integrations.map(async (integration) => ({
installationId: integration.installation_id,
repositories: (
repositories: normalizeRepositoryNames(
await getPostHogApiClient().getGithubUserRepositories(
integration.installation_id,
)
)
.map((repository) => repository.toLowerCase())
.filter((repository) => repository.length > 0),
),
),
})),
);

Expand All @@ -88,31 +87,17 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) {

return {
byInstallation,
partialError:
failedCount === 0
? null
: failedCount === integrations.length
? "Could not load GitHub repositories. Pull to retry."
: "Some GitHub repositories could not be loaded. Pull to retry.",
partialError: repositoryLoadWarning(failedCount, integrations.length),
};
},
enabled: enabled && integrations.length > 0,
});

const repositoryOptions = useMemo<RepositoryOption[]>(() => {
const byInstallation = repositoriesQuery.data?.byInstallation ?? {};
return integrations
.flatMap((integration) => {
const repositories = byInstallation[integration.installation_id] ?? [];
return repositories.map((repository) => ({
// GitHub installation ids fit in a JS number; use it as the numeric
// key the picker/RepositoryOption already expect.
integrationId: Number(integration.installation_id),
integrationLabel: integrationLabel(integration),
repository,
}));
})
.sort((left, right) => left.repository.localeCompare(right.repository));
return buildUserRepositoryOptions(
integrations,
repositoriesQuery.data?.byInstallation ?? {},
);
}, [integrations, repositoriesQuery.data]);

/** Resolve the `UserIntegration` UUID for a selected installation id, to send
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RepositoryOption } from "@posthog/core/integrations/repositories";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import type { RepositoryOption } from "../types";

interface RepositoryCacheState {
/** Last successfully fetched, sorted list of repository options across all
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/features/tasks/stores/taskStore.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { RepositorySelection } from "@posthog/core/integrations/repositories";
import type { TaskActivitySortMode } from "@posthog/core/tasks/taskActivity";
import type { ExecutionMode, SupportedReasoningEffort } from "@posthog/shared";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import type { RepositorySelection } from "../types";

export type OrganizeMode = "by-project" | "chronological";
export type SortMode = TaskActivitySortMode;
Expand Down
11 changes: 0 additions & 11 deletions apps/mobile/src/features/tasks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,6 @@ export interface UserGithubIntegration {
};
}

export interface RepositoryOption {
integrationId: number;
integrationLabel: string;
repository: string;
}

export interface RepositorySelection {
integrationId: number | null;
repository: string | null;
}

export interface CreateTaskOptions {
description: string;
title?: string;
Expand Down
89 changes: 0 additions & 89 deletions apps/mobile/src/features/tasks/utils/repositorySelection.test.ts

This file was deleted.

Loading
Loading