Skip to content

Commit 6a6b7c7

Browse files
committed
Eliminate N+1 API calls in ProjectSwitcher by deriving projects from @me response
1 parent 7e84097 commit 6a6b7c7

3 files changed

Lines changed: 52 additions & 25 deletions

File tree

apps/twig/src/renderer/features/auth/stores/authStore.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ export const useAuthStore = create<AuthState>()(
177177

178178
// Clear any cached data from previous sessions AFTER setting new auth
179179
queryClient.clear();
180+
queryClient.setQueryData(["currentUser"], user);
180181

181182
get().scheduleTokenRefresh();
182183

@@ -470,6 +471,8 @@ export const useAuthStore = create<AuthState>()(
470471
needsProjectSelection: false,
471472
});
472473

474+
queryClient.setQueryData(["currentUser"], user);
475+
473476
updateServiceTokens(currentTokens.accessToken);
474477

475478
get().scheduleTokenRefresh();
@@ -596,6 +599,7 @@ export const useAuthStore = create<AuthState>()(
596599
updateServiceTokens(tokenResponse.access_token);
597600

598601
queryClient.clear();
602+
queryClient.setQueryData(["currentUser"], user);
599603

600604
get().scheduleTokenRefresh();
601605

apps/twig/src/renderer/features/projects/hooks/useProjects.tsx

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useAuthStore } from "@features/auth/stores/authStore";
22
import { useQuery } from "@tanstack/react-query";
3+
import { useMemo } from "react";
34

45
export interface ProjectInfo {
56
id: number;
@@ -36,27 +37,52 @@ export function useProjects() {
3637
const client = useAuthStore((s) => s.client);
3738
const currentProjectId = useAuthStore((s) => s.projectId);
3839

39-
const query = useQuery({
40-
queryKey: ["projects", availableProjectIds],
41-
queryFn: async () => {
42-
if (!client || availableProjectIds.length === 0) {
43-
return [];
44-
}
45-
return client.getProjectDetails(availableProjectIds);
46-
},
47-
enabled: !!client && availableProjectIds.length > 0,
48-
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
40+
const {
41+
data: currentUser,
42+
isLoading,
43+
error,
44+
} = useQuery({
45+
queryKey: ["currentUser"],
46+
queryFn: () => client?.getCurrentUser(),
47+
enabled: !!client,
48+
staleTime: 5 * 60 * 1000,
4949
});
5050

51-
const currentProject = query.data?.find((p) => p.id === currentProjectId);
52-
const groupedProjects = query.data ? groupProjectsByOrg(query.data) : [];
51+
const projects = useMemo(() => {
52+
if (!currentUser?.organization) return [];
53+
54+
const teams = (currentUser.organization.teams ?? []) as Array<{
55+
id: number;
56+
name?: string;
57+
}>;
58+
const orgName = currentUser.organization.name ?? "Unknown Organization";
59+
const orgId = currentUser.organization.id ?? "";
60+
61+
const teamMap = new Map(teams.map((t) => [t.id, t]));
62+
63+
return availableProjectIds
64+
.map((id) => {
65+
const team = teamMap.get(id);
66+
if (!team) return null;
67+
return {
68+
id,
69+
name: team.name ?? `Project ${id}`,
70+
organization: { id: orgId, name: orgName },
71+
};
72+
})
73+
.filter((p): p is ProjectInfo => p !== null);
74+
}, [currentUser, availableProjectIds]);
75+
76+
const currentProject = projects.find((p) => p.id === currentProjectId);
77+
const groupedProjects = groupProjectsByOrg(projects);
5378

5479
return {
55-
projects: query.data ?? [],
80+
projects,
5681
groupedProjects,
5782
currentProject,
5883
currentProjectId,
59-
isLoading: query.isLoading,
60-
error: query.error,
84+
currentUser: currentUser ?? null,
85+
isLoading,
86+
error,
6187
};
6288
}

apps/twig/src/renderer/features/sidebar/components/ProjectSwitcher.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import {
2727
} from "@radix-ui/themes";
2828
import { trpcVanilla } from "@renderer/trpc/client";
2929
import { getCloudUrlFromRegion } from "@shared/constants/oauth";
30-
import { useQuery } from "@tanstack/react-query";
3130
import { isMac } from "@utils/platform";
3231
import { useCallback, useEffect, useRef, useState } from "react";
3332
import "./ProjectSwitcher.css";
@@ -56,15 +55,13 @@ export function ProjectSwitcher() {
5655
const cloudRegion = useAuthStore((s) => s.cloudRegion);
5756
const selectProject = useAuthStore((s) => s.selectProject);
5857
const logout = useAuthStore((s) => s.logout);
59-
const client = useAuthStore((s) => s.client);
60-
const { groupedProjects, currentProject, currentProjectId, isLoading } =
61-
useProjects();
62-
63-
const { data: currentUser } = useQuery({
64-
queryKey: ["currentUser"],
65-
queryFn: () => client?.getCurrentUser(),
66-
enabled: !!client,
67-
});
58+
const {
59+
groupedProjects,
60+
currentProject,
61+
currentProjectId,
62+
currentUser,
63+
isLoading,
64+
} = useProjects();
6865

6966
const handleProjectSelect = (projectId: number) => {
7067
if (projectId !== currentProjectId) {

0 commit comments

Comments
 (0)