Skip to content
Open
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
17 changes: 13 additions & 4 deletions apps/roam/src/components/Export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export type ExportDialogProps = {
title?: string;
columns?: Column[];
isExportDiscourseGraph?: boolean;
initialPanel?: "sendTo" | "export";
initialPanel?: "sendTo" | "export" | "publish";
};

type ExportDialogComponent = (
Expand All @@ -141,6 +141,14 @@ const EXPORT_DESTINATIONS = [
{ id: "github", label: "Send to GitHub", active: true },
];
const SEND_TO_DESTINATIONS = ["page", "graph"];
const INITIAL_PANEL_TO_TAB_ID: Record<
NonNullable<ExportDialogProps["initialPanel"]>,
string
> = {
sendTo: "sendto",
export: "export",
publish: "publish",
};

const exportDestinationById = Object.fromEntries(
EXPORT_DESTINATIONS.map((ed) => [ed.id, ed]),
Expand Down Expand Up @@ -209,18 +217,19 @@ const ExportDialog: ExportDialogComponent = ({
useState<(typeof SEND_TO_DESTINATIONS)[number]>("page");
const isSendToGraph = activeSendToDestination === "graph";
const [livePages, setLivePages] = useState<Result[]>([]);
const syncEnabled = useMemo(() => isSyncEnabled(), []);
const [selectedTabId, setSelectedTabId] = useState("sendto");
useEffect(() => {
if (initialPanel === "export") setSelectedTabId("export");
}, [initialPanel]);
if (initialPanel === "publish" && !syncEnabled) return;
if (initialPanel) setSelectedTabId(INITIAL_PANEL_TO_TAB_ID[initialPanel]);
}, [initialPanel, syncEnabled]);
const [includeDiscourseContext, setIncludeDiscourseContext] = useState(false);
const [gitHubAccessToken, setGitHubAccessToken] = useState<string | null>(
getSetting<string | null>("oauth-github", null),
);

const [canSendToGitHub, setCanSendToGitHub] = useState(false);

const syncEnabled = useMemo(() => isSyncEnabled(), []);
const [myGroups, setMyGroups] = useState<MyGroup[]>([]);
const [groupsLoading, setGroupsLoading] = useState(false);
const [groupsLoaded, setGroupsLoaded] = useState(false);
Expand Down
55 changes: 55 additions & 0 deletions apps/roam/src/components/PublishNodeTitleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Button } from "@blueprintjs/core";
import posthog from "posthog-js";
import React from "react";
import { handleTitleAdditions } from "~/utils/handleTitleAdditions";
import { openShareNodeDialog } from "~/utils/openShareNodeDialog";

const PUBLISH_TITLE_BUTTON_ATTRIBUTE = "data-roamjs-publish-node-title-button";

const PublishNodeTitleButton = ({
uid,
title,
nodeType,
}: {
uid: string;
title: string;
nodeType: string;
}): JSX.Element => (
<div className="flex space-x-2">
<Button
text="Publish"
icon="upload"
minimal
outlined
onClick={() => {
posthog.capture("Share Node: Page Title Button Triggered", {
pageUid: uid,
nodeType,
});
openShareNodeDialog({ uid, title, nodeType });
}}
/>
</div>
);

export const renderPublishNodeTitleButton = ({
h1,
uid,
title,
nodeType,
}: {
h1: HTMLHeadingElement;
uid: string;
title: string;
nodeType: string;
}): void => {
if (!uid) return;
if (h1.getAttribute(PUBLISH_TITLE_BUTTON_ATTRIBUTE) === uid) return;

h1.setAttribute(PUBLISH_TITLE_BUTTON_ATTRIBUTE, uid);
handleTitleAdditions(
h1,
<PublishNodeTitleButton uid={uid} title={title} nodeType={nodeType} />,
{ layout: "inline" },
);
};
10 changes: 9 additions & 1 deletion apps/roam/src/utils/handleTitleAdditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ import ReactDOM from "react-dom";

const ROAM_TITLE_CONTAINER_CLASS = "rm-title-display-container";
const ADDITIONS_CONTAINER_CLASS = "discourse-graph-title-additions";
const ADDITIONS_CONTAINER_CLASSES = `${ADDITIONS_CONTAINER_CLASS} flex flex-wrap items-start gap-x-2 gap-y-2`;
const INLINE_ADDITION_CLASSES = "min-w-0 flex-none";
const BLOCK_ADDITION_CLASSES = "min-w-0 basis-full grow shrink-0";

type TitleAdditionLayout = "inline" | "block";

export const handleTitleAdditions = (
h1: HTMLHeadingElement,
element: React.ReactNode,
{ layout = "block" }: { layout?: TitleAdditionLayout } = {},
): void => {
const titleDisplayContainer =
h1.closest(`.${ROAM_TITLE_CONTAINER_CLASS}`) ||
Expand All @@ -25,7 +31,7 @@ export const handleTitleAdditions = (
if (!parent) return;

container = document.createElement("div");
container.className = `${ADDITIONS_CONTAINER_CLASS} flex flex-col`;
container.className = ADDITIONS_CONTAINER_CLASSES;

const oldMarginBottom = getComputedStyle(h1).marginBottom;
const oldMarginBottomNum = Number.isFinite(parseFloat(oldMarginBottom))
Expand All @@ -45,6 +51,8 @@ export const handleTitleAdditions = (

if (React.isValidElement(element)) {
const renderContainer = document.createElement("div");
renderContainer.className =
layout === "inline" ? INLINE_ADDITION_CLASSES : BLOCK_ADDITION_CLASSES;
container.appendChild(renderContainer);
// eslint-disable-next-line react/no-deprecated
ReactDOM.render(element, renderContainer);
Expand Down
16 changes: 14 additions & 2 deletions apps/roam/src/utils/initializeObserversAndListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,18 @@ import {
import { renderNodeTagPopupButton } from "./renderNodeTagPopup";
import { renderImageToolsMenu } from "./renderImageToolsMenu";
import { mountLeftSidebar } from "~/components/LeftSidebarView";
import { getFeatureFlag } from "~/components/settings/utils/accessors";
import { getCleanTagText } from "~/components/settings/NodeConfig";
import { getNodeTagStyles } from "~/utils/getDiscourseNodeColors";
import { renderPossibleDuplicates } from "~/components/VectorDuplicateMatches";
import { renderPublishNodeTitleButton } from "~/components/PublishNodeTitleButton";
import { renderCanvasEmbed } from "~/components/canvas/CanvasEmbed";
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
import findDiscourseNode from "./findDiscourseNode";
import {
bulkReadSettings,
getFeatureFlag,
isSyncEnabled,
type SettingsSnapshot,
} from "~/components/settings/utils/accessors";
import {
Expand Down Expand Up @@ -123,7 +125,17 @@ export const initObservers = ({

const isDiscourseNode = node && node.backedBy !== "default";
if (isDiscourseNode) {
renderDiscourseContext({ h1, uid });
if (isSyncEnabled() && node.backedBy === "user") {
renderPublishNodeTitleButton({
h1,
uid,
title,
nodeType: node.type,
});
}
if (settings.personalSettings[PERSONAL_KEYS.discourseContextOverlay]) {
renderDiscourseContext({ h1, uid });
}
if (getFeatureFlag("Duplicate node alert enabled")) {
renderPossibleDuplicates(h1, title, node);
}
Expand Down
17 changes: 17 additions & 0 deletions apps/roam/src/utils/openShareNodeDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { render as exportRender } from "~/components/Export";

export const openShareNodeDialog = ({
uid,
title,
nodeType,
}: {
uid: string;
title: string;
nodeType: string;
}): void => {
exportRender({
results: [{ uid, text: title, type: nodeType }],
isExportDiscourseGraph: true,
initialPanel: "publish",
});
};
53 changes: 53 additions & 0 deletions apps/roam/src/utils/registerCommandPaletteCommands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { openQueryDrawer } from "~/components/QueryDrawer";
import { render as exportRender } from "~/components/Export";
import { render as renderToast } from "roamjs-components/components/Toast";
import { openShareNodeDialog } from "~/utils/openShareNodeDialog";
import { createBlock, updateBlock } from "roamjs-components/writes";
import {
getCurrentPageUid,
Expand Down Expand Up @@ -29,6 +30,7 @@ import {
getPersonalSetting,
setPersonalSetting,
setGlobalSetting,
isSyncEnabled,
} from "~/components/settings/utils/accessors";
import {
DISCOURSE_NODE_KEYS,
Expand Down Expand Up @@ -244,6 +246,54 @@ export const registerCommandPaletteCommands = (onloadArgs: OnloadArgs) => {
});
};

const shareCurrentNode = () => {
if (!isSyncEnabled()) {
renderToast({
id: "share-node-sync-disabled",
content: "Sync must be enabled to publish discourse nodes.",
});
return;
}

const pageUid = getCurrentPageUid();
if (!pageUid) {
renderToast({
id: "share-node-no-page",
content: "Navigate to a discourse node page to share it.",
});
return;
}

const pageTitle = getPageTitleByPageUid(pageUid);
if (!pageTitle) {
renderToast({
id: "share-node-no-title",
content: "Could not determine the current page title.",
});
return;
}

const discourseNode = findDiscourseNode({ uid: pageUid, title: pageTitle });
if (!discourseNode || discourseNode.backedBy !== "user") {
renderToast({
id: "share-node-not-a-node",
content: "This page is not a publishable discourse node.",
});
return;
}

posthog.capture("Share Node: Current Node Command Triggered", {
pageUid,
nodeType: discourseNode.type,
});

openShareNodeDialog({
uid: pageUid,
title: pageTitle,
nodeType: discourseNode.type,
});
};

const exportDiscourseGraph = async () => {
posthog.capture("Export: Discourse Graph Command Triggered");
const discourseNodes = getDiscourseNodes().filter(excludeDefaultNodes);
Expand Down Expand Up @@ -364,6 +414,9 @@ export const registerCommandPaletteCommands = (onloadArgs: OnloadArgs) => {
void addCommand("DG: Export - Current page", exportCurrentPage);
void addCommand("DG: Export - Discourse graph", exportDiscourseGraph);
void addCommand("DG: Open - Discourse settings", renderSettingsPopup);
if (isSyncEnabled()) {
void addCommand("DG: Share current node", shareCurrentNode);
}
if (getFeatureFlag("Advanced node search enabled")) {
void addCommand("DG: Open Node Search", () => {
posthog.capture("Node Search: Open Command Triggered");
Expand Down
1 change: 1 addition & 0 deletions apps/roam/src/utils/renderLinkedReferenceAdditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const renderDiscourseContext = ({
uid,
id: nanoid(),
}),
{ layout: "inline" },
);
h1.setAttribute("data-roamjs-top-discourse-context", "true");
};
Expand Down