From b63536b65441fab91cadb32bccd2e350d669a3b9 Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Mon, 27 Jul 2026 13:05:11 -0400 Subject: [PATCH 1/2] fix(loops): group loops by ownership rather than visibility Generated-By: PostHog Code --- .../loops/components/LoopsListView.test.tsx | 17 +++++++++++++---- .../loops/components/LoopsListView.tsx | 19 +++++++++++++++++-- packages/ui/src/shell/HedgehogMode.test.tsx | 2 ++ packages/ui/src/shell/HedgehogMode.tsx | 2 +- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/packages/ui/src/features/loops/components/LoopsListView.test.tsx b/packages/ui/src/features/loops/components/LoopsListView.test.tsx index f0157a4f04..b5a030ab96 100644 --- a/packages/ui/src/features/loops/components/LoopsListView.test.tsx +++ b/packages/ui/src/features/loops/components/LoopsListView.test.tsx @@ -18,11 +18,13 @@ vi.mock("./LoopRow", () => ({ function loop( id: string, visibility: LoopSchemas.LoopVisibilityEnum, + createdById = 1, ): LoopSchemas.Loop { return { id, name: `${visibility} loop`, visibility, + created_by_id: createdById, } as LoopSchemas.Loop; } @@ -34,22 +36,29 @@ function controlledPanel(tab: HTMLElement): HTMLElement { } describe("LoopsListViewPresentation", () => { - it("shows only the selected ownership tab", async () => { + it("groups loops by ownership rather than visibility", async () => { render( , ); - const personalTab = screen.getByRole("tab", { name: "My loops (1)" }); + const personalTab = screen.getByRole("tab", { name: "My loops (2)" }); expect( within(controlledPanel(personalTab)).getByText("personal loop"), ).toBeVisible(); - expect(screen.queryByText("team loop")).not.toBeInTheDocument(); + expect( + within(controlledPanel(personalTab)).getByText("team loop"), + ).toBeVisible(); const teamTab = screen.getByRole("tab", { name: "Team loops (1)" }); await userEvent.click(teamTab); diff --git a/packages/ui/src/features/loops/components/LoopsListView.tsx b/packages/ui/src/features/loops/components/LoopsListView.tsx index 04a47926e7..aa9f739964 100644 --- a/packages/ui/src/features/loops/components/LoopsListView.tsx +++ b/packages/ui/src/features/loops/components/LoopsListView.tsx @@ -8,6 +8,8 @@ import type { LoopSchemas } from "@posthog/api-client/loops"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@posthog/quill"; import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events"; import type { UserBasic } from "@posthog/shared/domain-types"; +import { useOptionalAuthenticatedClient } from "@posthog/ui/features/auth/authClient"; +import { useCurrentUser } from "@posthog/ui/features/auth/useCurrentUser"; import { useOrgMembers } from "@posthog/ui/features/canvas/hooks/useOrgMembers"; import { StopCloudRunDialog } from "@posthog/ui/features/sessions/components/StopCloudRunDialog"; import { useSetHeaderContent } from "@posthog/ui/hooks/useSetHeaderContent"; @@ -67,6 +69,8 @@ function startLoopFromTemplate(template: LoopTemplate): void { export function LoopsListView() { const { data: loops, isLoading, isError, error } = useLoops(); + const authenticatedClient = useOptionalAuthenticatedClient(); + const { data: currentUser } = useCurrentUser({ client: authenticatedClient }); const limits = useLoopLimits(); const limitReason = limits?.atLimit === true ? loopLimitReason(limits.max) : null; @@ -134,6 +138,7 @@ export function LoopsListView() { return ( loop.visibility === "personal"); - const teamLoops = loops.filter((loop) => loop.visibility === "team"); + const personalLoops = loops.filter( + (loop) => + loop.visibility === "personal" || + (currentUserId !== null && loop.created_by_id === currentUserId), + ); + const teamLoops = loops.filter( + (loop) => + loop.visibility === "team" && + (currentUserId === null || loop.created_by_id !== currentUserId), + ); return ( diff --git a/packages/ui/src/shell/HedgehogMode.test.tsx b/packages/ui/src/shell/HedgehogMode.test.tsx index 1bcfada5f6..6efe6079da 100644 --- a/packages/ui/src/shell/HedgehogMode.test.tsx +++ b/packages/ui/src/shell/HedgehogMode.test.tsx @@ -100,6 +100,8 @@ describe("HedgehogMode", () => { expect(mocks.mount).toHaveBeenCalledTimes(1); expect(overlay.querySelector("canvas")).not.toBeNull(); expect(overlay.style.visibility).toBe("visible"); + expect(overlay).toHaveClass("absolute"); + expect(overlay).not.toHaveClass("fixed"); }); it("destroys the game and reports when the context loss callback fires", async () => { diff --git a/packages/ui/src/shell/HedgehogMode.tsx b/packages/ui/src/shell/HedgehogMode.tsx index 179825964c..e4c9d44b48 100644 --- a/packages/ui/src/shell/HedgehogMode.tsx +++ b/packages/ui/src/shell/HedgehogMode.tsx @@ -134,7 +134,7 @@ export function HedgehogMode() { zIndex: 999998, visibility: hedgehogMode && !gameDead ? "visible" : "hidden", }} - className="pointer-events-none fixed inset-0" + className="pointer-events-none absolute inset-0" /> ); } From 4ef2141c94b1d669fd5a07e529aec7e5dd86ddc1 Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Mon, 27 Jul 2026 13:12:25 -0400 Subject: [PATCH 2/2] fix(loops): wait for current user before grouping Generated-By: PostHog Code Task-Id: 61f27d1d-bdc6-43b7-a82f-0c555d7b88f4 --- .../loops/components/LoopsListView.test.tsx | 16 ++++++++++++++++ .../features/loops/components/LoopsListView.tsx | 17 ++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/features/loops/components/LoopsListView.test.tsx b/packages/ui/src/features/loops/components/LoopsListView.test.tsx index b5a030ab96..62af735344 100644 --- a/packages/ui/src/features/loops/components/LoopsListView.test.tsx +++ b/packages/ui/src/features/loops/components/LoopsListView.test.tsx @@ -36,6 +36,22 @@ function controlledPanel(tab: HTMLElement): HTMLElement { } describe("LoopsListViewPresentation", () => { + it("does not render ownership groups while identity is loading", () => { + render( + + + , + ); + + expect(screen.queryByRole("tab")).not.toBeInTheDocument(); + }); + it("groups loops by ownership rather than visibility", async () => { render( diff --git a/packages/ui/src/features/loops/components/LoopsListView.tsx b/packages/ui/src/features/loops/components/LoopsListView.tsx index f69f27141a..8798e4a908 100644 --- a/packages/ui/src/features/loops/components/LoopsListView.tsx +++ b/packages/ui/src/features/loops/components/LoopsListView.tsx @@ -70,10 +70,21 @@ function startLoopFromTemplate(template: LoopTemplate): void { export function LoopsListView() { const { data: loops, isLoading, isError, error } = useLoops(); const authenticatedClient = useOptionalAuthenticatedClient(); - const { data: currentUser } = useCurrentUser({ client: authenticatedClient }); + const { + data: currentUser, + isLoading: currentUserLoading, + isError: currentUserError, + error: currentUserQueryError, + } = useCurrentUser({ client: authenticatedClient }); const limits = useLoopLimits(); const limitReason = limits?.atLimit === true ? loopLimitReason(limits.max) : null; + let listError: unknown = null; + if (isError) { + listError = error; + } else if (currentUserError) { + listError = currentUserQueryError; + } const headerContent = useMemo( () => ( @@ -139,8 +150,8 @@ export function LoopsListView() {