@dxos/plugin-thread 0.6.13-staging.1e988a3 → 0.6.14-main.1366248
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-O64ZECMF.mjs → ThreadArticle-6RFOFTU2.mjs} +3 -3
- package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs → ThreadMain-45OFO3G6.mjs} +3 -3
- package/dist/lib/browser/{chunk-P7ZE5N5M.mjs → chunk-OTCR7JTS.mjs} +17 -17
- package/dist/lib/browser/chunk-OTCR7JTS.mjs.map +7 -0
- package/dist/lib/browser/chunk-YLYCGZ62.mjs +17 -0
- package/dist/lib/browser/chunk-YLYCGZ62.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GKPZHUT3.mjs → chunk-YQUONHZJ.mjs} +2 -2
- package/dist/lib/browser/chunk-YQUONHZJ.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +452 -388
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/lib/browser/types.mjs +2 -2
- package/dist/lib/node/{ThreadArticle-DO56JXBU.cjs → ThreadArticle-IUK2EECC.cjs} +7 -7
- package/dist/lib/node/{ThreadMain-XJX75GHG.cjs → ThreadMain-CXIQPH4H.cjs} +7 -7
- package/dist/lib/node/{chunk-D5GOVXHS.cjs → chunk-LGA276VA.cjs} +6 -6
- package/dist/lib/node/chunk-LGA276VA.cjs.map +7 -0
- package/dist/lib/node/{chunk-5PZNPPQF.cjs → chunk-XOW5SL5M.cjs} +66 -66
- package/dist/lib/node/chunk-XOW5SL5M.cjs.map +7 -0
- package/dist/lib/node/{chunk-BWBONZGU.cjs → chunk-YWFYN3OY.cjs} +8 -22
- package/dist/lib/node/chunk-YWFYN3OY.cjs.map +7 -0
- package/dist/lib/node/index.cjs +501 -441
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.cjs +4 -4
- package/dist/lib/node/meta.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +3 -3
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/lib/node-esm/ThreadArticle-OA3T4XFJ.mjs +19 -0
- package/dist/lib/node-esm/ThreadArticle-OA3T4XFJ.mjs.map +7 -0
- package/dist/lib/node-esm/ThreadMain-VXFO6JL6.mjs +27 -0
- package/dist/lib/node-esm/ThreadMain-VXFO6JL6.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-3CZBPOEH.mjs +21 -0
- package/dist/lib/node-esm/chunk-3CZBPOEH.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-5R5QKQY4.mjs +411 -0
- package/dist/lib/node-esm/chunk-5R5QKQY4.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-SSEJYCYQ.mjs +19 -0
- package/dist/lib/node-esm/chunk-SSEJYCYQ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1167 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/meta.mjs +12 -0
- package/dist/lib/node-esm/meta.mjs.map +7 -0
- package/dist/lib/node-esm/types.mjs +9 -0
- package/dist/lib/node-esm/types.mjs.map +7 -0
- package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
- package/dist/types/src/components/Chat.stories.d.ts +3 -42
- 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 +1 -1
- package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
- package/dist/types/src/components/Comments.stories.d.ts +3 -41
- package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/components/CommentsContainer.d.ts +1 -5
- package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
- package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
- package/dist/types/src/components/ThreadComplementary.d.ts +10 -0
- package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +6 -6
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +2 -0
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/components/types.d.ts +2 -2
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +2 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/extensions/threads.d.ts +9 -0
- package/dist/types/src/extensions/threads.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +4 -9
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +8 -2
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +42 -46
- package/src/ThreadPlugin.tsx +158 -309
- package/src/components/Chat.stories.tsx +7 -4
- package/src/components/ChatContainer.tsx +7 -8
- package/src/components/CommentContainer.tsx +25 -23
- package/src/components/Comments.stories.tsx +9 -6
- package/src/components/CommentsContainer.tsx +18 -31
- package/src/components/MessageContainer.tsx +15 -24
- package/src/components/ThreadComplementary.tsx +113 -0
- package/src/components/ThreadSettings.tsx +3 -3
- package/src/components/index.ts +6 -6
- package/src/components/types.ts +2 -2
- package/src/extensions/index.ts +5 -0
- package/src/extensions/threads.ts +117 -0
- package/src/meta.ts +16 -0
- package/src/translations.ts +1 -0
- package/src/types.ts +11 -4
- package/dist/lib/browser/chunk-5IBJO53M.mjs +0 -21
- package/dist/lib/browser/chunk-5IBJO53M.mjs.map +0 -7
- package/dist/lib/browser/chunk-GKPZHUT3.mjs.map +0 -7
- package/dist/lib/browser/chunk-P7ZE5N5M.mjs.map +0 -7
- package/dist/lib/node/chunk-5PZNPPQF.cjs.map +0 -7
- package/dist/lib/node/chunk-BWBONZGU.cjs.map +0 -7
- package/dist/lib/node/chunk-D5GOVXHS.cjs.map +0 -7
- package/dist/types/src/types.test.d.ts +0 -2
- package/dist/types/src/types.test.d.ts.map +0 -1
- package/src/meta.tsx +0 -20
- package/src/types.test.ts +0 -13
- /package/dist/lib/browser/{ThreadArticle-O64ZECMF.mjs.map → ThreadArticle-6RFOFTU2.mjs.map} +0 -0
- /package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs.map → ThreadMain-45OFO3G6.mjs.map} +0 -0
- /package/dist/lib/node/{ThreadArticle-DO56JXBU.cjs.map → ThreadArticle-IUK2EECC.cjs.map} +0 -0
- /package/dist/lib/node/{ThreadMain-XJX75GHG.cjs.map → ThreadMain-CXIQPH4H.cjs.map} +0 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
+
import { type Meta } from '@storybook/react';
|
|
7
8
|
import React, { useEffect, useState } from 'react';
|
|
8
9
|
|
|
9
10
|
import { SurfaceProvider } from '@dxos/app-framework';
|
|
@@ -64,13 +65,15 @@ const Story = () => {
|
|
|
64
65
|
);
|
|
65
66
|
};
|
|
66
67
|
|
|
67
|
-
export
|
|
68
|
-
|
|
68
|
+
export const Default = {};
|
|
69
|
+
|
|
70
|
+
const meta: Meta = {
|
|
71
|
+
title: 'plugins/plugin-thread/Chat',
|
|
69
72
|
component: Thread,
|
|
70
|
-
// TODO(
|
|
73
|
+
// TODO(burdon): Use decorator.
|
|
71
74
|
render: () => <ClientRepeater component={Story} createIdentity createSpace types={[ThreadType]} />,
|
|
72
75
|
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
73
76
|
parameters: { translations },
|
|
74
77
|
};
|
|
75
78
|
|
|
76
|
-
export
|
|
79
|
+
export default meta;
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Chat } from '@phosphor-icons/react';
|
|
6
5
|
import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
7
6
|
|
|
8
7
|
import { create } from '@dxos/echo-schema';
|
|
9
8
|
import { MessageType } from '@dxos/plugin-space/types';
|
|
10
|
-
import { getSpace, useMembers } from '@dxos/react-client/echo';
|
|
9
|
+
import { fullyQualifiedId, getSpace, useMembers } from '@dxos/react-client/echo';
|
|
11
10
|
import { useIdentity } from '@dxos/react-client/halo';
|
|
12
|
-
import { ScrollArea, useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
13
|
-
import { PlankHeading
|
|
11
|
+
import { Icon, ScrollArea, useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { PlankHeading } from '@dxos/react-ui-deck';
|
|
14
13
|
import { createBasicExtensions, createThemeExtensions, listener } from '@dxos/react-ui-editor';
|
|
15
14
|
import { mx } from '@dxos/react-ui-theme';
|
|
16
15
|
import { MessageTextbox, type MessageTextboxProps, Thread, ThreadFooter, threadLayout } from '@dxos/react-ui-thread';
|
|
@@ -28,7 +27,7 @@ export const ChatHeading = ({ attendableId }: { attendableId?: string }) => {
|
|
|
28
27
|
return (
|
|
29
28
|
<div role='none' className='flex items-center'>
|
|
30
29
|
<PlankHeading.Button attendableId={attendableId}>
|
|
31
|
-
<
|
|
30
|
+
<Icon icon='ph--chat--regular' size={5} />
|
|
32
31
|
</PlankHeading.Button>
|
|
33
32
|
<PlankHeading.Label attendableId={attendableId}>{t('chat heading')}</PlankHeading.Label>
|
|
34
33
|
</div>
|
|
@@ -39,7 +38,7 @@ export const ChatContainer = ({ thread, context, current, autoFocusTextbox }: Th
|
|
|
39
38
|
const identity = useIdentity()!;
|
|
40
39
|
const space = getSpace(thread);
|
|
41
40
|
const members = useMembers(space?.key);
|
|
42
|
-
const activity = useStatus(space, thread
|
|
41
|
+
const activity = useStatus(space, fullyQualifiedId(thread));
|
|
43
42
|
const { t } = useTranslation(THREAD_PLUGIN);
|
|
44
43
|
// TODO(wittjosiah): This is a hack to reset the editor after a message is sent.
|
|
45
44
|
const [_count, _setCount] = useState(0);
|
|
@@ -48,7 +47,7 @@ export const ChatContainer = ({ thread, context, current, autoFocusTextbox }: Th
|
|
|
48
47
|
const threadScrollRef = useRef<HTMLDivElement | null>(null);
|
|
49
48
|
const { themeMode } = useThemeContext();
|
|
50
49
|
|
|
51
|
-
const textboxMetadata = getMessageMetadata(thread
|
|
50
|
+
const textboxMetadata = getMessageMetadata(fullyQualifiedId(thread), identity);
|
|
52
51
|
const messageRef = useRef('');
|
|
53
52
|
const extensions = useMemo(
|
|
54
53
|
() => [
|
|
@@ -103,7 +102,7 @@ export const ChatContainer = ({ thread, context, current, autoFocusTextbox }: Th
|
|
|
103
102
|
return (
|
|
104
103
|
<Thread
|
|
105
104
|
current={current}
|
|
106
|
-
id={thread
|
|
105
|
+
id={fullyQualifiedId(thread)}
|
|
107
106
|
classNames='bs-full grid-rows-[1fr_min-content_min-content] overflow-hidden transition-[padding-block-end] [[data-sidebar-inline-start-state=open]_&]:lg:pbe-0'
|
|
108
107
|
>
|
|
109
108
|
<ScrollArea.Root classNames='col-span-2'>
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { CheckCircle, X } from '@phosphor-icons/react';
|
|
6
|
-
import React, { useCallback,
|
|
6
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { create } from '@dxos/echo-schema';
|
|
9
9
|
import { MessageType } from '@dxos/plugin-space/types';
|
|
10
|
-
import { getSpace, useMembers } from '@dxos/react-client/echo';
|
|
10
|
+
import { fullyQualifiedId, getSpace, useMembers } from '@dxos/react-client/echo';
|
|
11
11
|
import { useIdentity } from '@dxos/react-client/halo';
|
|
12
12
|
import { Button, Tag, Tooltip, useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
13
13
|
import { createBasicExtensions, createThemeExtensions, listener } from '@dxos/react-ui-editor';
|
|
@@ -30,6 +30,9 @@ import { getMessageMetadata } from '../util';
|
|
|
30
30
|
|
|
31
31
|
const sizeClass = getSize(4);
|
|
32
32
|
|
|
33
|
+
// TODO(thure): #8149
|
|
34
|
+
const commentControlClassNames = '!p-1 !min-bs-0 transition-opacity';
|
|
35
|
+
|
|
33
36
|
const ToggleResolvedButton = ({
|
|
34
37
|
isResolved,
|
|
35
38
|
onResolve,
|
|
@@ -46,7 +49,7 @@ const ToggleResolvedButton = ({
|
|
|
46
49
|
variant='ghost'
|
|
47
50
|
data-testid='thread.toggle-resolved'
|
|
48
51
|
onClick={onResolve}
|
|
49
|
-
classNames={[
|
|
52
|
+
classNames={[commentControlClassNames, !isResolved && hoverableControlItem]}
|
|
50
53
|
>
|
|
51
54
|
<span className='sr-only'>{label}</span>
|
|
52
55
|
{isResolved ? <CheckCircle className={sizeClass} weight='fill' /> : <CheckCircle className={sizeClass} />}
|
|
@@ -72,7 +75,7 @@ const DeleteThreadButton = ({ onDelete }: { onDelete: () => void }) => {
|
|
|
72
75
|
variant='ghost'
|
|
73
76
|
data-testid='thread.delete'
|
|
74
77
|
onClick={onDelete}
|
|
75
|
-
classNames={[
|
|
78
|
+
classNames={[commentControlClassNames, hoverableControlItem]}
|
|
76
79
|
>
|
|
77
80
|
<span className='sr-only'>{label}</span>
|
|
78
81
|
<X className={sizeClass} />
|
|
@@ -93,7 +96,6 @@ export const CommentContainer = ({
|
|
|
93
96
|
detached,
|
|
94
97
|
context,
|
|
95
98
|
current,
|
|
96
|
-
autoFocusTextbox,
|
|
97
99
|
onAttend,
|
|
98
100
|
onThreadDelete,
|
|
99
101
|
onMessageDelete,
|
|
@@ -103,13 +105,12 @@ export const CommentContainer = ({
|
|
|
103
105
|
const identity = useIdentity()!;
|
|
104
106
|
const space = getSpace(thread);
|
|
105
107
|
const members = useMembers(space?.key);
|
|
106
|
-
const activity = useStatus(space, thread
|
|
108
|
+
const activity = useStatus(space, fullyQualifiedId(thread));
|
|
107
109
|
const { t } = useTranslation(THREAD_PLUGIN);
|
|
108
110
|
const threadScrollRef = useRef<HTMLDivElement | null>(null);
|
|
109
|
-
const [autoFocus, setAutoFocus] = useState(!!autoFocusTextbox);
|
|
110
111
|
const { themeMode } = useThemeContext();
|
|
111
112
|
|
|
112
|
-
const textboxMetadata = getMessageMetadata(thread
|
|
113
|
+
const textboxMetadata = getMessageMetadata(fullyQualifiedId(thread), identity);
|
|
113
114
|
// TODO(wittjosiah): This is a hack to reset the editor after a message is sent.
|
|
114
115
|
const [_count, _setCount] = useState(0);
|
|
115
116
|
const rerenderEditor = () => _setCount((count) => count + 1);
|
|
@@ -124,13 +125,6 @@ export const CommentContainer = ({
|
|
|
124
125
|
[_count],
|
|
125
126
|
);
|
|
126
127
|
|
|
127
|
-
// TODO(thure): Because of the way the `autoFocus` property is handled by TextEditor,
|
|
128
|
-
// this is the least-bad way of moving focus at the right time, though it is an anti-pattern.
|
|
129
|
-
// Refactor to behave more like <input/>’s `autoFocus` or `autofocus` (yes, they’re different).
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
setAutoFocus(!!autoFocusTextbox);
|
|
132
|
-
}, [autoFocusTextbox]);
|
|
133
|
-
|
|
134
128
|
// TODO(thure): Factor out.
|
|
135
129
|
const scrollToEnd = (behavior: ScrollBehavior) =>
|
|
136
130
|
setTimeout(() => threadScrollRef.current?.scrollIntoView({ behavior, block: 'end' }), 10);
|
|
@@ -151,7 +145,6 @@ export const CommentContainer = ({
|
|
|
151
145
|
onComment?.(thread);
|
|
152
146
|
|
|
153
147
|
messageRef.current = '';
|
|
154
|
-
setAutoFocus(true);
|
|
155
148
|
scrollToEnd('instant');
|
|
156
149
|
rerenderEditor();
|
|
157
150
|
|
|
@@ -159,10 +152,8 @@ export const CommentContainer = ({
|
|
|
159
152
|
return true;
|
|
160
153
|
}, [thread, identity]);
|
|
161
154
|
|
|
162
|
-
const handleDeleteMessage = (id: string) => onMessageDelete?.(id);
|
|
163
|
-
|
|
164
155
|
return (
|
|
165
|
-
<Thread onClickCapture={onAttend} onFocusCapture={onAttend} current={current} id={thread
|
|
156
|
+
<Thread onClickCapture={onAttend} onFocusCapture={onAttend} current={current} id={fullyQualifiedId(thread)}>
|
|
166
157
|
<div
|
|
167
158
|
role='none'
|
|
168
159
|
className={mx(
|
|
@@ -174,7 +165,7 @@ export const CommentContainer = ({
|
|
|
174
165
|
{detached ? (
|
|
175
166
|
<Tooltip.Root>
|
|
176
167
|
<Tooltip.Trigger asChild>
|
|
177
|
-
<ThreadHeading detached>{thread.name
|
|
168
|
+
<ThreadHeading detached>{thread.name}</ThreadHeading>
|
|
178
169
|
</Tooltip.Trigger>
|
|
179
170
|
<Tooltip.Portal>
|
|
180
171
|
<Tooltip.Content classNames='z-[21]' side='top'>
|
|
@@ -184,7 +175,7 @@ export const CommentContainer = ({
|
|
|
184
175
|
</Tooltip.Portal>
|
|
185
176
|
</Tooltip.Root>
|
|
186
177
|
) : (
|
|
187
|
-
<ThreadHeading>{thread.name
|
|
178
|
+
<ThreadHeading>{thread.name}</ThreadHeading>
|
|
188
179
|
)}
|
|
189
180
|
<div className='flex flex-row items-center pli-1'>
|
|
190
181
|
{thread.status === 'staged' && <Tag palette='neutral'>{t('draft button')}</Tag>}
|
|
@@ -195,9 +186,20 @@ export const CommentContainer = ({
|
|
|
195
186
|
</div>
|
|
196
187
|
</div>
|
|
197
188
|
{thread.messages.filter(nonNullable).map((message) => (
|
|
198
|
-
<MessageContainer
|
|
189
|
+
<MessageContainer
|
|
190
|
+
key={message.id}
|
|
191
|
+
message={message}
|
|
192
|
+
members={members}
|
|
193
|
+
onDelete={(id: string) => onMessageDelete?.(id)}
|
|
194
|
+
/>
|
|
199
195
|
))}
|
|
200
|
-
|
|
196
|
+
{/*
|
|
197
|
+
TODO(wittjosiah): Can't autofocus this generally.
|
|
198
|
+
There can be multiple threads with inputs and they can't all be focused.
|
|
199
|
+
Also, it steals focus from documents when first rendered.
|
|
200
|
+
Need to find a way to autofocus in one scenario only: when a new thread is created.
|
|
201
|
+
*/}
|
|
202
|
+
<MessageTextbox extensions={extensions} onSend={handleCreate} {...textboxMetadata} />
|
|
201
203
|
<ThreadFooter activity={activity}>{t('activity message')}</ThreadFooter>
|
|
202
204
|
{/* NOTE(thure): This can’t also be the `overflow-anchor` because `ScrollArea` injects an interceding node that contains this necessary ref’d element. */}
|
|
203
205
|
<div role='none' className='bs-px -mbs-px' ref={threadScrollRef} />
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
+
import { type Meta } from '@storybook/react';
|
|
7
8
|
import React, { useEffect, useState } from 'react';
|
|
8
9
|
|
|
9
10
|
import { MessageType, ThreadType } from '@dxos/plugin-space/types';
|
|
10
11
|
import { faker } from '@dxos/random';
|
|
11
|
-
import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
|
|
12
|
+
import { Filter, fullyQualifiedId, useQuery, useSpace } from '@dxos/react-client/echo';
|
|
12
13
|
import { useIdentity } from '@dxos/react-client/halo';
|
|
13
14
|
import { type ClientRepeatedComponentProps, ClientRepeater } from '@dxos/react-client/testing';
|
|
14
15
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
@@ -30,7 +31,7 @@ const Story = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
30
31
|
const t = setTimeout(async () => {
|
|
31
32
|
space.db.add(createCommentThread(identity));
|
|
32
33
|
const thread = space.db.add(createCommentThread(identity));
|
|
33
|
-
setDetached([thread
|
|
34
|
+
setDetached([fullyQualifiedId(thread)]);
|
|
34
35
|
});
|
|
35
36
|
|
|
36
37
|
return () => clearTimeout(t);
|
|
@@ -50,12 +51,14 @@ const Story = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
50
51
|
);
|
|
51
52
|
};
|
|
52
53
|
|
|
53
|
-
export
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
export const Default = {};
|
|
55
|
+
|
|
56
|
+
const meta: Meta = {
|
|
57
|
+
title: 'plugins/plugin-thread/Comments',
|
|
58
|
+
// TODO(wittjosiah): Use decorator.
|
|
56
59
|
render: () => <ClientRepeater component={Story} createIdentity createSpace types={[ThreadType, MessageType]} />,
|
|
57
60
|
decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true })],
|
|
58
61
|
parameters: { translations },
|
|
59
62
|
};
|
|
60
63
|
|
|
61
|
-
export
|
|
64
|
+
export default meta;
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { ChatText
|
|
5
|
+
import { ChatText } from '@phosphor-icons/react';
|
|
6
6
|
import React, { useEffect } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type ThreadType } from '@dxos/plugin-space/types';
|
|
9
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
9
10
|
import { useTranslation, Trans } from '@dxos/react-ui';
|
|
10
|
-
import { PlankHeading, plankHeadingIconProps } from '@dxos/react-ui-deck';
|
|
11
11
|
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
12
12
|
|
|
13
13
|
import { CommentContainer } from './CommentContainer';
|
|
@@ -24,7 +24,6 @@ export type ThreadsContainerProps = Omit<
|
|
|
24
24
|
*/
|
|
25
25
|
detached?: string[];
|
|
26
26
|
currentId?: string;
|
|
27
|
-
autoFocusCurrentTextbox?: boolean;
|
|
28
27
|
showResolvedThreads?: boolean;
|
|
29
28
|
onThreadAttend?: (thread: ThreadType) => void;
|
|
30
29
|
onThreadDelete?: (thread: ThreadType) => void;
|
|
@@ -33,19 +32,6 @@ export type ThreadsContainerProps = Omit<
|
|
|
33
32
|
onComment?: (thread: ThreadType) => void;
|
|
34
33
|
};
|
|
35
34
|
|
|
36
|
-
export const CommentsHeading = ({ attendableId }: { attendableId?: string }) => {
|
|
37
|
-
const { t } = useTranslation(THREAD_PLUGIN);
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div role='none' className='flex items-center'>
|
|
41
|
-
<PlankHeading.Button attendableId={attendableId}>
|
|
42
|
-
<Quotes {...plankHeadingIconProps} />
|
|
43
|
-
</PlankHeading.Button>
|
|
44
|
-
<PlankHeading.Label attendableId={attendableId}>{t('comments heading')}</PlankHeading.Label>
|
|
45
|
-
</div>
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
35
|
/**
|
|
50
36
|
* Comment threads.
|
|
51
37
|
*/
|
|
@@ -53,7 +39,6 @@ export const CommentsContainer = ({
|
|
|
53
39
|
threads,
|
|
54
40
|
detached = [],
|
|
55
41
|
currentId,
|
|
56
|
-
autoFocusCurrentTextbox,
|
|
57
42
|
showResolvedThreads,
|
|
58
43
|
onThreadAttend,
|
|
59
44
|
onThreadDelete,
|
|
@@ -73,20 +58,22 @@ export const CommentsContainer = ({
|
|
|
73
58
|
return (
|
|
74
59
|
<>
|
|
75
60
|
{filteredThreads.length > 0 ? (
|
|
76
|
-
filteredThreads.map((thread) =>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
61
|
+
filteredThreads.map((thread) => {
|
|
62
|
+
const threadId = fullyQualifiedId(thread);
|
|
63
|
+
return (
|
|
64
|
+
<CommentContainer
|
|
65
|
+
key={threadId}
|
|
66
|
+
thread={thread}
|
|
67
|
+
current={currentId === threadId}
|
|
68
|
+
detached={detached.includes(threadId)}
|
|
69
|
+
{...(onThreadAttend && { onAttend: () => onThreadAttend(thread) })}
|
|
70
|
+
{...(onThreadDelete && { onThreadDelete: () => onThreadDelete(thread) })}
|
|
71
|
+
{...(onMessageDelete && { onMessageDelete: (messageId: string) => onMessageDelete(thread, messageId) })}
|
|
72
|
+
{...(onThreadToggleResolved && { onResolve: () => onThreadToggleResolved(thread) })}
|
|
73
|
+
{...props}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
})
|
|
90
77
|
) : (
|
|
91
78
|
<div
|
|
92
79
|
role='alert'
|
|
@@ -1,25 +1,17 @@
|
|
|
1
1
|
//
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
|
+
|
|
4
5
|
import { EditorView } from '@codemirror/view';
|
|
5
6
|
import { Check, PencilSimple, X } from '@phosphor-icons/react';
|
|
6
7
|
import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
|
7
8
|
|
|
8
9
|
import { Surface } from '@dxos/app-framework';
|
|
9
|
-
import { type Expando, type EchoReactiveObject } from '@dxos/echo-schema';
|
|
10
10
|
import { type MessageType } from '@dxos/plugin-space/types';
|
|
11
11
|
import { PublicKey } from '@dxos/react-client';
|
|
12
|
-
import { type SpaceMember } from '@dxos/react-client/echo';
|
|
12
|
+
import { type EchoReactiveObject, type Expando, type SpaceMember } from '@dxos/react-client/echo';
|
|
13
13
|
import { useIdentity, type Identity } from '@dxos/react-client/halo';
|
|
14
|
-
import {
|
|
15
|
-
Button,
|
|
16
|
-
ButtonGroup,
|
|
17
|
-
DensityProvider,
|
|
18
|
-
Tooltip,
|
|
19
|
-
useOnTransition,
|
|
20
|
-
useThemeContext,
|
|
21
|
-
useTranslation,
|
|
22
|
-
} from '@dxos/react-ui';
|
|
14
|
+
import { Button, ButtonGroup, Tooltip, useOnTransition, useThemeContext, useTranslation } from '@dxos/react-ui';
|
|
23
15
|
import { createBasicExtensions, createThemeExtensions, useTextEditor } from '@dxos/react-ui-editor';
|
|
24
16
|
import { Mosaic, type MosaicTileComponent } from '@dxos/react-ui-mosaic';
|
|
25
17
|
import {
|
|
@@ -37,7 +29,8 @@ import { useOnEditAnalytics } from '../hooks';
|
|
|
37
29
|
import { THREAD_ITEM, THREAD_PLUGIN } from '../meta';
|
|
38
30
|
import { getMessageMetadata } from '../util';
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
// TODO(thure): #8149
|
|
33
|
+
const messageControlClassNames = ['!p-1 !min-bs-0 transition-opacity', hoverableControlItem];
|
|
41
34
|
|
|
42
35
|
export const MessageContainer = ({
|
|
43
36
|
message,
|
|
@@ -91,7 +84,7 @@ export const MessageContainer = ({
|
|
|
91
84
|
variant='ghost'
|
|
92
85
|
data-testid='thread.message.delete'
|
|
93
86
|
classNames={messageControlClassNames}
|
|
94
|
-
onClick={handleDelete}
|
|
87
|
+
onClick={() => handleDelete()}
|
|
95
88
|
>
|
|
96
89
|
<span className='sr-only'>{deleteLabel}</span>
|
|
97
90
|
<X className={getSize(4)} />
|
|
@@ -171,7 +164,7 @@ const TextboxBlock = ({
|
|
|
171
164
|
};
|
|
172
165
|
|
|
173
166
|
const MessageBlockObjectTile: MosaicTileComponent<EchoReactiveObject<any>> = forwardRef(
|
|
174
|
-
({ draggableStyle, draggableProps, item, active, ...props }, forwardedRef) => {
|
|
167
|
+
({ draggableStyle, draggableProps, item, active, ref: _ref, ...props }, forwardedRef) => {
|
|
175
168
|
let title = item.name ?? item.title ?? item.__typename ?? 'Object';
|
|
176
169
|
if (typeof title !== 'string') {
|
|
177
170
|
title = title?.content ?? '';
|
|
@@ -184,16 +177,14 @@ const MessageBlockObjectTile: MosaicTileComponent<EchoReactiveObject<any>> = for
|
|
|
184
177
|
style={draggableStyle}
|
|
185
178
|
ref={forwardedRef}
|
|
186
179
|
>
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
/>
|
|
196
|
-
</DensityProvider>
|
|
180
|
+
<Surface
|
|
181
|
+
role='card'
|
|
182
|
+
limit={1}
|
|
183
|
+
data={{ content: item }}
|
|
184
|
+
draggableProps={draggableProps}
|
|
185
|
+
fallback={title}
|
|
186
|
+
{...props}
|
|
187
|
+
/>
|
|
197
188
|
</div>
|
|
198
189
|
);
|
|
199
190
|
},
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { LayoutAction, type Plugin, useIntentDispatcher, useResolvePlugins } from '@dxos/app-framework';
|
|
8
|
+
import { type ThreadType } from '@dxos/plugin-space';
|
|
9
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
10
|
+
import { ScrollArea } from '@dxos/react-ui';
|
|
11
|
+
import { useAttended } from '@dxos/react-ui-attention';
|
|
12
|
+
import { nonNullable } from '@dxos/util';
|
|
13
|
+
|
|
14
|
+
import { CommentsContainer } from '../components';
|
|
15
|
+
import { THREAD_PLUGIN } from '../meta';
|
|
16
|
+
import { ThreadAction, type ThreadProvides } from '../types';
|
|
17
|
+
|
|
18
|
+
const providesThreadsConfig = (plugin: any): Plugin<ThreadProvides<any>> | undefined =>
|
|
19
|
+
'thread' in plugin.provides ? (plugin as Plugin<ThreadProvides<any>>) : undefined;
|
|
20
|
+
|
|
21
|
+
export const ThreadComplementary = ({
|
|
22
|
+
role,
|
|
23
|
+
subject,
|
|
24
|
+
drafts,
|
|
25
|
+
current,
|
|
26
|
+
showResolvedThreads,
|
|
27
|
+
}: {
|
|
28
|
+
role: string;
|
|
29
|
+
subject: any;
|
|
30
|
+
drafts: ThreadType[] | undefined;
|
|
31
|
+
current?: string;
|
|
32
|
+
showResolvedThreads?: boolean;
|
|
33
|
+
}) => {
|
|
34
|
+
const dispatch = useIntentDispatcher();
|
|
35
|
+
|
|
36
|
+
const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
|
|
37
|
+
const threadProvides = useMemo(() => threadsIntegrators.map((p) => p.provides.thread), [threadsIntegrators]);
|
|
38
|
+
const createSort = useMemo(
|
|
39
|
+
() => threadProvides.find((p) => p.predicate(subject))?.createSort,
|
|
40
|
+
[threadProvides, subject],
|
|
41
|
+
);
|
|
42
|
+
const sort = useMemo(() => createSort?.(subject), [createSort, subject]);
|
|
43
|
+
|
|
44
|
+
const threads = useMemo(() => {
|
|
45
|
+
return subject.threads.concat(drafts ?? []).filter(nonNullable) as ThreadType[];
|
|
46
|
+
}, [JSON.stringify(subject.threads), JSON.stringify(drafts)]);
|
|
47
|
+
|
|
48
|
+
const detachedIds = useMemo(() => {
|
|
49
|
+
return threads.filter(({ anchor }) => !anchor).map((thread) => fullyQualifiedId(thread));
|
|
50
|
+
}, [threads]);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!sort) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
threads.sort((a, b) => sort(a?.anchor, b?.anchor));
|
|
57
|
+
}, [sort, threads]);
|
|
58
|
+
|
|
59
|
+
const attended = useAttended();
|
|
60
|
+
const qualifiedSubjectId = fullyQualifiedId(subject);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div role='none' className='contents'>
|
|
64
|
+
<ScrollArea.Root classNames='row-span-2'>
|
|
65
|
+
<ScrollArea.Viewport>
|
|
66
|
+
<CommentsContainer
|
|
67
|
+
threads={threads}
|
|
68
|
+
detached={detachedIds}
|
|
69
|
+
currentId={attended.includes(qualifiedSubjectId) ? current : undefined}
|
|
70
|
+
showResolvedThreads={showResolvedThreads}
|
|
71
|
+
onThreadAttend={(thread) => {
|
|
72
|
+
const threadId = fullyQualifiedId(thread);
|
|
73
|
+
if (current !== threadId) {
|
|
74
|
+
current = threadId;
|
|
75
|
+
|
|
76
|
+
void dispatch?.([
|
|
77
|
+
{
|
|
78
|
+
action: LayoutAction.SCROLL_INTO_VIEW,
|
|
79
|
+
data: {
|
|
80
|
+
id: fullyQualifiedId(subject),
|
|
81
|
+
thread: threadId,
|
|
82
|
+
cursor: thread.anchor,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
}}
|
|
88
|
+
onThreadDelete={(thread) => {
|
|
89
|
+
return dispatch({ plugin: THREAD_PLUGIN, action: ThreadAction.DELETE, data: { subject, thread } });
|
|
90
|
+
}}
|
|
91
|
+
onMessageDelete={(thread, messageId) =>
|
|
92
|
+
dispatch({
|
|
93
|
+
plugin: THREAD_PLUGIN,
|
|
94
|
+
action: ThreadAction.DELETE_MESSAGE,
|
|
95
|
+
data: { subject, thread, messageId },
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
onThreadToggleResolved={(thread) =>
|
|
99
|
+
dispatch({ plugin: THREAD_PLUGIN, action: ThreadAction.TOGGLE_RESOLVED, data: { thread } })
|
|
100
|
+
}
|
|
101
|
+
onComment={(thread) => {
|
|
102
|
+
void dispatch({ action: ThreadAction.ON_MESSAGE_ADD, data: { thread, subject } });
|
|
103
|
+
}}
|
|
104
|
+
/>
|
|
105
|
+
<div role='none' className='bs-10' />
|
|
106
|
+
<ScrollArea.Scrollbar>
|
|
107
|
+
<ScrollArea.Thumb />
|
|
108
|
+
</ScrollArea.Scrollbar>
|
|
109
|
+
</ScrollArea.Viewport>
|
|
110
|
+
</ScrollArea.Root>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import { SettingsValue } from '@dxos/plugin-settings';
|
|
8
7
|
import { Input, useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { DeprecatedFormInput } from '@dxos/react-ui-data';
|
|
9
9
|
|
|
10
10
|
import { THREAD_PLUGIN } from '../meta';
|
|
11
11
|
import type { ThreadSettingsProps } from '../types';
|
|
@@ -15,9 +15,9 @@ export const ThreadSettings = ({ settings }: { settings: ThreadSettingsProps })
|
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
17
|
<>
|
|
18
|
-
<
|
|
18
|
+
<DeprecatedFormInput label={t('settings standalone label')}>
|
|
19
19
|
<Input.Switch checked={settings.standalone} onCheckedChange={(checked) => (settings.standalone = !!checked)} />
|
|
20
|
-
</
|
|
20
|
+
</DeprecatedFormInput>
|
|
21
21
|
</>
|
|
22
22
|
);
|
|
23
23
|
};
|
package/src/components/index.ts
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { lazy } from 'react';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
export const
|
|
9
|
-
export const ThreadArticle = React.lazy(() => import('./ThreadArticle'));
|
|
7
|
+
export const ThreadMain = lazy(() => import('./ThreadMain'));
|
|
8
|
+
export const ThreadArticle = lazy(() => import('./ThreadArticle'));
|
|
10
9
|
|
|
11
10
|
// TODO(wittjosiah): Suspense boundary for sidebar?
|
|
12
|
-
export * from './ThreadSettings';
|
|
13
11
|
export * from './CommentContainer';
|
|
14
|
-
export * from './MessageContainer';
|
|
15
12
|
export * from './CommentsContainer';
|
|
16
13
|
export * from './ChatContainer';
|
|
14
|
+
export * from './MessageContainer';
|
|
15
|
+
export * from './ThreadComplementary';
|
|
16
|
+
export * from './ThreadSettings';
|
package/src/components/types.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type EchoReactiveObject } from '@dxos/echo
|
|
5
|
+
import { type EchoReactiveObject } from '@dxos/client/echo';
|
|
6
6
|
import { type ThreadType } from '@dxos/plugin-space/types';
|
|
7
|
-
import type
|
|
7
|
+
import { type ThreadProps } from '@dxos/react-ui-thread';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Props for components which connect an ECHO Thread object to the UI component Thread.
|