@dxos/plugin-thread 0.6.10 → 0.6.11-staging.66fb9aa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-TL3JITN7.mjs → chunk-GKPZHUT3.mjs} +2 -1
- package/dist/lib/browser/chunk-GKPZHUT3.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +153 -115
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/node/{chunk-WPPLQNMJ.cjs → chunk-D5GOVXHS.cjs} +5 -4
- package/dist/lib/node/chunk-D5GOVXHS.cjs.map +7 -0
- package/dist/lib/node/index.cjs +164 -126
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +2 -2
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
- package/dist/types/src/components/Chat.stories.d.ts +0 -1
- package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
- package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +7 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +28 -28
- package/src/ThreadPlugin.tsx +147 -104
- package/src/components/Chat.stories.tsx +3 -3
- package/src/components/Comments.stories.tsx +8 -13
- package/src/types.ts +10 -0
- package/dist/lib/browser/chunk-TL3JITN7.mjs.map +0 -7
- package/dist/lib/node/chunk-WPPLQNMJ.cjs.map +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["types.cjs"],
|
|
4
|
-
"sourcesContent": ["import {\n ThreadAction\n} from \"./chunk-
|
|
4
|
+
"sourcesContent": ["import {\n ThreadAction\n} from \"./chunk-D5GOVXHS.cjs\";\nimport \"./chunk-BWBONZGU.cjs\";\nexport {\n ThreadAction\n};\n//# sourceMappingURL=types.cjs.map\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAEO;AACP,4BAAO;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadPlugin.d.ts","sourceRoot":"","sources":["../../../src/ThreadPlugin.tsx"],"names":[],"mappings":"AAQA,OAAO,EAML,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"ThreadPlugin.d.ts","sourceRoot":"","sources":["../../../src/ThreadPlugin.tsx"],"names":[],"mappings":"AAQA,OAAO,EAML,KAAK,gBAAgB,EAWtB,MAAM,qBAAqB,CAAC;AAqC7B,OAAO,EAAqC,KAAK,oBAAoB,EAA4B,MAAM,SAAS,CAAC;AAkBjH,eAAO,MAAM,YAAY,QAAO,gBAAgB,CAAC,oBAAoB,CAssBpE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/Chat.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAA8B,MAAM,OAAO,CAAC
|
|
1
|
+
{"version":3,"file":"Chat.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/Chat.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAA8B,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DnD,wBAOE;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Comments.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/Comments.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAA8B,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Comments.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/Comments.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAA8B,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CnD,wBAME;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
|
|
@@ -6,9 +6,16 @@ export declare enum ThreadAction {
|
|
|
6
6
|
CREATE = "dxos.org/plugin/thread/action/create",
|
|
7
7
|
SELECT = "dxos.org/plugin/thread/action/select",
|
|
8
8
|
DELETE = "dxos.org/plugin/thread/action/delete",
|
|
9
|
+
ON_MESSAGE_ADD = "dxos.org/plugin/thread/action/on-message-add",
|
|
9
10
|
DELETE_MESSAGE = "dxos.org/plugin/thread/action/delete-message",
|
|
10
11
|
TOGGLE_RESOLVED = "dxos.org/plugin/thread/action/toggle-resolved"
|
|
11
12
|
}
|
|
13
|
+
export type ThreadProvides<T> = {
|
|
14
|
+
thread: {
|
|
15
|
+
predicate: (obj: any) => obj is T;
|
|
16
|
+
createSort: (obj: T) => (anchorA: string, anchorB: string) => number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
12
19
|
export type ThreadPluginProvides = SurfaceProvides & IntentResolverProvides & GraphBuilderProvides & MetadataRecordsProvides & SettingsProvides & TranslationsProvides & SchemaProvides & MarkdownExtensionProvides;
|
|
13
20
|
export type ThreadSettingsProps = {
|
|
14
21
|
standalone?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAK3D,oBAAY,YAAY;IACtB,MAAM,yCAA4B;IAClC,MAAM,yCAA4B;IAClC,MAAM,yCAA4B;IAClC,cAAc,iDAAoC;IAClD,eAAe,kDAAqC;CACrD;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAK3D,oBAAY,YAAY;IACtB,MAAM,yCAA4B;IAClC,MAAM,yCAA4B;IAClC,MAAM,yCAA4B;IAClC,cAAc,iDAAoC;IAClD,cAAc,iDAAoC;IAClD,eAAe,kDAAqC;CACrD;AAID,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,MAAM,EAAE;QACN,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC;QAClC,UAAU,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;KACtE,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,eAAe,GAChD,sBAAsB,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,gBAAgB,GAChB,oBAAoB,GACpB,cAAc,GACd,yBAAyB,CAAC;AAE5B,MAAM,MAAM,mBAAmB,GAAG;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,CAAC;CAClB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-thread",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11-staging.66fb9aa",
|
|
4
4
|
"description": "DXOS Threads Surface plugin",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -51,28 +51,28 @@
|
|
|
51
51
|
"@preact/signals-core": "^1.6.0",
|
|
52
52
|
"date-fns": "^3.3.1",
|
|
53
53
|
"lodash.get": "^4.4.2",
|
|
54
|
-
"@dxos/app-framework": "0.6.
|
|
55
|
-
"@dxos/async": "0.6.
|
|
56
|
-
"@dxos/display-name": "0.6.
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/
|
|
59
|
-
"@dxos/local-storage": "0.6.
|
|
60
|
-
"@dxos/log": "0.6.
|
|
61
|
-
"@dxos/plugin-attention": "0.6.
|
|
62
|
-
"@dxos/plugin-client": "0.6.
|
|
63
|
-
"@dxos/plugin-
|
|
64
|
-
"@dxos/plugin-
|
|
65
|
-
"@dxos/plugin-settings": "0.6.
|
|
66
|
-
"@dxos/plugin-
|
|
67
|
-
"@dxos/plugin-space": "0.6.
|
|
68
|
-
"@dxos/react-client": "0.6.
|
|
69
|
-
"@dxos/react-ui-deck": "0.6.
|
|
70
|
-
"@dxos/react-ui-attention": "0.6.
|
|
71
|
-
"@dxos/react-ui-editor": "0.6.
|
|
72
|
-
"@dxos/react-ui-
|
|
73
|
-
"@dxos/react-ui-
|
|
74
|
-
"@dxos/react-ui-thread": "0.6.
|
|
75
|
-
"@dxos/util": "0.6.
|
|
54
|
+
"@dxos/app-framework": "0.6.11-staging.66fb9aa",
|
|
55
|
+
"@dxos/async": "0.6.11-staging.66fb9aa",
|
|
56
|
+
"@dxos/display-name": "0.6.11-staging.66fb9aa",
|
|
57
|
+
"@dxos/invariant": "0.6.11-staging.66fb9aa",
|
|
58
|
+
"@dxos/echo-schema": "0.6.11-staging.66fb9aa",
|
|
59
|
+
"@dxos/local-storage": "0.6.11-staging.66fb9aa",
|
|
60
|
+
"@dxos/log": "0.6.11-staging.66fb9aa",
|
|
61
|
+
"@dxos/plugin-attention": "0.6.11-staging.66fb9aa",
|
|
62
|
+
"@dxos/plugin-client": "0.6.11-staging.66fb9aa",
|
|
63
|
+
"@dxos/plugin-markdown": "0.6.11-staging.66fb9aa",
|
|
64
|
+
"@dxos/plugin-observability": "0.6.11-staging.66fb9aa",
|
|
65
|
+
"@dxos/plugin-settings": "0.6.11-staging.66fb9aa",
|
|
66
|
+
"@dxos/plugin-graph": "0.6.11-staging.66fb9aa",
|
|
67
|
+
"@dxos/plugin-space": "0.6.11-staging.66fb9aa",
|
|
68
|
+
"@dxos/react-client": "0.6.11-staging.66fb9aa",
|
|
69
|
+
"@dxos/react-ui-deck": "0.6.11-staging.66fb9aa",
|
|
70
|
+
"@dxos/react-ui-attention": "0.6.11-staging.66fb9aa",
|
|
71
|
+
"@dxos/react-ui-editor": "0.6.11-staging.66fb9aa",
|
|
72
|
+
"@dxos/react-ui-theme": "0.6.11-staging.66fb9aa",
|
|
73
|
+
"@dxos/react-ui-mosaic": "0.6.11-staging.66fb9aa",
|
|
74
|
+
"@dxos/react-ui-thread": "0.6.11-staging.66fb9aa",
|
|
75
|
+
"@dxos/util": "0.6.11-staging.66fb9aa"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@phosphor-icons/react": "^2.1.5",
|
|
@@ -82,16 +82,16 @@
|
|
|
82
82
|
"react": "~18.2.0",
|
|
83
83
|
"react-dom": "~18.2.0",
|
|
84
84
|
"vite": "^5.3.4",
|
|
85
|
-
"@dxos/random": "0.6.
|
|
86
|
-
"@dxos/
|
|
87
|
-
"@dxos/
|
|
85
|
+
"@dxos/random": "0.6.11-staging.66fb9aa",
|
|
86
|
+
"@dxos/storybook-utils": "0.6.11-staging.66fb9aa",
|
|
87
|
+
"@dxos/react-ui": "0.6.11-staging.66fb9aa"
|
|
88
88
|
},
|
|
89
89
|
"optionalDependencies": {
|
|
90
90
|
"@phosphor-icons/react": "^2.1.5",
|
|
91
91
|
"react": "^18.0.0",
|
|
92
92
|
"react-dom": "^18.0.0",
|
|
93
|
-
"@dxos/react-ui": "0.6.
|
|
94
|
-
"@dxos/react-ui-theme": "0.6.
|
|
93
|
+
"@dxos/react-ui": "0.6.11-staging.66fb9aa",
|
|
94
|
+
"@dxos/react-ui-theme": "0.6.11-staging.66fb9aa"
|
|
95
95
|
},
|
|
96
96
|
"publishConfig": {
|
|
97
97
|
"access": "public"
|
package/src/ThreadPlugin.tsx
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
isLayoutParts,
|
|
23
23
|
parseMetadataResolverPlugin,
|
|
24
24
|
type IntentDispatcher,
|
|
25
|
+
useResolvePlugins,
|
|
25
26
|
} from '@dxos/app-framework';
|
|
26
27
|
import { type UnsubscribeCallback } from '@dxos/async';
|
|
27
28
|
import { type EchoReactiveObject, getTypename } from '@dxos/echo-schema';
|
|
@@ -40,7 +41,6 @@ import {
|
|
|
40
41
|
Filter,
|
|
41
42
|
createDocAccessor,
|
|
42
43
|
fullyQualifiedId,
|
|
43
|
-
getRangeFromCursor,
|
|
44
44
|
loadObjectReferences,
|
|
45
45
|
} from '@dxos/react-client/echo';
|
|
46
46
|
import { ScrollArea } from '@dxos/react-ui';
|
|
@@ -60,9 +60,10 @@ import {
|
|
|
60
60
|
} from './components';
|
|
61
61
|
import meta, { THREAD_ITEM, THREAD_PLUGIN } from './meta';
|
|
62
62
|
import translations from './translations';
|
|
63
|
-
import { ThreadAction, type ThreadPluginProvides, type ThreadSettingsProps } from './types';
|
|
63
|
+
import { ThreadAction, type ThreadProvides, type ThreadPluginProvides, type ThreadSettingsProps } from './types';
|
|
64
64
|
|
|
65
65
|
type ThreadState = {
|
|
66
|
+
/** An in-memory staging area for threads that are being drafted. */
|
|
66
67
|
staging: Record<string, ThreadType[]>;
|
|
67
68
|
current?: string | undefined;
|
|
68
69
|
focus?: boolean;
|
|
@@ -72,6 +73,11 @@ type SubjectId = string;
|
|
|
72
73
|
const initialViewState = { showResolvedThreads: false };
|
|
73
74
|
type ViewStore = Record<SubjectId, typeof initialViewState>;
|
|
74
75
|
|
|
76
|
+
// TODO(Zan): More robust runtime check.
|
|
77
|
+
const providesThreadsConfig = (plugin: any): Plugin<ThreadProvides<any>> | undefined =>
|
|
78
|
+
'thread' in plugin.provides ? (plugin as Plugin<ThreadProvides<any>>) : undefined;
|
|
79
|
+
|
|
80
|
+
// TODO(Zan): Every instance of `cursor` should be replaced with `anchor`.
|
|
75
81
|
export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
76
82
|
const settings = new LocalStorageStore<ThreadSettingsProps>(THREAD_PLUGIN);
|
|
77
83
|
const state = create<ThreadState>({ staging: {} });
|
|
@@ -197,7 +203,6 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
197
203
|
const [spaceId, objectId] = docId?.split(':') ?? [];
|
|
198
204
|
const space = client.spaces.get().find((space) => space.id === spaceId);
|
|
199
205
|
const doc = space?.db.getObjectById(objectId);
|
|
200
|
-
|
|
201
206
|
if (!doc || !docId) {
|
|
202
207
|
return;
|
|
203
208
|
}
|
|
@@ -223,7 +228,6 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
223
228
|
},
|
|
224
229
|
actions: ({ node }) => {
|
|
225
230
|
const dataId = node.id.split('~').at(0);
|
|
226
|
-
|
|
227
231
|
if (!node.id.endsWith('~comments') || !dataId) {
|
|
228
232
|
return;
|
|
229
233
|
}
|
|
@@ -310,15 +314,15 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
310
314
|
case 'article':
|
|
311
315
|
case 'complementary': {
|
|
312
316
|
const location = navigationPlugin?.provides.location;
|
|
317
|
+
const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
|
|
318
|
+
const threadProvides = threadsIntegrators.map((p) => p.provides.thread);
|
|
313
319
|
|
|
314
320
|
if (data.object instanceof ChannelType && data.object.threads[0]) {
|
|
315
321
|
const channel = data.object;
|
|
316
322
|
// TODO(zan): Maybe we should have utility for positional main object ids.
|
|
317
323
|
if (isLayoutParts(location?.active) && location.active.main) {
|
|
318
324
|
const layoutEntries = location.active.main;
|
|
319
|
-
|
|
320
325
|
const currentPosition = layoutEntries.findIndex((entry) => channel.id === entry.id);
|
|
321
|
-
|
|
322
326
|
if (currentPosition > 0) {
|
|
323
327
|
const objectToTheLeft = layoutEntries[currentPosition - 1];
|
|
324
328
|
const context = getSpace(data.object)?.db.getObjectById(objectToTheLeft.id);
|
|
@@ -329,45 +333,41 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
329
333
|
return <ThreadArticle thread={data.object.threads[0]} />;
|
|
330
334
|
}
|
|
331
335
|
|
|
332
|
-
if (data.subject instanceof
|
|
333
|
-
|
|
334
|
-
|
|
336
|
+
if (data.subject instanceof ThreadType) {
|
|
337
|
+
return (
|
|
338
|
+
<>
|
|
339
|
+
<ChatHeading attendableId={data.subject.id} />
|
|
340
|
+
<ChatContainer thread={data.subject} />
|
|
341
|
+
</>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
335
344
|
|
|
336
|
-
|
|
337
|
-
|
|
345
|
+
if (
|
|
346
|
+
data.subject &&
|
|
347
|
+
typeof data.subject === 'object' &&
|
|
348
|
+
'threads' in data.subject &&
|
|
349
|
+
Array.isArray(data.subject.threads)
|
|
350
|
+
) {
|
|
351
|
+
const threads = data.subject.threads
|
|
352
|
+
.concat(state.staging[fullyQualifiedId(data.subject)])
|
|
353
|
+
.filter(nonNullable);
|
|
354
|
+
|
|
355
|
+
const createSort = threadProvides.find((p) => p.predicate(data.subject))?.createSort;
|
|
356
|
+
if (createSort) {
|
|
357
|
+
const sort = createSort(data.subject);
|
|
358
|
+
threads.sort((a, b) => sort(a.anchor, b.anchor));
|
|
338
359
|
}
|
|
339
360
|
|
|
340
|
-
const getStartPosition = (cursor: string | undefined) => {
|
|
341
|
-
const range = cursor ? getRangeFromCursor(accessor, cursor) : undefined;
|
|
342
|
-
return range?.start ?? Number.MAX_SAFE_INTEGER;
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
const threads = doc.threads
|
|
346
|
-
.concat(state.staging[data.subject.id])
|
|
347
|
-
.filter(nonNullable)
|
|
348
|
-
.sort((a, b) => getStartPosition(a.anchor) - getStartPosition(b.anchor));
|
|
349
|
-
|
|
350
361
|
const detached = data.subject.threads
|
|
351
362
|
.filter(nonNullable)
|
|
352
363
|
.filter(({ anchor }) => !anchor)
|
|
353
364
|
.map((thread) => thread.id);
|
|
354
365
|
|
|
355
|
-
const qualifiedSubjectId = fullyQualifiedId(
|
|
366
|
+
const qualifiedSubjectId = fullyQualifiedId(data.subject);
|
|
356
367
|
const attention = attentionPlugin?.provides.attention?.attended ?? new Set([qualifiedSubjectId]);
|
|
357
368
|
const attendableAttrs = createAttendableAttributes(qualifiedSubjectId);
|
|
358
|
-
const space = getSpace(doc);
|
|
359
369
|
const { showResolvedThreads } = getViewState(qualifiedSubjectId);
|
|
360
370
|
|
|
361
|
-
const dispatchAnalytic = (name: string, meta: any) => {
|
|
362
|
-
void dispatch?.({
|
|
363
|
-
action: ObservabilityAction.SEND_EVENT,
|
|
364
|
-
data: {
|
|
365
|
-
name,
|
|
366
|
-
properties: { ...meta, space: space?.id },
|
|
367
|
-
},
|
|
368
|
-
});
|
|
369
|
-
};
|
|
370
|
-
|
|
371
371
|
return (
|
|
372
372
|
<div role='none' className='contents group/attention' {...attendableAttrs}>
|
|
373
373
|
{role === 'complementary' && <CommentsHeading attendableId={qualifiedSubjectId} />}
|
|
@@ -396,14 +396,14 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
396
396
|
dispatch?.({
|
|
397
397
|
plugin: THREAD_PLUGIN,
|
|
398
398
|
action: ThreadAction.DELETE,
|
|
399
|
-
data: {
|
|
399
|
+
data: { subject: data.subject, thread },
|
|
400
400
|
})
|
|
401
401
|
}
|
|
402
402
|
onMessageDelete={(thread, messageId) =>
|
|
403
403
|
dispatch?.({
|
|
404
404
|
plugin: THREAD_PLUGIN,
|
|
405
405
|
action: ThreadAction.DELETE_MESSAGE,
|
|
406
|
-
data: {
|
|
406
|
+
data: { subject: data.subject, thread, messageId },
|
|
407
407
|
})
|
|
408
408
|
}
|
|
409
409
|
onThreadToggleResolved={(thread) =>
|
|
@@ -414,19 +414,9 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
414
414
|
})
|
|
415
415
|
}
|
|
416
416
|
onComment={(thread) => {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// Move thread from staging to document.
|
|
421
|
-
thread.status = 'active';
|
|
422
|
-
doc.threads ? doc.threads.push(thread) : (doc.threads = [thread]);
|
|
423
|
-
state.staging[doc.id] = state.staging[doc.id]?.filter((t) => t.id !== thread.id);
|
|
424
|
-
dispatchAnalytic('threads.thread-created', { threadId: thread.id });
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
dispatchAnalytic('threads.message-added', {
|
|
428
|
-
threadId: thread.id,
|
|
429
|
-
threadLength: thread.messages.length,
|
|
417
|
+
void intentPlugin?.provides.intent.dispatch({
|
|
418
|
+
action: ThreadAction.ON_MESSAGE_ADD,
|
|
419
|
+
data: { thread, subject: data.subject },
|
|
430
420
|
});
|
|
431
421
|
}}
|
|
432
422
|
/>
|
|
@@ -438,13 +428,6 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
438
428
|
</ScrollArea.Root>
|
|
439
429
|
</div>
|
|
440
430
|
);
|
|
441
|
-
} else if (data.subject instanceof ThreadType) {
|
|
442
|
-
return (
|
|
443
|
-
<>
|
|
444
|
-
<ChatHeading attendableId={data.subject.id} />
|
|
445
|
-
<ChatContainer thread={data.subject} />
|
|
446
|
-
</>
|
|
447
|
-
);
|
|
448
431
|
}
|
|
449
432
|
}
|
|
450
433
|
}
|
|
@@ -456,7 +439,49 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
456
439
|
resolver: async (intent) => {
|
|
457
440
|
switch (intent.action) {
|
|
458
441
|
case ThreadAction.CREATE: {
|
|
459
|
-
|
|
442
|
+
if (intent.data && intent.data.cursor !== undefined) {
|
|
443
|
+
const { cursor, name, subject } = intent.data;
|
|
444
|
+
|
|
445
|
+
const subjectId = fullyQualifiedId(subject);
|
|
446
|
+
const thread = create(ThreadType, { name, anchor: cursor, messages: [], status: 'staged' });
|
|
447
|
+
const stagingArea = state.staging[subjectId];
|
|
448
|
+
if (stagingArea) {
|
|
449
|
+
stagingArea.push(thread);
|
|
450
|
+
} else {
|
|
451
|
+
state.staging[subjectId] = [thread];
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
data: thread,
|
|
456
|
+
intents: [
|
|
457
|
+
[
|
|
458
|
+
{
|
|
459
|
+
action: NavigationAction.OPEN,
|
|
460
|
+
data: {
|
|
461
|
+
activeParts: {
|
|
462
|
+
complementary: `${subjectId}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`,
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
action: ThreadAction.SELECT,
|
|
468
|
+
data: { current: thread.id, focus: true },
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
action: LayoutAction.SET_LAYOUT,
|
|
472
|
+
data: {
|
|
473
|
+
element: 'complementary',
|
|
474
|
+
subject: subjectId,
|
|
475
|
+
state: true,
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
],
|
|
480
|
+
};
|
|
481
|
+
} else {
|
|
482
|
+
// NOTE: This is the standalone thread creation case.
|
|
483
|
+
return { data: create(ChannelType, { threads: [create(ThreadType, { messages: [] })] }) };
|
|
484
|
+
}
|
|
460
485
|
}
|
|
461
486
|
|
|
462
487
|
case ThreadAction.SELECT: {
|
|
@@ -493,32 +518,33 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
493
518
|
}
|
|
494
519
|
|
|
495
520
|
case ThreadAction.DELETE: {
|
|
496
|
-
const {
|
|
497
|
-
if (!(
|
|
521
|
+
const { subject, thread } = intent.data ?? {};
|
|
522
|
+
if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType)) {
|
|
498
523
|
return;
|
|
499
524
|
}
|
|
500
525
|
|
|
501
|
-
const
|
|
526
|
+
const subjectId = fullyQualifiedId(subject);
|
|
527
|
+
const stagingArea = state.staging[subjectId];
|
|
502
528
|
if (stagingArea) {
|
|
503
529
|
// Check if we're deleting a thread that's in the staging area.
|
|
504
530
|
// If so, remove it from the staging area without ceremony.
|
|
505
|
-
const index =
|
|
531
|
+
const index = stagingArea.findIndex((t) => t.id === thread.id);
|
|
506
532
|
if (index !== -1) {
|
|
507
|
-
|
|
533
|
+
stagingArea.splice(index, 1);
|
|
508
534
|
return;
|
|
509
535
|
}
|
|
510
536
|
}
|
|
511
537
|
|
|
512
538
|
const space = getSpace(thread);
|
|
513
|
-
if (!space || !
|
|
539
|
+
if (!space || !subject.threads) {
|
|
514
540
|
return;
|
|
515
541
|
}
|
|
516
542
|
|
|
517
543
|
if (!intent.undo) {
|
|
518
|
-
const index =
|
|
519
|
-
const cursor =
|
|
544
|
+
const index = subject.threads.findIndex((t) => t?.id === thread.id);
|
|
545
|
+
const cursor = subject.threads[index]?.anchor;
|
|
520
546
|
if (index !== -1) {
|
|
521
|
-
|
|
547
|
+
subject.threads?.splice(index, 1);
|
|
522
548
|
}
|
|
523
549
|
|
|
524
550
|
space.db.remove(thread);
|
|
@@ -540,7 +566,7 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
540
566
|
} else if (intent.undo) {
|
|
541
567
|
// TODO(wittjosiah): SDK should do this automatically.
|
|
542
568
|
const savedThread = space.db.add(thread);
|
|
543
|
-
|
|
569
|
+
subject.threads.push(savedThread);
|
|
544
570
|
|
|
545
571
|
return {
|
|
546
572
|
data: true,
|
|
@@ -556,11 +582,52 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
556
582
|
}
|
|
557
583
|
break;
|
|
558
584
|
}
|
|
585
|
+
|
|
586
|
+
case ThreadAction.ON_MESSAGE_ADD: {
|
|
587
|
+
const { thread, subject } = intent.data ?? {};
|
|
588
|
+
if (
|
|
589
|
+
!(thread instanceof ThreadType) ||
|
|
590
|
+
!(subject && typeof subject === 'object' && 'threads' in subject)
|
|
591
|
+
) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const subjectId = fullyQualifiedId(subject);
|
|
596
|
+
const space = getSpace(subject);
|
|
597
|
+
const intents = [];
|
|
598
|
+
const analyticsProperties = { threadId: thread.id, spaceId: space?.id };
|
|
599
|
+
|
|
600
|
+
if (state.staging[subjectId]?.find((t) => t === thread)) {
|
|
601
|
+
// Move thread from staging to document.
|
|
602
|
+
thread.status = 'active';
|
|
603
|
+
subject.threads ? subject.threads.push(thread) : (subject.threads = [thread]);
|
|
604
|
+
state.staging[subjectId] = state.staging[subjectId]?.filter((t) => t.id !== thread.id);
|
|
605
|
+
|
|
606
|
+
intents.push({
|
|
607
|
+
action: ObservabilityAction.SEND_EVENT,
|
|
608
|
+
data: { name: 'threads.thread-created', properties: analyticsProperties },
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
intents.push({
|
|
613
|
+
action: ObservabilityAction.SEND_EVENT,
|
|
614
|
+
data: {
|
|
615
|
+
name: 'threads.message-added',
|
|
616
|
+
properties: { ...analyticsProperties, threadLength: thread.messages.length },
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
return {
|
|
621
|
+
data: thread,
|
|
622
|
+
intents: [intents],
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
559
626
|
case ThreadAction.DELETE_MESSAGE: {
|
|
560
|
-
const {
|
|
561
|
-
const space = getSpace(
|
|
627
|
+
const { subject, thread, messageId } = intent.data ?? {};
|
|
628
|
+
const space = getSpace(subject);
|
|
562
629
|
|
|
563
|
-
if (!(
|
|
630
|
+
if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType) || !space) {
|
|
564
631
|
return;
|
|
565
632
|
}
|
|
566
633
|
|
|
@@ -574,7 +641,7 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
574
641
|
if (messageIndex === 0 && thread.messages.length === 1) {
|
|
575
642
|
// If the message is the only message in the thread, delete the thread.
|
|
576
643
|
return {
|
|
577
|
-
intents: [[{ action: ThreadAction.DELETE, data: {
|
|
644
|
+
intents: [[{ action: ThreadAction.DELETE, data: { subject, thread } }]],
|
|
578
645
|
};
|
|
579
646
|
} else {
|
|
580
647
|
thread.messages.splice(messageIndex, 1);
|
|
@@ -638,7 +705,7 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
638
705
|
|
|
639
706
|
// TODO(Zan): When we have the deepsignal specific equivalent of this we should use that instead.
|
|
640
707
|
const threads = computed(() =>
|
|
641
|
-
[...doc.threads.filter(nonNullable), ...(state.staging[doc
|
|
708
|
+
[...doc.threads.filter(nonNullable), ...(state.staging[fullyQualifiedId(doc)] ?? [])].filter(
|
|
642
709
|
(thread) => !(thread?.status === 'resolved'),
|
|
643
710
|
),
|
|
644
711
|
);
|
|
@@ -673,43 +740,19 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
673
740
|
onCreate: ({ cursor }) => {
|
|
674
741
|
const [start, end] = cursor.split(':');
|
|
675
742
|
const name = doc.content && getTextInRange(createDocAccessor(doc.content, ['content']), start, end);
|
|
676
|
-
const thread = create(ThreadType, { name, anchor: cursor, messages: [], status: 'staged' });
|
|
677
743
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
void intentPlugin?.provides.intent.dispatch([
|
|
686
|
-
{
|
|
687
|
-
action: NavigationAction.OPEN,
|
|
688
|
-
data: {
|
|
689
|
-
activeParts: {
|
|
690
|
-
complementary: `${fullyQualifiedId(doc)}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`,
|
|
691
|
-
},
|
|
692
|
-
},
|
|
693
|
-
},
|
|
694
|
-
{
|
|
695
|
-
action: ThreadAction.SELECT,
|
|
696
|
-
data: { current: thread.id, focus: true },
|
|
697
|
-
},
|
|
698
|
-
{
|
|
699
|
-
action: LayoutAction.SET_LAYOUT,
|
|
700
|
-
data: {
|
|
701
|
-
element: 'complementary',
|
|
702
|
-
subject: doc,
|
|
703
|
-
state: true,
|
|
704
|
-
},
|
|
744
|
+
void intentPlugin?.provides.intent.dispatch({
|
|
745
|
+
action: ThreadAction.CREATE,
|
|
746
|
+
data: {
|
|
747
|
+
cursor,
|
|
748
|
+
name,
|
|
749
|
+
subject: doc,
|
|
705
750
|
},
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
return thread.id;
|
|
751
|
+
});
|
|
709
752
|
},
|
|
710
753
|
onDelete: ({ id }) => {
|
|
711
754
|
// If the thread is in the staging area, remove it.
|
|
712
|
-
const stagingArea = state.staging[doc
|
|
755
|
+
const stagingArea = state.staging[fullyQualifiedId(doc)];
|
|
713
756
|
if (stagingArea) {
|
|
714
757
|
const index = stagingArea.findIndex((thread) => thread.id === id);
|
|
715
758
|
if (index !== -1) {
|
|
@@ -724,7 +767,7 @@ export const ThreadPlugin = (): PluginDefinition<ThreadPluginProvides> => {
|
|
|
724
767
|
},
|
|
725
768
|
onUpdate: ({ id, cursor }) => {
|
|
726
769
|
const thread =
|
|
727
|
-
state.staging[doc
|
|
770
|
+
state.staging[fullyQualifiedId(doc)]?.find((thread) => thread.id === id) ??
|
|
728
771
|
doc.threads.find((thread) => thread?.id === id);
|
|
729
772
|
|
|
730
773
|
if (thread instanceof ThreadType && thread.anchor) {
|
|
@@ -15,7 +15,7 @@ import { useIdentity } from '@dxos/react-client/halo';
|
|
|
15
15
|
import { ClientRepeater } from '@dxos/react-client/testing';
|
|
16
16
|
import { Mosaic } from '@dxos/react-ui-mosaic';
|
|
17
17
|
import { Thread } from '@dxos/react-ui-thread';
|
|
18
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
18
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
19
19
|
|
|
20
20
|
import { ChatContainer } from './ChatContainer';
|
|
21
21
|
import { createChatThread } from './testing';
|
|
@@ -69,8 +69,8 @@ export default {
|
|
|
69
69
|
component: Thread,
|
|
70
70
|
// TODO(wittjosiah): Register schemas.
|
|
71
71
|
render: () => <ClientRepeater component={Story} createIdentity createSpace types={[ThreadType]} />,
|
|
72
|
-
decorators: [withTheme],
|
|
73
|
-
parameters: { translations
|
|
72
|
+
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
73
|
+
parameters: { translations },
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
export const Default = {};
|
|
@@ -8,12 +8,10 @@ import React, { useEffect, useState } from 'react';
|
|
|
8
8
|
|
|
9
9
|
import { MessageType, ThreadType } from '@dxos/plugin-space/types';
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
|
-
import { type PublicKey } from '@dxos/react-client';
|
|
12
11
|
import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
|
|
13
12
|
import { useIdentity } from '@dxos/react-client/halo';
|
|
14
|
-
import { ClientRepeater } from '@dxos/react-client/testing';
|
|
15
|
-
import {
|
|
16
|
-
import { withFullscreen, withTheme } from '@dxos/storybook-utils';
|
|
13
|
+
import { type ClientRepeatedComponentProps, ClientRepeater } from '@dxos/react-client/testing';
|
|
14
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
17
15
|
|
|
18
16
|
import { CommentsContainer } from './CommentsContainer';
|
|
19
17
|
import { createCommentThread } from './testing';
|
|
@@ -21,7 +19,7 @@ import translations from '../translations';
|
|
|
21
19
|
|
|
22
20
|
faker.seed(1);
|
|
23
21
|
|
|
24
|
-
const Story = ({ spaceKey }:
|
|
22
|
+
const Story = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
25
23
|
const identity = useIdentity();
|
|
26
24
|
const space = useSpace(spaceKey);
|
|
27
25
|
const threads = useQuery(space, Filter.schema(ThreadType));
|
|
@@ -43,15 +41,12 @@ const Story = ({ spaceKey }: { spaceKey: PublicKey }) => {
|
|
|
43
41
|
return null;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
// TODO(wittjosiah): Include Tooltip.Provider in `withTheme` decorator?
|
|
47
44
|
return (
|
|
48
|
-
<
|
|
49
|
-
<div className='flex
|
|
50
|
-
<
|
|
51
|
-
<CommentsContainer threads={threads} detached={detached} onThreadDelete={console.log} />
|
|
52
|
-
</div>
|
|
45
|
+
<div className='flex justify-center overflow-y-auto bg-white dark:bg-black'>
|
|
46
|
+
<div className='flex flex-col w-[400px]'>
|
|
47
|
+
<CommentsContainer threads={threads} detached={detached} onThreadDelete={console.log} />
|
|
53
48
|
</div>
|
|
54
|
-
</
|
|
49
|
+
</div>
|
|
55
50
|
);
|
|
56
51
|
};
|
|
57
52
|
|
|
@@ -59,7 +54,7 @@ export default {
|
|
|
59
54
|
title: 'plugin-thread/Comments',
|
|
60
55
|
// TODO(wittjosiah): Register schemas.
|
|
61
56
|
render: () => <ClientRepeater component={Story} createIdentity createSpace types={[ThreadType, MessageType]} />,
|
|
62
|
-
decorators: [withTheme,
|
|
57
|
+
decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true })],
|
|
63
58
|
parameters: { translations },
|
|
64
59
|
};
|
|
65
60
|
|