Skip to content

Commit b473b2c

Browse files
committed
Merge remote-tracking branch 'origin/main' into eng-1549-convert-existing-page-to-node-roam
# Conflicts: # apps/roam/src/utils/createDiscourseNode.ts
2 parents 993a866 + 0da8051 commit b473b2c

87 files changed

Lines changed: 5649 additions & 3563 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/roam-main.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jobs:
2323
steps:
2424
- name: Checkout Code
2525
uses: actions/checkout@v6
26+
with:
27+
fetch-depth: 0
2628

2729
- name: Install pnpm
2830
uses: pnpm/action-setup@v4
@@ -53,3 +55,9 @@ jobs:
5355

5456
- name: Deploy
5557
run: npx turbo run deploy --filter=roam --ui stream -- --no-compile
58+
59+
- name: Sync Linear release
60+
uses: linear/linear-release-action@v0
61+
with:
62+
access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY }}
63+
include_paths: "apps/roam/**,packages/tailwind-config/**,packages/utils/**,packages/database/**,packages/ui/**"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Complete Roam Linear Release
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
version:
6+
description: "Roam release version to complete in Linear, for example 0.18.1."
7+
required: true
8+
type: string
9+
10+
jobs:
11+
complete-release:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout Code
15+
uses: actions/checkout@v6
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Complete Linear release
20+
uses: linear/linear-release-action@v0
21+
with:
22+
access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY }}
23+
command: complete
24+
version: ${{ inputs.version }}

.github/workflows/roam-release.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
steps:
2020
- name: Checkout Code
2121
uses: actions/checkout@v6
22+
with:
23+
fetch-depth: 0
2224

2325
- name: Install pnpm
2426
uses: pnpm/action-setup@v4
@@ -42,6 +44,14 @@ jobs:
4244
id: version
4345
run: echo "version=$(node -p "require('./apps/roam/package.json').version")" >> $GITHUB_OUTPUT
4446

47+
- name: Sync Linear release
48+
uses: linear/linear-release-action@v0
49+
with:
50+
access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY }}
51+
name: Roam ${{ steps.version.outputs.version }}
52+
version: ${{ steps.version.outputs.version }}
53+
include_paths: "apps/roam/**,packages/tailwind-config/**,packages/utils/**,packages/database/**,packages/ui/**"
54+
4555
- name: Inject & upload source maps to PostHog
4656
uses: PostHog/upload-source-maps@v0.5.7.0
4757
with:
@@ -53,3 +63,11 @@ jobs:
5363

5464
- name: Update Roam Depot Extension
5565
run: npx turbo run publish --filter=roam --ui stream -- --no-compile
66+
67+
- name: Mark Linear release sent to Roam review
68+
uses: linear/linear-release-action@v0
69+
with:
70+
access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY }}
71+
command: update
72+
version: ${{ steps.version.outputs.version }}
73+
stage: Sent to Roam for Review

.github/workflows/test-database.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
- name: Setup node
2222
uses: actions/setup-node@v4
2323
with:
24-
node-version: "20"
24+
node-version: "22"
2525
cache: "pnpm"
2626
- name: Install Dependencies
2727
run: pnpm install --frozen-lockfile

apps/obsidian/src/components/AdminPanelSettings.tsx

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { useState, useCallback } from "react";
22
import { usePlugin } from "./PluginContext";
3-
import { Notice } from "obsidian";
3+
import { Notice, setIcon } from "obsidian";
4+
import { updateUsername } from "~/utils/supabaseContext";
45
import { initializeSupabaseSync } from "~/utils/syncDgNodesToSupabase";
6+
import { nextRoot } from "@repo/utils/execContext";
7+
import { getLoggedInClient } from "~/utils/supabaseContext";
58

69
export const AdminPanelSettings = () => {
710
const plugin = usePlugin();
811
const [syncModeEnabled, setSyncModeEnabled] = useState<boolean>(
912
plugin.settings.syncModeEnabled ?? false,
1013
);
14+
const [username, setUsername] = useState<string>(
15+
plugin.settings.username || "",
16+
);
1117

1218
const handleSyncModeToggle = useCallback(
1319
async (newValue: boolean) => {
@@ -30,6 +36,38 @@ export const AdminPanelSettings = () => {
3036
[plugin],
3137
);
3238

39+
const handleSetUsername = async (newValue: string) => {
40+
setUsername(newValue);
41+
plugin.settings.username = newValue;
42+
await plugin.saveSettings();
43+
await updateUsername(plugin, newValue);
44+
};
45+
46+
const handleLoginHandoff = async () => {
47+
const client = await getLoggedInClient(plugin);
48+
if (!client) {
49+
new Notice("Failed to connect to the database", 3000);
50+
return;
51+
}
52+
const sessionData = await client.auth.getSession();
53+
if (!sessionData.data.session) {
54+
new Notice("Failed to connect to the database", 3000);
55+
return;
56+
}
57+
/* eslint-disable @typescript-eslint/naming-convention */
58+
const { access_token, refresh_token } = sessionData.data.session;
59+
const { data, error } = await client.rpc("create_secret_token", {
60+
v_payload: JSON.stringify({ access_token, refresh_token }),
61+
expiry_interval: "45s",
62+
});
63+
/* eslint-enable @typescript-eslint/naming-convention */
64+
if (error || typeof data !== "string") {
65+
new Notice("Failed to connect to the database", 3000);
66+
return;
67+
}
68+
if (data) window.open(`${nextRoot()}/auth/token?t=${data}&url=/`, "_blank");
69+
};
70+
3371
return (
3472
<div className="general-settings">
3573
<div className="setting-item">
@@ -48,6 +86,52 @@ export const AdminPanelSettings = () => {
4886
</div>
4987
</div>
5088
</div>
89+
<div
90+
className={
91+
"setting-item " + (plugin.settings.syncModeEnabled ? "" : "hidden")
92+
}
93+
>
94+
<div className="setting-item-info">
95+
<div className="setting-item-name">Username</div>
96+
<div className="setting-item-description">
97+
A username that will be associated with your vault if you share
98+
data.
99+
</div>
100+
</div>
101+
<div className="setting-item-control">
102+
<input
103+
type="text"
104+
value={username}
105+
onChange={(e) => setUsername(e.target.value)}
106+
onBlur={(e) => void handleSetUsername(e.target.value)}
107+
/>
108+
</div>
109+
</div>
110+
<div
111+
className={
112+
"setting-item " + (plugin.settings.syncModeEnabled ? "" : "hidden")
113+
}
114+
>
115+
<div className="setting-item-info">
116+
<div className="setting-item-name">Group management</div>
117+
<div className="setting-item-description">
118+
This will allow you to view and manage your sharing groups
119+
</div>
120+
</div>
121+
<div className="setting-item-control">
122+
<button
123+
onClick={() => {
124+
void handleLoginHandoff();
125+
}}
126+
>
127+
Manage groups
128+
<span
129+
className="icon"
130+
ref={(el) => (el && setIcon(el, "arrow-up-right")) || undefined}
131+
/>
132+
</button>
133+
</div>
134+
</div>
51135
</div>
52136
);
53137
};

apps/obsidian/src/components/DiscourseContextView.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import { getDiscourseNodeFormatExpression } from "~/utils/getDiscourseNodeFormat
1313
import { RelationshipSection } from "~/components/RelationshipSection";
1414
import { VIEW_TYPE_DISCOURSE_CONTEXT } from "~/types";
1515
import { PluginProvider, usePlugin } from "~/components/PluginContext";
16-
import { getNodeTypeById, getAndFormatImportSource } from "~/utils/typeUtils";
16+
import {
17+
getNodeTypeById,
18+
getAndFormatImportSource,
19+
getUserNameById,
20+
} from "~/utils/typeUtils";
1721
import { refreshImportedFile } from "~/utils/importNodes";
1822
import { publishNode } from "~/utils/publishNode";
1923
import { createBaseForNodeType } from "~/utils/baseForNodeType";
@@ -27,7 +31,7 @@ type InfoTooltipProps = {
2731
content: string;
2832
};
2933

30-
const InfoTooltip = ({ content }: InfoTooltipProps) => (
34+
export const InfoTooltip = ({ content }: InfoTooltipProps) => (
3135
<button
3236
ref={(el) => {
3337
if (el) setTooltip(el, content);
@@ -161,6 +165,13 @@ const DiscourseContext = ({ activeFile }: DiscourseContextProps) => {
161165
!isImported &&
162166
!!frontmatter.nodeTypeId;
163167

168+
const formattedVaultName = isImported
169+
? getAndFormatImportSource(
170+
frontmatter.importedFromRid as string,
171+
plugin.settings.spaceNames,
172+
)
173+
: "";
174+
164175
return (
165176
<>
166177
<div className="mb-6">
@@ -232,7 +243,14 @@ const DiscourseContext = ({ activeFile }: DiscourseContextProps) => {
232243
View only
233244
</span>
234245
<InfoTooltip
235-
content={`Imported from ${getAndFormatImportSource(frontmatter.importedFromRid as string, plugin.settings.spaceNames)}. Direct edits will be overwritten when refreshed.`}
246+
content={
247+
`Imported from ${formattedVaultName}. ` +
248+
(frontmatter.authorId &&
249+
typeof frontmatter.authorId === "number"
250+
? `By ${getUserNameById(plugin, frontmatter.authorId)}. `
251+
: "") +
252+
"Direct edits will be overwritten when refreshed."
253+
}
236254
/>
237255
</div>
238256
)}

apps/obsidian/src/components/ImportNodesModal.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { createRoot, Root } from "react-dom/client";
33
import { StrictMode, useState, useEffect, useCallback } from "react";
44
import type DiscourseGraphPlugin from "../index";
55
import type { ImportableNode, GroupWithNodes } from "~/types";
6+
import { getUserNameById } from "~/utils/typeUtils";
67
import {
8+
fetchUserNames,
79
getAvailableGroupIds,
810
getPublishedNodesForGroups,
911
getLocalNodeInstanceIds,
@@ -61,6 +63,8 @@ const ImportNodesContent = ({ plugin, onClose }: ImportNodesModalProps) => {
6163
return;
6264
}
6365

66+
await fetchUserNames(plugin, client);
67+
6468
const publishedNodes = await getPublishedNodesForGroups({
6569
client,
6670
groupIds,
@@ -82,7 +86,7 @@ const ImportNodesContent = ({ plugin, onClose }: ImportNodesModalProps) => {
8286
getSpaceUris(client, uniqueSpaceIds),
8387
]);
8488

85-
// Populate plugin settings with current space names so they stay up to date
89+
// Keep spaceNames in settings up to date for UI display (formatImportSource reads it)
8690
if (uniqueSpaceIds.length > 0) {
8791
if (!plugin.settings.spaceNames) plugin.settings.spaceNames = {};
8892

@@ -106,21 +110,26 @@ const ImportNodesContent = ({ plugin, onClose }: ImportNodesModalProps) => {
106110
groupName:
107111
spaceNames.get(node.space_id) ?? `Space ${node.space_id}`,
108112
nodes: [],
113+
authorIds: new Set(),
109114
});
110115
}
111116

112117
const group = grouped.get(groupId)!;
118+
const spaceName =
119+
spaceNames.get(node.space_id) ?? `Space ${node.space_id}`;
113120
group.nodes.push({
114121
nodeInstanceId: node.source_local_id,
115122
title: node.text,
116123
spaceId: node.space_id,
117-
spaceName: spaceNames.get(node.space_id) ?? `Space ${node.space_id}`,
124+
spaceName,
118125
groupId,
119126
selected: false,
120127
createdAt: node.createdAt,
121128
modifiedAt: node.modifiedAt,
122129
filePath: node.filePath,
130+
authorId: node.authorId,
123131
});
132+
if (node.authorId) group.authorIds.add(node.authorId);
124133
}
125134

126135
setGroupsWithNodes(Array.from(grouped.values()));
@@ -258,6 +267,7 @@ const ImportNodesContent = ({ plugin, onClose }: ImportNodesModalProps) => {
258267
number,
259268
{
260269
spaceName: string;
270+
authorIds: Set<number>;
261271
nodes: Array<{
262272
node: ImportableNode;
263273
groupId: string;
@@ -271,6 +281,7 @@ const ImportNodesContent = ({ plugin, onClose }: ImportNodesModalProps) => {
271281
if (!nodesBySpace.has(node.spaceId)) {
272282
nodesBySpace.set(node.spaceId, {
273283
spaceName: node.spaceName,
284+
authorIds: group.authorIds,
274285
nodes: [],
275286
});
276287
}
@@ -322,14 +333,19 @@ const ImportNodesContent = ({ plugin, onClose }: ImportNodesModalProps) => {
322333

323334
<div className="max-h-96 overflow-y-auto rounded border">
324335
{Array.from(nodesBySpace.entries()).map(
325-
([spaceId, { spaceName, nodes }]) => {
336+
([spaceId, { spaceName, nodes, authorIds }]) => {
326337
return (
327338
<div key={spaceId} className="border-b">
328339
<div className="bg-muted/10 flex items-center px-3 py-2">
329340
<span className="mr-2">📂</span>
330341
<span className="text-accent-foreground line-clamp-1 font-medium italic">
331342
{spaceName}
332343
</span>
344+
{authorIds.size === 1 && (
345+
<span>
346+
&nbsp;({getUserNameById(plugin, [...authorIds][0]!)})
347+
</span>
348+
)}
333349
<span className="text-muted ml-2 text-sm">
334350
({nodes.length} node{nodes.length !== 1 ? "s" : ""})
335351
</span>
@@ -349,6 +365,11 @@ const ImportNodesContent = ({ plugin, onClose }: ImportNodesModalProps) => {
349365
<div className="min-w-0 flex-1">
350366
<div className="line-clamp-3 font-medium">
351367
{node.title}
368+
{node.authorId && authorIds.size > 1 && (
369+
<span className="font-light">
370+
&nbsp;({getUserNameById(plugin, node.authorId)})
371+
</span>
372+
)}
352373
</div>
353374
</div>
354375
</div>

0 commit comments

Comments
 (0)