diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index 6d1b5f071..0f1c350fb 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -91,7 +91,7 @@ import { type NodeUidWithType, } from "~/utils/publishNodesToGroups"; import { getLoggedInClient, getSupabaseContext } from "~/utils/supabaseContext"; -import { isSyncEnabled } from "~/components/settings/utils/accessors"; +import { isNodeSharingEnabled } from "~/components/settings/utils/accessors"; const ExportProgress = ({ id }: { id: string }) => { const [progress, setProgress] = useState(0); @@ -218,12 +218,12 @@ const ExportDialog: ExportDialogComponent = ({ useState<(typeof SEND_TO_DESTINATIONS)[number]>("page"); const isSendToGraph = activeSendToDestination === "graph"; const [livePages, setLivePages] = useState([]); - const syncEnabled = useMemo(() => isSyncEnabled(), []); + const sharingEnabled = useMemo(() => isNodeSharingEnabled(), []); const [selectedTabId, setSelectedTabId] = useState("sendto"); useEffect(() => { - if (initialPanel === "publish" && !syncEnabled) return; + if (initialPanel === "publish" && !sharingEnabled) return; if (initialPanel) setSelectedTabId(INITIAL_PANEL_TO_TAB_ID[initialPanel]); - }, [initialPanel, syncEnabled]); + }, [initialPanel, sharingEnabled]); const [includeDiscourseContext, setIncludeDiscourseContext] = useState(false); const [gitHubAccessToken, setGitHubAccessToken] = useState( getSetting("oauth-github", null), @@ -240,7 +240,7 @@ const ExportDialog: ExportDialogComponent = ({ const publishableNodes = useMemo( () => - syncEnabled + sharingEnabled ? results .map((r) => { const node = findDiscourseNode({ uid: r.uid }); @@ -250,7 +250,7 @@ const ExportDialog: ExportDialogComponent = ({ }) .filter((n): n is NodeUidWithType => n !== null) : [], - [results, syncEnabled], + [results, sharingEnabled], ); const nonDiscourseCount = results.length - publishableNodes.length; @@ -808,7 +808,7 @@ const ExportDialog: ExportDialogComponent = ({ }; useEffect(() => { if ( - !syncEnabled || + !sharingEnabled || !isOpen || selectedTabId !== "publish" || groupsLoaded || @@ -829,7 +829,7 @@ const ExportDialog: ExportDialogComponent = ({ setGroupsLoaded(true); } })(); - }, [syncEnabled, isOpen, selectedTabId, groupsLoaded, groupsLoading]); + }, [sharingEnabled, isOpen, selectedTabId, groupsLoaded, groupsLoading]); const handlePublish = async () => { setPublishError(""); @@ -840,7 +840,7 @@ const ExportDialog: ExportDialogComponent = ({ if (!client || !context) throw new Error("Could not connect to sync."); const { publishedNodeUids, - skippedUnsyncedUids, + failedUpsertUids, okGroupIds, failedGroupIds, } = await publishNodeUidsWithTypeToGroups({ @@ -849,10 +849,14 @@ const ExportDialog: ExportDialogComponent = ({ groupIds: selectedGroupIds, nodeUids: publishableNodes, }); + const selectedNodeUids = new Set(publishableNodes.map((n) => n.uid)); + const failedNodeCount = failedUpsertUids.filter((uid) => + selectedNodeUids.has(uid), + ).length; posthog.capture("Export Dialog: Publish", { groupCount: okGroupIds.length, publishedNodeCount: publishedNodeUids.length, - skippedUnsyncedCount: skippedUnsyncedUids.length, + failedUpsertCount: failedUpsertUids.length, nonDiscourseCount, failedGroupCount: failedGroupIds.length, }); @@ -866,10 +870,8 @@ const ExportDialog: ExportDialogComponent = ({ }.`, ] : ["No nodes were published."]; - if (skippedUnsyncedUids.length) - messages.push( - `${skippedUnsyncedUids.length} not synced yet — try again shortly.`, - ); + if (failedNodeCount) + messages.push(`${failedNodeCount} failed to publish.`); if (nonDiscourseCount) messages.push(`${nonDiscourseCount} skipped (not discourse nodes).`); if (failedGroupIds.length) @@ -881,7 +883,9 @@ const ExportDialog: ExportDialogComponent = ({ renderToast({ content: messages.join(" "), intent: - failedGroupIds.length || !hasPublishedNodes ? "warning" : "success", + failedGroupIds.length || failedNodeCount || !hasPublishedNodes + ? "warning" + : "success", id: "query-builder-publish-success", }); if (hasPublishedNodes) onClose(); @@ -1263,7 +1267,7 @@ const ExportDialog: ExportDialogComponent = ({ > - {syncEnabled && ( + {sharingEnabled && ( )} diff --git a/apps/roam/src/components/RefreshImportedNodeTitleButton.tsx b/apps/roam/src/components/RefreshImportedNodeTitleButton.tsx new file mode 100644 index 000000000..2fa6ac04a --- /dev/null +++ b/apps/roam/src/components/RefreshImportedNodeTitleButton.tsx @@ -0,0 +1,63 @@ +import { Button } from "@blueprintjs/core"; +import posthog from "posthog-js"; +import React, { useState } from "react"; +import renderToast from "roamjs-components/components/Toast"; +import { handleTitleAdditions } from "~/utils/handleTitleAdditions"; +import { refreshImportedNode } from "~/utils/refreshImportedNode"; + +const REFRESH_TITLE_BUTTON_ATTRIBUTE = + "data-roamjs-refresh-imported-node-title-button"; + +const RefreshImportedNodeTitleButton = ({ + uid, +}: { + uid: string; +}): JSX.Element => { + const [refreshing, setRefreshing] = useState(false); + + const refresh = async (): Promise => { + setRefreshing(true); + try { + const result = await refreshImportedNode({ pageUid: uid, force: true }); + const failed = result.status === "failed"; + renderToast({ + id: failed + ? "refresh-imported-node-failed" + : "refresh-imported-node-success", + intent: failed ? "danger" : "success", + content: result.message, + }); + } finally { + setRefreshing(false); + } + }; + + return ( +