@dxos/plugin-thread 0.7.4 → 0.7.5-main.937ce75
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/{ThreadArticle-6IXTL7CH.mjs → ThreadArticle-EWLTNU6Z.mjs} +2 -2
- package/dist/lib/browser/{ThreadMain-4IAS67KE.mjs → ThreadMain-ABEDFWHC.mjs} +2 -2
- package/dist/lib/browser/chunk-6KTMQPL4.mjs +77 -0
- package/dist/lib/browser/chunk-6KTMQPL4.mjs.map +7 -0
- package/dist/lib/browser/{chunk-52HWDLBR.mjs → chunk-OD2IZKRP.mjs} +23 -45
- package/dist/lib/browser/chunk-OD2IZKRP.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +323 -480
- 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/{ThreadArticle-I7ADXDW3.cjs → ThreadArticle-ZCNMFBAO.cjs} +6 -6
- package/dist/lib/node/{ThreadMain-MRNJC3TI.cjs → ThreadMain-5AYGCXXP.cjs} +6 -6
- package/dist/lib/node/{chunk-YCDM2SZM.cjs → chunk-2CB5AVBL.cjs} +27 -50
- package/dist/lib/node/chunk-2CB5AVBL.cjs.map +7 -0
- package/dist/lib/node/chunk-VW2P26MZ.cjs +96 -0
- package/dist/lib/node/chunk-VW2P26MZ.cjs.map +7 -0
- package/dist/lib/node/index.cjs +336 -491
- 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/lib/node-esm/{ThreadArticle-CJHYMF2W.mjs → ThreadArticle-F37HYTOU.mjs} +2 -2
- package/dist/lib/node-esm/{ThreadMain-VISGW4IF.mjs → ThreadMain-OL6ZHNZY.mjs} +2 -2
- package/dist/lib/node-esm/{chunk-JG7MXH6O.mjs → chunk-AQ74G3GY.mjs} +23 -45
- package/dist/lib/node-esm/chunk-AQ74G3GY.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-RMYATBXM.mjs +78 -0
- package/dist/lib/node-esm/chunk-RMYATBXM.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +323 -480
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types.mjs +1 -1
- package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
- package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
- package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
- package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
- package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
- package/dist/types/src/components/ThreadComplementary.d.ts +1 -2
- package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +50 -66
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/extensions/threads.d.ts +2 -2
- package/dist/types/src/extensions/threads.d.ts.map +1 -1
- package/dist/types/src/hooks/analytics.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +76 -8
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +30 -30
- package/src/ThreadPlugin.tsx +215 -361
- package/src/components/Chat.stories.tsx +10 -8
- package/src/components/ChatContainer.tsx +13 -11
- package/src/components/CommentContainer.tsx +14 -11
- package/src/components/MessageContainer.tsx +7 -5
- package/src/components/ThreadComplementary.tsx +18 -32
- package/src/components/testing.ts +26 -22
- package/src/extensions/threads.ts +16 -25
- package/src/hooks/analytics.ts +6 -10
- package/src/types.ts +60 -9
- package/dist/lib/browser/chunk-52HWDLBR.mjs.map +0 -7
- package/dist/lib/browser/chunk-XUHNZID5.mjs +0 -20
- package/dist/lib/browser/chunk-XUHNZID5.mjs.map +0 -7
- package/dist/lib/node/chunk-XFO6D2LG.cjs +0 -39
- package/dist/lib/node/chunk-XFO6D2LG.cjs.map +0 -7
- package/dist/lib/node/chunk-YCDM2SZM.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-I5MU54RT.mjs +0 -21
- package/dist/lib/node-esm/chunk-I5MU54RT.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JG7MXH6O.mjs.map +0 -7
- /package/dist/lib/browser/{ThreadArticle-6IXTL7CH.mjs.map → ThreadArticle-EWLTNU6Z.mjs.map} +0 -0
- /package/dist/lib/browser/{ThreadMain-4IAS67KE.mjs.map → ThreadMain-ABEDFWHC.mjs.map} +0 -0
- /package/dist/lib/node/{ThreadArticle-I7ADXDW3.cjs.map → ThreadArticle-ZCNMFBAO.cjs.map} +0 -0
- /package/dist/lib/node/{ThreadMain-MRNJC3TI.cjs.map → ThreadMain-5AYGCXXP.cjs.map} +0 -0
- /package/dist/lib/node-esm/{ThreadArticle-CJHYMF2W.mjs.map → ThreadArticle-F37HYTOU.mjs.map} +0 -0
- /package/dist/lib/node-esm/{ThreadMain-VISGW4IF.mjs.map → ThreadMain-OL6ZHNZY.mjs.map} +0 -0
package/src/ThreadPlugin.tsx
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
createIntent,
|
|
9
|
+
createResolver,
|
|
10
|
+
createSurface,
|
|
8
11
|
type IntentPluginProvides,
|
|
9
12
|
isLayoutParts,
|
|
10
13
|
LayoutAction,
|
|
11
|
-
type LocationProvides,
|
|
12
14
|
NavigationAction,
|
|
13
15
|
parseIntentPlugin,
|
|
14
16
|
parseMetadataResolverPlugin,
|
|
@@ -18,33 +20,28 @@ import {
|
|
|
18
20
|
resolvePlugin,
|
|
19
21
|
} from '@dxos/app-framework';
|
|
20
22
|
import { type UnsubscribeCallback } from '@dxos/async';
|
|
23
|
+
import { Ref } from '@dxos/echo-schema';
|
|
21
24
|
import { LocalStorageStore } from '@dxos/local-storage';
|
|
22
25
|
import { log } from '@dxos/log';
|
|
23
|
-
import { parseClientPlugin } from '@dxos/plugin-client';
|
|
24
|
-
import {
|
|
25
|
-
import { ObservabilityAction } from '@dxos/plugin-observability/
|
|
26
|
-
import { memoizeQuery
|
|
26
|
+
import { parseClientPlugin } from '@dxos/plugin-client/types';
|
|
27
|
+
import { createExtension, toSignal } from '@dxos/plugin-graph';
|
|
28
|
+
import { ObservabilityAction } from '@dxos/plugin-observability/types';
|
|
29
|
+
import { memoizeQuery } from '@dxos/plugin-space';
|
|
27
30
|
import { ChannelType, MessageType, ThreadType } from '@dxos/plugin-space/types';
|
|
28
31
|
import {
|
|
29
32
|
create,
|
|
30
33
|
fullyQualifiedId,
|
|
31
34
|
getSpace,
|
|
32
35
|
getTypename,
|
|
33
|
-
|
|
36
|
+
makeRef,
|
|
34
37
|
parseId,
|
|
35
38
|
type ReactiveEchoObject,
|
|
39
|
+
RefArray,
|
|
36
40
|
SpaceState,
|
|
37
41
|
} from '@dxos/react-client/echo';
|
|
38
42
|
import { translations as threadTranslations } from '@dxos/react-ui-thread';
|
|
39
43
|
|
|
40
|
-
import {
|
|
41
|
-
ChatContainer,
|
|
42
|
-
ChatHeading,
|
|
43
|
-
ThreadArticle,
|
|
44
|
-
ThreadComplementary,
|
|
45
|
-
ThreadMain,
|
|
46
|
-
ThreadSettings,
|
|
47
|
-
} from './components';
|
|
44
|
+
import { ThreadArticle, ThreadComplementary, ThreadSettings } from './components';
|
|
48
45
|
import { threads } from './extensions';
|
|
49
46
|
import meta, { THREAD_ITEM, THREAD_PLUGIN } from './meta';
|
|
50
47
|
import translations from './translations';
|
|
@@ -73,7 +70,6 @@ export const ThreadPlugin = (): PluginDefinition<
|
|
|
73
70
|
return viewStore[subjectId];
|
|
74
71
|
};
|
|
75
72
|
|
|
76
|
-
let navigationPlugin: Plugin<LocationProvides> | undefined;
|
|
77
73
|
let intentPlugin: Plugin<IntentPluginProvides> | undefined;
|
|
78
74
|
|
|
79
75
|
const unsubscribeCallbacks = [] as UnsubscribeCallback[];
|
|
@@ -91,8 +87,7 @@ export const ThreadPlugin = (): PluginDefinition<
|
|
|
91
87
|
},
|
|
92
88
|
};
|
|
93
89
|
},
|
|
94
|
-
ready: async (plugins) => {
|
|
95
|
-
navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
|
|
90
|
+
ready: async ({ plugins }) => {
|
|
96
91
|
intentPlugin = resolvePlugin(plugins, parseIntentPlugin)!;
|
|
97
92
|
},
|
|
98
93
|
unload: async () => {
|
|
@@ -103,15 +98,15 @@ export const ThreadPlugin = (): PluginDefinition<
|
|
|
103
98
|
metadata: {
|
|
104
99
|
records: {
|
|
105
100
|
[ChannelType.typename]: {
|
|
106
|
-
createObject: ThreadAction.
|
|
101
|
+
createObject: (props: { name?: string }) => createIntent(ThreadAction.Create, props),
|
|
107
102
|
placeholder: ['channel name placeholder', { ns: THREAD_PLUGIN }],
|
|
108
103
|
icon: 'ph--chat--regular',
|
|
109
104
|
// TODO(wittjosiah): Move out of metadata.
|
|
110
|
-
loadReferences: (channel: ChannelType) =>
|
|
105
|
+
loadReferences: async (channel: ChannelType) => await RefArray.loadAll(channel.threads ?? []),
|
|
111
106
|
},
|
|
112
107
|
[ThreadType.typename]: {
|
|
113
108
|
// TODO(wittjosiah): Move out of metadata.
|
|
114
|
-
loadReferences: (thread: ThreadType) =>
|
|
109
|
+
loadReferences: async (thread: ThreadType) => await RefArray.loadAll(thread.messages ?? []),
|
|
115
110
|
},
|
|
116
111
|
[MessageType.typename]: {
|
|
117
112
|
// TODO(wittjosiah): Move out of metadata.
|
|
@@ -144,7 +139,7 @@ export const ThreadPlugin = (): PluginDefinition<
|
|
|
144
139
|
graph: {
|
|
145
140
|
builder: (plugins) => {
|
|
146
141
|
const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
|
|
147
|
-
const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.
|
|
142
|
+
const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatchPromise;
|
|
148
143
|
const metadataResolver = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
|
|
149
144
|
if (!client || !dispatch || !metadataResolver) {
|
|
150
145
|
return [];
|
|
@@ -221,16 +216,15 @@ export const ThreadPlugin = (): PluginDefinition<
|
|
|
221
216
|
const [spaceId, objectId] = dataId.split(':');
|
|
222
217
|
|
|
223
218
|
const viewState = getViewState(dataId);
|
|
224
|
-
const toggle = () => {
|
|
219
|
+
const toggle = async () => {
|
|
225
220
|
const newToggleState = !viewState.showResolvedThreads;
|
|
226
221
|
viewState.showResolvedThreads = newToggleState;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
data: {
|
|
222
|
+
await dispatch(
|
|
223
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
230
224
|
name: 'threads.toggle-show-resolved',
|
|
231
225
|
properties: { spaceId, threadId: objectId, showResolved: newToggleState },
|
|
232
|
-
},
|
|
233
|
-
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
234
228
|
};
|
|
235
229
|
|
|
236
230
|
return [
|
|
@@ -248,405 +242,265 @@ export const ThreadPlugin = (): PluginDefinition<
|
|
|
248
242
|
];
|
|
249
243
|
},
|
|
250
244
|
}),
|
|
251
|
-
createExtension({
|
|
252
|
-
id: ThreadAction.CREATE,
|
|
253
|
-
filter: (node): node is ActionGroup =>
|
|
254
|
-
!!settings.values.standalone && isActionGroup(node) && node.id.startsWith(SpaceAction.ADD_OBJECT),
|
|
255
|
-
actions: ({ node }) => {
|
|
256
|
-
const id = node.id.split('/').at(-1);
|
|
257
|
-
const [spaceId, objectId] = id?.split(':') ?? [];
|
|
258
|
-
const space = client.spaces.get().find((space) => space.id === spaceId);
|
|
259
|
-
const object = objectId && space?.db.getObjectById(objectId);
|
|
260
|
-
const target = objectId ? object : space;
|
|
261
|
-
if (!target) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return [
|
|
266
|
-
{
|
|
267
|
-
id: `${THREAD_PLUGIN}/create/${node.id}`,
|
|
268
|
-
data: async () => {
|
|
269
|
-
await dispatch([
|
|
270
|
-
{ plugin: THREAD_PLUGIN, action: ThreadAction.CREATE },
|
|
271
|
-
{ action: SpaceAction.ADD_OBJECT, data: { target } },
|
|
272
|
-
{ action: NavigationAction.OPEN },
|
|
273
|
-
]);
|
|
274
|
-
},
|
|
275
|
-
properties: {
|
|
276
|
-
label: ['create channel label', { ns: THREAD_PLUGIN }],
|
|
277
|
-
icon: 'ph--chat--regular',
|
|
278
|
-
testId: 'threadPlugin.createObject',
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
];
|
|
282
|
-
},
|
|
283
|
-
}),
|
|
284
245
|
];
|
|
285
246
|
},
|
|
286
247
|
},
|
|
287
248
|
surface: {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
case 'article': {
|
|
301
|
-
const location = navigationPlugin?.provides.location;
|
|
302
|
-
|
|
303
|
-
if (data.object instanceof ChannelType && data.object.threads[0]) {
|
|
304
|
-
const channel = data.object;
|
|
249
|
+
definitions: ({ plugins }) => {
|
|
250
|
+
const location = resolvePlugin(plugins, parseNavigationPlugin)?.provides.location;
|
|
251
|
+
return [
|
|
252
|
+
createSurface({
|
|
253
|
+
id: `${THREAD_PLUGIN}/channel`,
|
|
254
|
+
role: 'article',
|
|
255
|
+
filter: (data): data is { subject: ChannelType } =>
|
|
256
|
+
data.subject instanceof ChannelType && !!data.subject.threads[0],
|
|
257
|
+
component: ({ data }) => {
|
|
258
|
+
const channel = data.subject;
|
|
259
|
+
const thread = channel.threads[0].target!;
|
|
305
260
|
// TODO(zan): Maybe we should have utility for positional main object ids.
|
|
306
261
|
if (isLayoutParts(location?.active) && location.active.main) {
|
|
307
262
|
const layoutEntries = location.active.main;
|
|
308
263
|
const currentPosition = layoutEntries.findIndex((entry) => channel.id === entry.id);
|
|
309
264
|
if (currentPosition > 0) {
|
|
310
265
|
const objectToTheLeft = layoutEntries[currentPosition - 1];
|
|
311
|
-
const context = getSpace(
|
|
312
|
-
return <ThreadArticle thread={
|
|
266
|
+
const context = getSpace(channel)?.db.getObjectById(objectToTheLeft.id);
|
|
267
|
+
return <ThreadArticle thread={thread} context={context} />;
|
|
313
268
|
}
|
|
314
269
|
}
|
|
315
270
|
|
|
316
|
-
return <ThreadArticle thread={
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
</>
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
break;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
case 'complementary--comments': {
|
|
332
|
-
if (
|
|
333
|
-
data.subject &&
|
|
271
|
+
return <ThreadArticle thread={thread} />;
|
|
272
|
+
},
|
|
273
|
+
}),
|
|
274
|
+
createSurface({
|
|
275
|
+
id: `${THREAD_PLUGIN}/thread`,
|
|
276
|
+
role: 'complementary--comments',
|
|
277
|
+
filter: (data): data is { subject: { threads: Ref<ThreadType>[] } } =>
|
|
278
|
+
!!data.subject &&
|
|
334
279
|
typeof data.subject === 'object' &&
|
|
335
280
|
'threads' in data.subject &&
|
|
336
281
|
Array.isArray(data.subject.threads) &&
|
|
337
|
-
!(data.subject instanceof ChannelType)
|
|
338
|
-
) {
|
|
282
|
+
!(data.subject instanceof ChannelType),
|
|
283
|
+
component: ({ data }) => {
|
|
339
284
|
const { showResolvedThreads } = getViewState(fullyQualifiedId(data.subject));
|
|
340
285
|
return (
|
|
341
286
|
<ThreadComplementary
|
|
342
|
-
role={role}
|
|
343
287
|
subject={data.subject}
|
|
344
288
|
drafts={state.drafts[fullyQualifiedId(data.subject)]}
|
|
345
289
|
current={state.current}
|
|
346
290
|
showResolvedThreads={showResolvedThreads}
|
|
347
291
|
/>
|
|
348
292
|
);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
293
|
+
},
|
|
294
|
+
}),
|
|
295
|
+
createSurface({
|
|
296
|
+
id: `${THREAD_PLUGIN}/settings`,
|
|
297
|
+
role: 'settings',
|
|
298
|
+
filter: (data): data is any => data.plugin === THREAD_PLUGIN,
|
|
299
|
+
component: () => <ThreadSettings settings={settings.values} />,
|
|
300
|
+
}),
|
|
301
|
+
];
|
|
356
302
|
},
|
|
357
303
|
},
|
|
358
304
|
intent: {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
} catch (err) {
|
|
371
|
-
log.error('Subject does not support threads array', subject?.typename);
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const subjectId = fullyQualifiedId(subject);
|
|
377
|
-
const thread = create(ThreadType, { name, anchor: cursor, messages: [], status: 'staged' });
|
|
378
|
-
const draft = state.drafts[subjectId];
|
|
379
|
-
if (draft) {
|
|
380
|
-
draft.push(thread);
|
|
381
|
-
} else {
|
|
382
|
-
state.drafts[subjectId] = [thread];
|
|
305
|
+
resolvers: () => [
|
|
306
|
+
createResolver(ThreadAction.Create, ({ name, cursor, subject }) => {
|
|
307
|
+
if (cursor && subject) {
|
|
308
|
+
// Seed the threads array if it does not exist.
|
|
309
|
+
if (subject?.threads === undefined) {
|
|
310
|
+
try {
|
|
311
|
+
// Static schema will throw an error if subject does not support threads array property.
|
|
312
|
+
subject.threads = [];
|
|
313
|
+
} catch (err) {
|
|
314
|
+
log.error('Subject does not support threads array', subject?.typename);
|
|
315
|
+
return;
|
|
383
316
|
}
|
|
317
|
+
}
|
|
384
318
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
action: ThreadAction.SELECT,
|
|
391
|
-
data: { current: fullyQualifiedId(thread) },
|
|
392
|
-
},
|
|
393
|
-
],
|
|
394
|
-
[
|
|
395
|
-
{
|
|
396
|
-
action: NavigationAction.OPEN,
|
|
397
|
-
data: {
|
|
398
|
-
activeParts: { complementary: 'comments' },
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
],
|
|
402
|
-
[
|
|
403
|
-
{
|
|
404
|
-
action: LayoutAction.SET_LAYOUT,
|
|
405
|
-
data: {
|
|
406
|
-
element: 'complementary',
|
|
407
|
-
state: true,
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
],
|
|
411
|
-
],
|
|
412
|
-
};
|
|
319
|
+
const subjectId = fullyQualifiedId(subject);
|
|
320
|
+
const thread = create(ThreadType, { name, anchor: cursor, messages: [], status: 'staged' });
|
|
321
|
+
const draft = state.drafts[subjectId];
|
|
322
|
+
if (draft) {
|
|
323
|
+
draft.push(thread);
|
|
413
324
|
} else {
|
|
414
|
-
|
|
415
|
-
return {
|
|
416
|
-
data: create(ChannelType, { threads: [create(ThreadType, { messages: [] })] }),
|
|
417
|
-
};
|
|
325
|
+
state.drafts[subjectId] = [thread];
|
|
418
326
|
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
case ThreadAction.SELECT: {
|
|
422
|
-
state.current = intent.data?.current;
|
|
423
327
|
|
|
424
328
|
return {
|
|
425
|
-
data:
|
|
426
|
-
intents:
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
]
|
|
437
|
-
: [],
|
|
329
|
+
data: { object: thread },
|
|
330
|
+
intents: [
|
|
331
|
+
createIntent(ThreadAction.Select, { current: fullyQualifiedId(thread) }),
|
|
332
|
+
createIntent(NavigationAction.Open, { activeParts: { complementary: 'comments' } }),
|
|
333
|
+
createIntent(LayoutAction.SetLayout, { element: 'complementary', state: true }),
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
} else {
|
|
337
|
+
// NOTE: This is the standalone thread creation case.
|
|
338
|
+
return {
|
|
339
|
+
data: { object: create(ChannelType, { threads: [makeRef(create(ThreadType, { messages: [] }))] }) },
|
|
438
340
|
};
|
|
439
341
|
}
|
|
342
|
+
}),
|
|
343
|
+
createResolver(ThreadAction.Select, ({ current, skipOpen }) => {
|
|
344
|
+
state.current = current;
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
intents: !skipOpen
|
|
348
|
+
? [createIntent(NavigationAction.Open, { activeParts: { complementary: 'comments' } })]
|
|
349
|
+
: undefined,
|
|
350
|
+
};
|
|
351
|
+
}),
|
|
352
|
+
createResolver(ThreadAction.ToggleResolved, ({ thread }) => {
|
|
353
|
+
if (thread.status === 'active' || thread.status === undefined) {
|
|
354
|
+
thread.status = 'resolved';
|
|
355
|
+
} else if (thread.status === 'resolved') {
|
|
356
|
+
thread.status = 'active';
|
|
357
|
+
}
|
|
440
358
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
359
|
+
const space = getSpace(thread);
|
|
360
|
+
const spaceId = space?.id;
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
intents: [
|
|
364
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
365
|
+
name: 'threads.toggle-resolved',
|
|
366
|
+
properties: { threadId: thread.id, spaceId },
|
|
367
|
+
}),
|
|
368
|
+
],
|
|
369
|
+
};
|
|
370
|
+
}),
|
|
371
|
+
createResolver(ThreadAction.Delete, ({ subject, thread }, undo) => {
|
|
372
|
+
const subjectId = fullyQualifiedId(subject);
|
|
373
|
+
const draft = state.drafts[subjectId];
|
|
374
|
+
if (draft) {
|
|
375
|
+
// Check if we're deleting a draft; if so, remove it.
|
|
376
|
+
const index = draft.findIndex((t) => t.id === thread.id);
|
|
377
|
+
if (index !== -1) {
|
|
378
|
+
draft.splice(index, 1);
|
|
444
379
|
return;
|
|
445
380
|
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const space = getSpace(thread);
|
|
384
|
+
if (!space || !subject.threads) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
446
387
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
388
|
+
if (!undo) {
|
|
389
|
+
const index = subject.threads.findIndex((t: any) => t?.id === thread.id);
|
|
390
|
+
const cursor = subject.threads[index]?.anchor;
|
|
391
|
+
if (index !== -1) {
|
|
392
|
+
subject.threads?.splice(index, 1);
|
|
451
393
|
}
|
|
452
394
|
|
|
453
|
-
|
|
454
|
-
const spaceId = space?.id;
|
|
395
|
+
space.db.remove(thread);
|
|
455
396
|
|
|
456
397
|
return {
|
|
457
|
-
|
|
398
|
+
undoable: {
|
|
399
|
+
message: ['thread deleted label', { ns: THREAD_PLUGIN }],
|
|
400
|
+
data: { cursor },
|
|
401
|
+
},
|
|
458
402
|
intents: [
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
},
|
|
464
|
-
],
|
|
403
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
404
|
+
name: 'threads.delete',
|
|
405
|
+
properties: { threadId: thread.id, spaceId: space.id },
|
|
406
|
+
}),
|
|
465
407
|
],
|
|
466
408
|
};
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (
|
|
472
|
-
!(thread instanceof ThreadType) ||
|
|
473
|
-
!(subject && typeof subject === 'object' && 'threads' in subject)
|
|
474
|
-
) {
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
409
|
+
} else {
|
|
410
|
+
// TODO(wittjosiah): SDK should do this automatically.
|
|
411
|
+
const savedThread = space.db.add(thread);
|
|
412
|
+
subject.threads.push(savedThread);
|
|
477
413
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
414
|
+
return {
|
|
415
|
+
intents: [
|
|
416
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
417
|
+
name: 'threads.undo-delete',
|
|
418
|
+
properties: { threadId: thread.id, spaceId: space.id },
|
|
419
|
+
}),
|
|
420
|
+
],
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}),
|
|
424
|
+
createResolver(ThreadAction.OnMessageAdd, ({ thread, subject }) => {
|
|
425
|
+
const subjectId = fullyQualifiedId(subject);
|
|
426
|
+
const space = getSpace(subject);
|
|
427
|
+
const intents = [];
|
|
428
|
+
const analyticsProperties = { threadId: thread.id, spaceId: space?.id };
|
|
429
|
+
|
|
430
|
+
if (state.drafts[subjectId]?.find((t) => t === thread)) {
|
|
431
|
+
// Move draft to document.
|
|
432
|
+
thread.status = 'active';
|
|
433
|
+
subject.threads ? subject.threads.push(makeRef(thread)) : (subject.threads = [makeRef(thread)]);
|
|
434
|
+
state.drafts[subjectId] = state.drafts[subjectId]?.filter(({ id }) => id !== thread.id);
|
|
435
|
+
intents.push(
|
|
436
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
437
|
+
name: 'threads.thread-created',
|
|
438
|
+
properties: analyticsProperties,
|
|
439
|
+
}),
|
|
440
|
+
);
|
|
441
|
+
}
|
|
488
442
|
|
|
489
|
-
|
|
490
|
-
|
|
443
|
+
intents.push(
|
|
444
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
445
|
+
name: 'threads.message-added',
|
|
446
|
+
properties: { ...analyticsProperties, threadLength: thread.messages.length },
|
|
447
|
+
}),
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
return { intents };
|
|
451
|
+
}),
|
|
452
|
+
createResolver(ThreadAction.DeleteMessage, ({ subject, thread, messageId, message, messageIndex }, undo) => {
|
|
453
|
+
const space = getSpace(subject);
|
|
454
|
+
|
|
455
|
+
if (!undo) {
|
|
456
|
+
const messageIndex = thread.messages.findIndex(Ref.hasObjectId(messageId));
|
|
457
|
+
const message = thread.messages[messageIndex]?.target;
|
|
458
|
+
if (!message) {
|
|
491
459
|
return;
|
|
492
460
|
}
|
|
493
461
|
|
|
494
|
-
if (
|
|
495
|
-
|
|
496
|
-
const cursor = subject.threads[index]?.anchor;
|
|
497
|
-
if (index !== -1) {
|
|
498
|
-
subject.threads?.splice(index, 1);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
space.db.remove(thread);
|
|
502
|
-
|
|
503
|
-
return {
|
|
504
|
-
undoable: {
|
|
505
|
-
message: translations[0]['en-US'][THREAD_PLUGIN]['thread deleted label'],
|
|
506
|
-
data: { cursor },
|
|
507
|
-
},
|
|
508
|
-
intents: [
|
|
509
|
-
[
|
|
510
|
-
{
|
|
511
|
-
action: ObservabilityAction.SEND_EVENT,
|
|
512
|
-
data: { name: 'threads.delete', properties: { threadId: thread.id, spaceId: space.id } },
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
],
|
|
516
|
-
};
|
|
517
|
-
} else if (intent.undo) {
|
|
518
|
-
// TODO(wittjosiah): SDK should do this automatically.
|
|
519
|
-
const savedThread = space.db.add(thread);
|
|
520
|
-
subject.threads.push(savedThread);
|
|
521
|
-
|
|
462
|
+
if (messageIndex === 0 && thread.messages.length === 1) {
|
|
463
|
+
// If the message is the only message in the thread, delete the thread.
|
|
522
464
|
return {
|
|
523
|
-
|
|
524
|
-
intents: [
|
|
525
|
-
[
|
|
526
|
-
{
|
|
527
|
-
action: ObservabilityAction.SEND_EVENT,
|
|
528
|
-
data: { name: 'threads.undo-delete', properties: { threadId: thread.id, spaceId: space.id } },
|
|
529
|
-
},
|
|
530
|
-
],
|
|
531
|
-
],
|
|
465
|
+
intents: [createIntent(ThreadAction.Delete, { subject, thread })],
|
|
532
466
|
};
|
|
467
|
+
} else {
|
|
468
|
+
thread.messages.splice(messageIndex, 1);
|
|
533
469
|
}
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
case ThreadAction.ON_MESSAGE_ADD: {
|
|
538
|
-
const { thread, subject } = intent.data ?? {};
|
|
539
|
-
if (
|
|
540
|
-
!(thread instanceof ThreadType) ||
|
|
541
|
-
!(subject && typeof subject === 'object' && 'threads' in subject && Array.isArray(subject.threads))
|
|
542
|
-
) {
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
const subjectId = fullyQualifiedId(subject);
|
|
547
|
-
const space = getSpace(subject);
|
|
548
|
-
const intents = [];
|
|
549
|
-
const analyticsProperties = { threadId: thread.id, spaceId: space?.id };
|
|
550
|
-
|
|
551
|
-
if (state.drafts[subjectId]?.find((t) => t === thread)) {
|
|
552
|
-
// Move draft to document.
|
|
553
|
-
thread.status = 'active';
|
|
554
|
-
subject.threads ? subject.threads.push(thread) : (subject.threads = [thread]);
|
|
555
|
-
state.drafts[subjectId] = state.drafts[subjectId]?.filter(({ id }) => id !== thread.id);
|
|
556
|
-
intents.push({
|
|
557
|
-
action: ObservabilityAction.SEND_EVENT,
|
|
558
|
-
data: { name: 'threads.thread-created', properties: analyticsProperties },
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
intents.push({
|
|
563
|
-
action: ObservabilityAction.SEND_EVENT,
|
|
564
|
-
data: {
|
|
565
|
-
name: 'threads.message-added',
|
|
566
|
-
properties: { ...analyticsProperties, threadLength: thread.messages.length },
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
470
|
|
|
570
471
|
return {
|
|
571
|
-
|
|
572
|
-
|
|
472
|
+
undoable: {
|
|
473
|
+
message: ['message deleted label', { ns: THREAD_PLUGIN }],
|
|
474
|
+
data: { message, messageIndex },
|
|
475
|
+
},
|
|
476
|
+
intents: [
|
|
477
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
478
|
+
name: 'threads.message.delete',
|
|
479
|
+
properties: { threadId: thread.id, spaceId: space?.id },
|
|
480
|
+
}),
|
|
481
|
+
],
|
|
573
482
|
};
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
case ThreadAction.DELETE_MESSAGE: {
|
|
577
|
-
const { subject, thread, messageId } = intent.data ?? {};
|
|
578
|
-
const space = getSpace(subject);
|
|
579
|
-
|
|
580
|
-
if (
|
|
581
|
-
!(thread instanceof ThreadType) ||
|
|
582
|
-
!(subject && typeof subject === 'object' && 'threads' in subject)
|
|
583
|
-
) {
|
|
483
|
+
} else {
|
|
484
|
+
if (!messageIndex || !message) {
|
|
584
485
|
return;
|
|
585
486
|
}
|
|
586
487
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
return {
|
|
597
|
-
intents: [[{ action: ThreadAction.DELETE, data: { subject, thread } }]],
|
|
598
|
-
};
|
|
599
|
-
} else {
|
|
600
|
-
thread.messages.splice(messageIndex, 1);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
return {
|
|
604
|
-
undoable: {
|
|
605
|
-
message: translations[0]['en-US'][THREAD_PLUGIN]['message deleted label'],
|
|
606
|
-
data: { message, messageIndex },
|
|
607
|
-
},
|
|
608
|
-
intents: [
|
|
609
|
-
[
|
|
610
|
-
{
|
|
611
|
-
action: ObservabilityAction.SEND_EVENT,
|
|
612
|
-
data: {
|
|
613
|
-
name: 'threads.message.delete',
|
|
614
|
-
properties: { threadId: thread.id, spaceId: space?.id },
|
|
615
|
-
},
|
|
616
|
-
},
|
|
617
|
-
],
|
|
618
|
-
],
|
|
619
|
-
};
|
|
620
|
-
} else if (intent.undo) {
|
|
621
|
-
const message = intent.data?.message;
|
|
622
|
-
const messageIndex = intent.data?.messageIndex;
|
|
623
|
-
if (!(message instanceof MessageType) || !(typeof messageIndex === 'number')) {
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
thread.messages.splice(messageIndex, 0, message);
|
|
628
|
-
return {
|
|
629
|
-
data: true,
|
|
630
|
-
intents: [
|
|
631
|
-
[
|
|
632
|
-
{
|
|
633
|
-
action: ObservabilityAction.SEND_EVENT,
|
|
634
|
-
data: {
|
|
635
|
-
name: 'threads.message.undo-delete',
|
|
636
|
-
properties: { threadId: thread.id, spaceId: space?.id },
|
|
637
|
-
},
|
|
638
|
-
},
|
|
639
|
-
],
|
|
640
|
-
],
|
|
641
|
-
};
|
|
642
|
-
}
|
|
488
|
+
thread.messages.splice(messageIndex, 0, makeRef(message));
|
|
489
|
+
return {
|
|
490
|
+
intents: [
|
|
491
|
+
createIntent(ObservabilityAction.SendEvent, {
|
|
492
|
+
name: 'threads.message.undo-delete',
|
|
493
|
+
properties: { threadId: thread.id, spaceId: space?.id },
|
|
494
|
+
}),
|
|
495
|
+
],
|
|
496
|
+
};
|
|
643
497
|
}
|
|
644
|
-
}
|
|
645
|
-
|
|
498
|
+
}),
|
|
499
|
+
],
|
|
646
500
|
},
|
|
647
501
|
markdown: {
|
|
648
502
|
extensions: ({ document: doc }) => {
|
|
649
|
-
return threads(state, doc, intentPlugin?.provides.intent.
|
|
503
|
+
return threads(state, doc, intentPlugin?.provides.intent.dispatchPromise);
|
|
650
504
|
},
|
|
651
505
|
},
|
|
652
506
|
},
|