From e6ef0829619ea31ef5a990989ef434ac0795eebb Mon Sep 17 00:00:00 2001 From: sid597 Date: Wed, 26 Aug 2026 15:54:44 +0530 Subject: [PATCH 1/2] ENG-1968 Pre-check groups a node is already shared to in the Publish tab --- apps/roam/src/components/Export.tsx | 27 ++++++++++++++++++++- apps/roam/src/utils/publishNodesToGroups.ts | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index 6d1b5f071..bee21b22d 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -87,6 +87,7 @@ import { AddReferencedNodeType } from "./canvas/DiscourseRelationShape/Discourse import posthog from "posthog-js"; import { getMyGroups, type MyGroup } from "@repo/database/lib/groups"; import { + getAllPublishedIdsByGroup, publishNodeUidsWithTypeToGroups, type NodeUidWithType, } from "~/utils/publishNodesToGroups"; @@ -821,6 +822,23 @@ const ExportDialog: ExportDialogComponent = ({ const client = await getLoggedInClient(); if (!client) throw new Error("Could not connect to sync."); const groups = await getMyGroups(client); + const context = await getSupabaseContext(); + if (context && groups.length && publishableNodes.length) { + const publishedIdsByGroup = await getAllPublishedIdsByGroup( + client, + context.spaceId, + groups.map((g) => g.id), + ); + setSelectedGroupIds( + groups + .map((g) => g.id) + .filter((id) => + publishableNodes.every(({ uid }) => + publishedIdsByGroup[id].has(uid), + ), + ), + ); + } setMyGroups(groups); } catch (e) { setGroupsError((e as Error).message || "Failed to load groups."); @@ -829,7 +847,14 @@ const ExportDialog: ExportDialogComponent = ({ setGroupsLoaded(true); } })(); - }, [syncEnabled, isOpen, selectedTabId, groupsLoaded, groupsLoading]); + }, [ + syncEnabled, + isOpen, + selectedTabId, + groupsLoaded, + groupsLoading, + publishableNodes, + ]); const handlePublish = async () => { setPublishError(""); diff --git a/apps/roam/src/utils/publishNodesToGroups.ts b/apps/roam/src/utils/publishNodesToGroups.ts index b6c27bfb5..0042338a5 100644 --- a/apps/roam/src/utils/publishNodesToGroups.ts +++ b/apps/roam/src/utils/publishNodesToGroups.ts @@ -31,7 +31,7 @@ export type NodeUidWithType = { type: string; }; -const getAllPublishedIdsByGroup = async ( +export const getAllPublishedIdsByGroup = async ( client: DGSupabaseClient, spaceId: number, groupIds: string[], From ffdafe1c1bc3b3ec6164d4cb878031e2f0528ebf Mon Sep 17 00:00:00 2001 From: sid597 Date: Mon, 31 Aug 2026 16:53:38 +0530 Subject: [PATCH 2/2] ENG-1968 Recompute prechecked groups on reopen and paginate the published-ids lookup --- apps/roam/src/components/Export.tsx | 65 +++++++++++---------- apps/roam/src/utils/publishNodesToGroups.ts | 32 ++++++---- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index bee21b22d..1e3fade49 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -808,53 +808,56 @@ const ExportDialog: ExportDialogComponent = ({ } }; useEffect(() => { - if ( - !syncEnabled || - !isOpen || - selectedTabId !== "publish" || - groupsLoaded || - groupsLoading - ) - return; + if (isOpen) return; + setGroupsLoaded(false); + setSelectedGroupIds([]); + setGroupsError(""); + }, [isOpen]); + useEffect(() => { + if (!syncEnabled || !isOpen || selectedTabId !== "publish") return; + let active = true; setGroupsLoading(true); + setGroupsLoaded(false); + setGroupsError(""); void (async () => { try { const client = await getLoggedInClient(); if (!client) throw new Error("Could not connect to sync."); const groups = await getMyGroups(client); const context = await getSupabaseContext(); + let preselectedGroupIds: string[] = []; if (context && groups.length && publishableNodes.length) { - const publishedIdsByGroup = await getAllPublishedIdsByGroup( + const publishedIdsByGroup = await getAllPublishedIdsByGroup({ client, - context.spaceId, - groups.map((g) => g.id), - ); - setSelectedGroupIds( - groups - .map((g) => g.id) - .filter((id) => - publishableNodes.every(({ uid }) => - publishedIdsByGroup[id].has(uid), - ), + spaceId: context.spaceId, + groupIds: groups.map((g) => g.id), + sourceLocalIds: publishableNodes.map(({ uid }) => uid), + }); + preselectedGroupIds = groups + .map((g) => g.id) + .filter((id) => + publishableNodes.every(({ uid }) => + publishedIdsByGroup[id].has(uid), ), - ); + ); } + if (!active) return; + setSelectedGroupIds(preselectedGroupIds); setMyGroups(groups); } catch (e) { - setGroupsError((e as Error).message || "Failed to load groups."); + if (active) + setGroupsError((e as Error).message || "Failed to load groups."); } finally { - setGroupsLoading(false); - setGroupsLoaded(true); + if (active) { + setGroupsLoading(false); + setGroupsLoaded(true); + } } })(); - }, [ - syncEnabled, - isOpen, - selectedTabId, - groupsLoaded, - groupsLoading, - publishableNodes, - ]); + return () => { + active = false; + }; + }, [syncEnabled, isOpen, selectedTabId, publishableNodes]); const handlePublish = async () => { setPublishError(""); diff --git a/apps/roam/src/utils/publishNodesToGroups.ts b/apps/roam/src/utils/publishNodesToGroups.ts index 0042338a5..19e82e11e 100644 --- a/apps/roam/src/utils/publishNodesToGroups.ts +++ b/apps/roam/src/utils/publishNodesToGroups.ts @@ -20,6 +20,7 @@ import { } from "@repo/database/lib/crossAppConverters"; import { ensurePartialSpaceAccess } from "@repo/database/lib/groups"; import { isIgnorableUpsertError } from "@repo/database/lib/contextFunctions"; +import { getAllPages } from "@repo/database/lib/pagination"; import { ridToSpaceUriAndLocalId } from "@repo/database/lib/rid"; import getDiscourseNodes from "./getDiscourseNodes"; import { difference, intersection } from "@repo/utils/setOperations"; @@ -31,21 +32,32 @@ export type NodeUidWithType = { type: string; }; -export const getAllPublishedIdsByGroup = async ( - client: DGSupabaseClient, - spaceId: number, - groupIds: string[], -): Promise>> => { - const response = await client +export const getAllPublishedIdsByGroup = async ({ + client, + spaceId, + groupIds, + sourceLocalIds, +}: { + client: DGSupabaseClient; + spaceId: number; + groupIds: string[]; + sourceLocalIds?: string[]; +}): Promise>> => { + let query = client .from("ResourceAccess") .select("account_uid, source_local_id") .eq("space_id", spaceId) .in("account_uid", groupIds); - if (response.error) throw response.error; + if (sourceLocalIds) query = query.in("source_local_id", sourceLocalIds); + const rows = await getAllPages( + query.order("account_uid").order("source_local_id"), + 1000, + ); + if (!Array.isArray(rows)) throw rows; const publishedIdsByGroupId = Object.fromEntries( groupIds.map((gid) => [gid, new Set()]), ); - response.data.forEach(({ account_uid, source_local_id }) => { + rows.forEach(({ account_uid, source_local_id }) => { publishedIdsByGroupId[account_uid].add(source_local_id); }); @@ -141,11 +153,11 @@ export const gatherCorrespondingRelations = async ({ (forNodeIds.has(r.sourceUid) || forNodeIds.has(r.destinationUid)), ) : allRelations.filter((r) => r.importedFromRid === undefined); - const publishedIdsByGroup = await getAllPublishedIdsByGroup( + const publishedIdsByGroup = await getAllPublishedIdsByGroup({ client, spaceId, groupIds, - ); + }); // calculate separately to avoid case of a relation between nodes published to or from different groups const relevantRelationIdsPerGroupId = Object.fromEntries( groupIds.map((groupId) => {