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
96 changes: 66 additions & 30 deletions apps/roam/src/components/Export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import {
ProgressBar,
Toaster,
Toast,
Tooltip,
Tab,
Tabs,
RadioGroup,
Radio,
FormGroup,
Collapse,
} from "@blueprintjs/core";
import React, { useState, useEffect, useMemo, FormEvent } from "react";
import React, { useState, useEffect, useMemo, useRef, FormEvent } from "react";
import MenuItemSelect from "roamjs-components/components/MenuItemSelect";
import { saveAs } from "file-saver";
import { Result } from "roamjs-components/types/query-builder";
Expand Down Expand Up @@ -91,7 +90,14 @@ import {
type NodeUidWithType,
} from "~/utils/publishNodesToGroups";
import { getLoggedInClient, getSupabaseContext } from "~/utils/supabaseContext";
import { isNodeSharingEnabled } from "~/components/settings/utils/accessors";
import {
bulkReadSettings,
isNodeSharingEnabled,
} from "~/components/settings/utils/accessors";
import refreshConfigTree from "~/utils/refreshConfigTree";
import { flushPendingSettingWrites } from "~/utils/pendingSettingWrites";
import ExportOptions from "./ExportOptions";
import Description from "~/components/settings/SettingsDescription";

const ExportProgress = ({ id }: { id: string }) => {
const [progress, setProgress] = useState(0);
Expand Down Expand Up @@ -225,6 +231,21 @@ const ExportDialog: ExportDialogComponent = ({
if (initialPanel) setSelectedTabId(INITIAL_PANEL_TO_TAB_ID[initialPanel]);
}, [initialPanel, sharingEnabled]);
const [includeDiscourseContext, setIncludeDiscourseContext] = useState(false);
const [exportOptionsOpen, setExportOptionsOpen] = useState(false);
const exportOptionsOpened = useRef(false);
// Collapse unmounts the panels while closed, so each open seeds them from current values.
const exportGlobalSettings = useMemo(
() => bulkReadSettings().globalSettings,
// eslint-disable-next-line react-hooks/exhaustive-deps
[exportOptionsOpen],
);

// Option panels also write legacy config blocks, so refresh the tree as SettingsDialog
// does, but only if they were opened: refreshConfigTree re-reads every node page.
const closeDialog = (): void => {
if (exportOptionsOpened.current) refreshConfigTree();
onClose();
};
const [gitHubAccessToken, setGitHubAccessToken] = useState<string | null>(
getSetting<string | null>("oauth-github", null),
);
Expand Down Expand Up @@ -756,7 +777,7 @@ const ExportDialog: ExportDialogComponent = ({
});
} finally {
setLoading(false);
onClose();
closeDialog();
}
};

Expand Down Expand Up @@ -796,7 +817,7 @@ const ExportDialog: ExportDialogComponent = ({
fileCount: files.length,
});
}
onClose();
closeDialog();
} catch (e) {
setError("Failed to export files.");
posthog.capture("Export Dialog: Export Failed", {
Expand Down Expand Up @@ -888,7 +909,7 @@ const ExportDialog: ExportDialogComponent = ({
: "success",
id: "query-builder-publish-success",
});
if (hasPublishedNodes) onClose();
if (hasPublishedNodes) closeDialog();
} catch (e) {
internalError({
error: e as Error,
Expand Down Expand Up @@ -951,41 +972,53 @@ const ExportDialog: ExportDialogComponent = ({
/>
</Label>

<div className="flex items-end justify-between">
<div className="mt-2 flex items-center justify-between gap-4">
<Button
minimal={true}
small={true}
icon={exportOptionsOpen ? "chevron-down" : "chevron-right"}
text="Export options"
onClick={() => {
const nextOpen = !exportOptionsOpen;
setExportOptionsOpen(nextOpen);
if (nextOpen) exportOptionsOpened.current = true;
posthog.capture("Export Dialog: Options Toggled", {
open: nextOpen,
});
}}
/>
<span>
{typeof results === "function"
? "Calculating number of results..."
: `Exporting ${results.length} results`}
</span>
<div className="flex flex-col items-end">
<FormGroup className={`m-0`} inline>
</div>
<Collapse isOpen={exportOptionsOpen}>
<div className="max-h-64 overflow-y-auto">
<div className="px-1 pt-1">
<Checkbox
alignIndicator={"right"}
checked={includeDiscourseContext}
onChange={(e) => {
onChange={(e) =>
setIncludeDiscourseContext(
(e.target as HTMLInputElement).checked,
);
}}
)
}
labelElement={
<Tooltip
className="m-0"
content={
"Include the discourse context of each result in the export."
}
>
<span>Discourse context</span>
</Tooltip>
<>
Discourse context
<Description description="Include the discourse context of each result in the export." />
</>
}
/>
</FormGroup>
</div>
<ExportOptions globalSettings={exportGlobalSettings} />
</div>
</div>
</Collapse>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<span className="text-red-700">{error}</span>
<Button text={"Cancel"} intent={Intent.NONE} onClick={onClose} />
<Button text={"Cancel"} intent={Intent.NONE} onClick={closeDialog} />
<Button
text={"Export"}
intent={Intent.PRIMARY}
Expand All @@ -1009,6 +1042,9 @@ const ExportDialog: ExportDialogComponent = ({
// eslint-disable-next-line @typescript-eslint/no-misused-promises
setTimeout(async () => {
try {
// Awaited, not just fired: a commit only starts the Roam update, and an option edited
// a moment ago would otherwise still read as its previous value here.
await flushPendingSettingWrites();
const exportType = exportTypes.find(
(e) => e.name === activeExportType,
);
Expand Down Expand Up @@ -1052,7 +1088,7 @@ const ExportDialog: ExportDialogComponent = ({
destination: activeExportDestination,
fileCount: files.length,
});
onClose();
closeDialog();
}
} catch (error) {
const e = error as Error;
Expand All @@ -1072,7 +1108,7 @@ const ExportDialog: ExportDialogComponent = ({
destination: activeExportDestination,
fileCount: files.length,
});
onClose();
closeDialog();
return;
}

Expand All @@ -1089,7 +1125,7 @@ const ExportDialog: ExportDialogComponent = ({
destination: activeExportDestination,
fileCount: files.length,
});
onClose();
closeDialog();
});
} else {
setError(`Unsupported export type: ${exportType}`);
Expand Down Expand Up @@ -1164,7 +1200,7 @@ const ExportDialog: ExportDialogComponent = ({
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button text={"Cancel"} intent={Intent.NONE} onClick={onClose} />
<Button text={"Cancel"} intent={Intent.NONE} onClick={closeDialog} />
<Button
text={`Send ${
isSendToGraph ? livePages.length : results.length
Expand Down Expand Up @@ -1224,7 +1260,7 @@ const ExportDialog: ExportDialogComponent = ({
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<span className="text-red-700">{publishError}</span>
<Button text={"Cancel"} intent={Intent.NONE} onClick={onClose} />
<Button text={"Cancel"} intent={Intent.NONE} onClick={closeDialog} />
<Button
text={"Publish"}
intent={Intent.PRIMARY}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import {
GlobalNumberPanel,
GlobalMultiTextPanel,
GlobalSelectPanel,
} from "./components/BlockPropSettingPanels";
} from "~/components/settings/components/BlockPropSettingPanels";
import {
GLOBAL_KEYS,
EXPORT_KEYS,
} from "~/components/settings/utils/settingKeys";
import { type SettingsSnapshot } from "./utils/accessors";
import { ROAM_DOCS, withDocsLink } from "./utils/docs";
import { type SettingsSnapshot } from "~/components/settings/utils/accessors";
import { ROAM_DOCS, withDocsLink } from "~/components/settings/utils/docs";

const DiscourseGraphExport = ({
const ExportOptions = ({
globalSettings,
}: {
globalSettings: SettingsSnapshot["globalSettings"];
Expand Down Expand Up @@ -123,4 +123,4 @@ const DiscourseGraphExport = ({
);
};

export default DiscourseGraphExport;
export default ExportOptions;
27 changes: 14 additions & 13 deletions apps/roam/src/components/settings/DiscourseNodeSpecification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ const NodeSpecification = ({
</style>
<DiscourseNodeFlagPanel
nodeType={node.type}
title="enabled"
title="Enabled"
blockKey="enabled"
description=""
settingKeys={[
DISCOURSE_NODE_KEYS.specification,
Expand All @@ -170,18 +171,18 @@ const NodeSpecification = ({
parentSetEnabled?.(checked);
}}
/>
<div
className={`${enabled ? "" : "bg-gray-200 opacity-75"} overflow-auto`}
>
<QueryEditor
parentUid={parentUid}
key={Number(migrated)}
hideCustomSwitch
discourseNodeType={node.type}
settingKey="specification"
returnNode={node.text}
/>
</div>
{enabled && (
<div className="overflow-auto">
<QueryEditor
parentUid={parentUid}
key={Number(migrated)}
hideCustomSwitch
discourseNodeType={node.type}
settingKey="specification"
returnNode={node.text}
/>
</div>
)}
</div>
);
};
Expand Down
49 changes: 36 additions & 13 deletions apps/roam/src/components/settings/DiscourseNodeSuggestiveRules.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useMemo } from "react";
import { Button, Intent } from "@blueprintjs/core";
import { Button, Collapse, Intent } from "@blueprintjs/core";
import DualWriteBlocksPanel from "./components/EphemeralBlocksPanel";
import getSubTree from "roamjs-components/util/getSubTree";
import { DiscourseNode } from "~/utils/getDiscourseNodes";
Expand All @@ -17,6 +17,7 @@ import {
TEMPLATE_SETTING_KEYS,
} from "~/components/settings/utils/settingKeys";
import { RenderRoamBlock } from "~/utils/roamReactComponents";
import Description from "~/components/settings/SettingsDescription";
import { ROAM_DOCS, withDocsLink } from "./utils/docs";

const DiscourseNodeSuggestiveRules = ({
Expand All @@ -42,6 +43,7 @@ const DiscourseNodeSuggestiveRules = ({
}).uid || "",
[nodeUid],
);
const [isTemplateOpen, setIsTemplateOpen] = useState(false);

const [isUpdating, setIsUpdating] = useState(false);

Expand All @@ -68,18 +70,39 @@ const DiscourseNodeSuggestiveRules = ({
};

return (
<div className="flex flex-col gap-4 p-4">
<DualWriteBlocksPanel
nodeType={node.type}
title="Template"
description={withDocsLink(
`The template that auto fills ${node.text} page when generated.`,
ROAM_DOCS.creatingNodes,
)}
settingKeys={TEMPLATE_SETTING_KEYS}
uid={templateUid}
defaultValue={node.template}
/>
<div className="flex flex-col gap-4">
<div>
<div className="flex items-center">
<Button
minimal
small
icon={isTemplateOpen ? "chevron-down" : "chevron-right"}
text="Template"
onClick={() => setIsTemplateOpen((open) => !open)}
/>
<Description
description={withDocsLink(
`The template that auto fills ${node.text} page when generated.`,
ROAM_DOCS.creatingNodes,
)}
/>
</div>
{/* Collapse unmounts its children, so the buffer block only exists while open. */}
<Collapse isOpen={isTemplateOpen}>
<div className="pl-6 pt-2">
<DualWriteBlocksPanel
nodeType={node.type}
description={withDocsLink(
`The template that auto fills ${node.text} page when generated.`,
ROAM_DOCS.creatingNodes,
)}
settingKeys={TEMPLATE_SETTING_KEYS}
uid={templateUid}
defaultValue={node.template}
/>
</div>
</Collapse>
</div>

<DiscourseNodeTextPanel
nodeType={nodeUid}
Expand Down
Loading