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
11 changes: 9 additions & 2 deletions apps/app/src/components/layout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { useSetRootComposeProjectId } from "@/lib/root-compose-selection";
import { IframeDragGuardOverlay } from "@/lib/iframe-drag-guard";
import { dispatchBrowserViewBoundsSync } from "@/lib/browser-view-bounds-sync";
import { useFaviconBadge } from "@/lib/favicon-color-preference";
import { ThreadDetailDataOwner } from "@/views/thread-detail/ThreadDetailDataOwner";

const SIDEBAR_WIDTH_KEY = "bb.sidebar.width";
const SIDEBAR_OPEN_KEY = "bb.sidebar.open";
Expand Down Expand Up @@ -389,9 +390,7 @@ export function AppLayout({ children }: AppLayoutProps) {
];
}, [sidebarNavigationQuery.data]);
const threadDetailBootstrapQuery = useThreadDetailBootstrap(threadId ?? "", {
composerBootstrapPrefetch: isThreadView && Boolean(threadId),
enabled: isThreadView && Boolean(threadId),
timelinePrefetch: isThreadView && Boolean(threadId),
});
const hasThreadDetailBootstrapSettled =
threadDetailBootstrapQuery.isSuccess || threadDetailBootstrapQuery.isError;
Expand Down Expand Up @@ -574,6 +573,14 @@ export function AppLayout({ children }: AppLayoutProps) {
return (
<ProjectActionsProvider>
<ThreadActionsProvider>
<ThreadDetailDataOwner
bootstrapThread={threadDetailBootstrapQuery.data}
cachedThread={thread}
enabled={isThreadView && Boolean(threadId)}
hasThreadDetailBootstrapSettled={hasThreadDetailBootstrapSettled}
projectId={projectId}
threadId={threadId}
/>
<IframeDragGuardOverlay active={isSidebarResizing} />
<SidebarStateBridge
providerRef={providerRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface UseThreadStorageViewerParams {
activePath: string | null;
fileListEnabled?: boolean;
fileListOptions?: ThreadStorageFileListOptions;
fileListRefetchOnMount?: boolean | "always";
fileListStaleTime?: number;
filePreviewEnabled?: boolean;
threadId?: string;
}
Expand All @@ -19,6 +21,8 @@ export function useThreadStorageViewer({
activePath,
fileListEnabled = true,
fileListOptions = DEFAULT_THREAD_STORAGE_FILE_LIST_OPTIONS,
fileListRefetchOnMount,
fileListStaleTime,
filePreviewEnabled = true,
threadId,
}: UseThreadStorageViewerParams) {
Expand All @@ -30,6 +34,8 @@ export function useThreadStorageViewer({
refetch: refetchThreadStorageFiles,
} = useThreadStorageFiles(threadId ?? "", fileListOptions, {
enabled: hasThread && fileListEnabled,
refetchOnMount: fileListRefetchOnMount,
staleTime: fileListStaleTime,
});
const {
data: threadStorageFilePreview,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ const CACHE_OWNER_QUERY_KEY_IMPORTS: CacheOwnerQueryKeyImportRegistry = {
"hostQueryKey",
"hostsQueryKey",
"threadQueryKey",
"threadTimelineFeedQueryKey",
],
"hooks/cache-owners/thread-list-cache-owner.ts": [
"sidebarNavigationQueryKey",
Expand Down
33 changes: 0 additions & 33 deletions apps/app/src/hooks/cache-owners/thread-detail-cache-owner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@ import type {
ThreadResponse,
ThreadWithIncludesResponse,
} from "@bb/server-contract";
import * as api from "@/lib/api";
import { getCachedThreadListPlaceholder } from "./query-cache";
import {
fetchAndHydrateThreadComposerBootstrap,
threadComposerBootstrapQueryKey,
} from "../queries/thread-composer-bootstrap-query";
import {
environmentQueryKey,
hostQueryKey,
hostsQueryKey,
threadQueryKey,
threadTimelineFeedQueryKey,
} from "../queries/query-keys";

type HostList = Host[];
Expand All @@ -32,10 +26,8 @@ interface CachedThreadProjectIdArgs {
}

export interface ThreadDetailBootstrapIngestionArgs {
composerBootstrapPrefetch: boolean;
queryClient: QueryClient;
thread: ThreadWithIncludesResponse;
timelinePrefetch: boolean;
}

function stripThreadIncludes(
Expand Down Expand Up @@ -63,10 +55,8 @@ function upsertHostList({ host, hosts }: UpsertHostListArgs): HostList {
}

export function ingestThreadDetailBootstrap({
composerBootstrapPrefetch,
queryClient,
thread,
timelinePrefetch,
}: ThreadDetailBootstrapIngestionArgs): void {
queryClient.setQueryData(
threadQueryKey(thread.id),
Expand All @@ -88,29 +78,6 @@ export function ingestThreadDetailBootstrap({
);
}

if (timelinePrefetch) {
void queryClient.prefetchQuery({
queryKey: threadTimelineFeedQueryKey(thread.id),
queryFn: () =>
api.getThreadTimelineFeed({
id: thread.id,
}),
});
}

if (composerBootstrapPrefetch) {
const environmentId = thread.environmentId ?? null;
void queryClient.prefetchQuery({
queryKey: threadComposerBootstrapQueryKey(thread.id, environmentId),
queryFn: () =>
fetchAndHydrateThreadComposerBootstrap({
environmentId,
providerId: thread.providerId,
queryClient,
threadId: thread.id,
}),
});
}
}

export function getCachedThreadProjectId({
Expand Down
10 changes: 4 additions & 6 deletions apps/app/src/hooks/queries/environment-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ import { requireEnabledQueryArg } from "./query-helpers";

interface QueryOptions {
enabled?: boolean;
}

interface EnvironmentQueryOptions extends QueryOptions {
refetchOnMount?: boolean | "always";
staleTime?: number;
}

Expand Down Expand Up @@ -62,7 +60,7 @@ function requireEnvironmentId(

export function useEnvironment(
environmentId: string | null | undefined,
options?: EnvironmentQueryOptions,
options?: QueryOptions,
) {
const enabled = (options?.enabled ?? true) && Boolean(environmentId);
useEnvironmentDetailRealtimeSubscription(environmentId, { enabled });
Expand Down Expand Up @@ -98,9 +96,9 @@ export function useEnvironmentWorkStatus(
enabled,
// Subscriptions can be absent while no UI is listening, so remount must
// establish a fresh baseline instead of trusting cached data.
refetchOnMount: "always",
refetchOnMount: options?.refetchOnMount ?? "always",
refetchOnWindowFocus: false,
staleTime: 0,
staleTime: options?.staleTime ?? 0,
placeholderData: (previousData, previousQuery) =>
environmentId
? resolveEnvironmentWorkStatusPlaceholder(
Expand Down
12 changes: 3 additions & 9 deletions apps/app/src/hooks/queries/thread-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ interface QueryOptions {
const THREAD_LIST_STALE_TIME_MS = 10_000;
export const THREAD_MENTION_CANDIDATE_LIMIT = 200;

interface ThreadDetailBootstrapQueryOptions extends QueryOptions {
composerBootstrapPrefetch?: boolean;
timelinePrefetch?: boolean;
}

type ThreadTimelineFeedQueryOptions = QueryOptions;

type ThreadTimelineRowDetailQueryOptions = QueryOptions;
Expand Down Expand Up @@ -486,7 +481,7 @@ export function useThread(id: string, options?: QueryOptions) {

export function useThreadDetailBootstrap(
id: string,
options?: ThreadDetailBootstrapQueryOptions,
options?: QueryOptions,
) {
const queryClient = useQueryClient();
const enabled = (options?.enabled ?? true) && Boolean(id);
Expand All @@ -499,10 +494,8 @@ export function useThreadDetailBootstrap(
requireThreadId(id, "useThreadDetailBootstrap"),
);
ingestThreadDetailBootstrap({
composerBootstrapPrefetch: options?.composerBootstrapPrefetch ?? false,
queryClient,
thread,
timelinePrefetch: options?.timelinePrefetch ?? false,
});
return thread;
},
Expand Down Expand Up @@ -591,8 +584,9 @@ export function useThreadStorageFiles(
enabled,
// Subscriptions can be absent while no UI is listening, so remount must
// establish a fresh baseline instead of trusting cached data.
refetchOnMount: "always",
refetchOnMount: options?.refetchOnMount ?? "always",
refetchOnWindowFocus: false,
staleTime: options?.staleTime,
});
}

Expand Down
2 changes: 2 additions & 0 deletions apps/app/src/hooks/queries/thread-terminal-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { requireEnabledQueryArg } from "./query-helpers";

interface QueryOptions {
enabled?: boolean;
staleTime?: number;
}

interface CreateThreadTerminalMutationRequest
Expand Down Expand Up @@ -49,6 +50,7 @@ export function useThreadTerminals(id: string, options?: QueryOptions) {
),
enabled: (options?.enabled ?? true) && Boolean(id),
refetchOnWindowFocus: false,
staleTime: options?.staleTime,
});
}

Expand Down
107 changes: 107 additions & 0 deletions apps/app/src/views/thread-detail/ThreadDetailDataOwner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import type {
ThreadResponse,
ThreadWithIncludesResponse,
} from "@bb/server-contract";
import { useEnvironment } from "@/hooks/queries/environment-queries";
import { useThreadComposerBootstrap } from "@/hooks/queries/thread-composer-bootstrap-query";
import {
useThreadPendingInteractions,
useThreadSchedules,
useThreadStorageFiles,
useThreadTimelineFeed,
useThreads,
} from "@/hooks/queries/thread-queries";
import { useThreadTerminals } from "@/hooks/queries/thread-terminal-queries";
import { DEFAULT_THREAD_STORAGE_FILE_LIST_OPTIONS } from "@/lib/thread-storage-files";
import { resolveThreadComposerBootstrapReady } from "./threadDetailComposerBootstrapState";

const THREAD_DETAIL_DATA_OWNER_STALE_TIME_MS = 10_000;

interface ThreadDetailDataOwnerProps {
bootstrapThread?: ThreadWithIncludesResponse;
cachedThread?: ThreadResponse;
enabled: boolean;
hasThreadDetailBootstrapSettled: boolean;
projectId?: string;
threadId?: string;
}

export function ThreadDetailDataOwner({
bootstrapThread,
cachedThread,
enabled,
hasThreadDetailBootstrapSettled,
projectId,
threadId,
}: ThreadDetailDataOwnerProps) {
const routeThreadId = threadId ?? "";
const bootstrapThreadForRoute =
bootstrapThread?.id === routeThreadId ? bootstrapThread : undefined;
const cachedThreadForRoute =
cachedThread?.id === routeThreadId ? cachedThread : undefined;
const thread = bootstrapThreadForRoute ?? cachedThreadForRoute;
const activeThreadId = thread?.id ?? "";
const activeProjectId = thread?.projectId ?? projectId;
const environmentId = thread?.environmentId ?? undefined;
const canLoadThreadData =
enabled && hasThreadDetailBootstrapSettled && Boolean(activeThreadId);

useEnvironment(environmentId, {
enabled: canLoadThreadData && Boolean(environmentId),
staleTime: THREAD_DETAIL_DATA_OWNER_STALE_TIME_MS,
});

const threadComposerBootstrapQuery = useThreadComposerBootstrap(
activeThreadId,
{
enabled: canLoadThreadData,
environmentId,
providerId: thread?.providerId,
},
);
const hasThreadComposerBootstrapData =
threadComposerBootstrapQuery.data !== undefined;
const hasThreadComposerBootstrapReady = resolveThreadComposerBootstrapReady({
hasData: hasThreadComposerBootstrapData,
isError: threadComposerBootstrapQuery.isError,
isFetching: threadComposerBootstrapQuery.isFetching,
isSuccess: threadComposerBootstrapQuery.isSuccess,
});

useThreads(
{
archived: false,
projectId: activeProjectId,
},
{
enabled: canLoadThreadData && Boolean(activeProjectId),
},
);
useThreadTimelineFeed(activeThreadId, {
enabled: canLoadThreadData,
staleTime: Infinity,
});
useThreadSchedules(activeThreadId, {
enabled: canLoadThreadData,
staleTime: THREAD_DETAIL_DATA_OWNER_STALE_TIME_MS,
});
useThreadStorageFiles(
activeThreadId,
DEFAULT_THREAD_STORAGE_FILE_LIST_OPTIONS,
{
enabled: canLoadThreadData,
staleTime: THREAD_DETAIL_DATA_OWNER_STALE_TIME_MS,
},
);
useThreadTerminals(activeThreadId, {
enabled: canLoadThreadData,
staleTime: THREAD_DETAIL_DATA_OWNER_STALE_TIME_MS,
});
useThreadPendingInteractions(activeThreadId, {
enabled: hasThreadComposerBootstrapReady,
staleTime: hasThreadComposerBootstrapData
? THREAD_DETAIL_DATA_OWNER_STALE_TIME_MS
: undefined,
});
return null;
}
Loading
Loading