File size: 3,474 Bytes
9b906ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { useQueries } from "@tanstack/react-query";
import { getCloudOrganizationMe } from "#/api/cloud/organization-service.api";
import {
  useActiveBackend,
  useActiveBackendContext,
} from "#/contexts/active-backend-context";
import { useAllCloudOrganizations } from "./use-cloud-organizations";

/**
 * Resolve the current user's `user_id` per cloud backend with one
 * `/api/organizations/{orgId}/me` call per backend (NOT one per org).
 *
 * The cloud contract: `/me` returns `{ org_id, user_id, … }`. `user_id`
 * is identical regardless of which org you ask, so we make a single
 * call per backend.
 *
 * Path-param rule: when `backend.id === active.backend.id` and
 * `active.orgId` is set, the call uses **that** orgId — i.e. `/me`
 * always tracks the currently selected environment for the active
 * backend. For inactive backends (or when no org is selected yet), the
 * first org is used as a sentinel just to obtain `user_id`. This
 * matches the requirement that `/me` reflect the selected org for the
 * active environment, while still supporting the personal-workspace
 * label across non-active backends in the dropdown.
 *
 * The query key includes `active.orgId`, so picking a different org
 * via `setActive` re-keys this query and refetches `/me` with the new
 * active orgId.
 */
export function useCloudCurrentUserId(): Record<
  string,
  { isLoading: boolean; userId: string | null }
> {
  const { backends } = useActiveBackendContext();
  const active = useActiveBackend();
  const cloudOrgs = useAllCloudOrganizations();

  const targets: {
    backendId: string;
    connectionRevision: number;
    orgIdForMe: string;
  }[] = [];
  for (const backend of backends) {
    if (backend.kind === "cloud") {
      const entry = cloudOrgs[backend.id];
      // Prefer the active org when this backend IS the active one and
      // an org has been selected; otherwise fall back to the first org
      // we know about for that backend.
      const preferredOrgId =
        backend.id === active.backend.id && active.orgId
          ? active.orgId
          : (entry?.orgs[0]?.id ?? null);
      if (preferredOrgId) {
        targets.push({
          backendId: backend.id,
          connectionRevision: backend.connectionRevision ?? 0,
          orgIdForMe: preferredOrgId,
        });
      }
    }
  }

  const results = useQueries({
    queries: targets.map(({ backendId, connectionRevision, orgIdForMe }) => {
      const backend = backends.find((b) => b.id === backendId);
      return {
        // `orgIdForMe` is included in the query key so re-resolving the
        // active org also re-keys this query → React Query refetches
        // automatically without relying on explicit invalidation.
        queryKey: [
          "cloud-current-user",
          backendId,
          orgIdForMe,
          connectionRevision,
        ] as const,
        queryFn: async () => {
          if (!backend) return { orgId: orgIdForMe, userId: "" };
          return getCloudOrganizationMe(orgIdForMe, backend);
        },
        enabled: !!backend,
        staleTime: 1000 * 60 * 5,
        retry: false,
        meta: { disableToast: true },
      };
    }),
  });

  const out: Record<string, { isLoading: boolean; userId: string | null }> = {};
  targets.forEach((target, index) => {
    const q = results[index];
    out[target.backendId] = {
      isLoading: q.isLoading,
      userId: q.data?.userId ?? null,
    };
  });
  return out;
}