@hocuspocus/provider 2.15.2 → 3.0.4-rc.0
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/hocuspocus-provider.cjs +931 -1532
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +931 -1527
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/extension-database/src/Database.d.ts +1 -1
- package/dist/packages/extension-logger/src/Logger.d.ts +1 -1
- package/dist/packages/extension-redis/src/Redis.d.ts +4 -3
- package/dist/packages/extension-sqlite/src/SQLite.d.ts +2 -1
- package/dist/packages/extension-throttle/src/index.d.ts +2 -2
- package/dist/packages/extension-webhook/src/index.d.ts +3 -3
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +12 -45
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +5 -9
- package/dist/packages/provider/src/IncomingMessage.d.ts +3 -3
- package/dist/packages/provider/src/MessageReceiver.d.ts +2 -4
- package/dist/packages/provider/src/MessageSender.d.ts +2 -2
- package/dist/packages/provider/src/OutgoingMessage.d.ts +2 -2
- package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/AwarenessMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/CloseMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/QueryAwarenessMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/StatelessMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/SyncStepOneMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/SyncStepTwoMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +2 -1
- package/dist/packages/provider/src/index.d.ts +0 -2
- package/dist/packages/provider/src/types.d.ts +12 -12
- package/dist/packages/server/src/ClientConnection.d.ts +19 -10
- package/dist/packages/server/src/Connection.d.ts +7 -23
- package/dist/packages/server/src/DirectConnection.d.ts +2 -2
- package/dist/packages/server/src/Document.d.ts +3 -7
- package/dist/packages/server/src/Hocuspocus.d.ts +7 -36
- package/dist/packages/server/src/IncomingMessage.d.ts +3 -3
- package/dist/packages/server/src/MessageReceiver.d.ts +4 -6
- package/dist/packages/server/src/OutgoingMessage.d.ts +4 -3
- package/dist/packages/server/src/Server.d.ts +23 -3
- package/dist/packages/server/src/index.d.ts +1 -1
- package/dist/packages/server/src/types.d.ts +15 -29
- package/dist/packages/server/src/util/getParameters.d.ts +1 -1
- package/dist/packages/transformer/src/Prosemirror.d.ts +1 -1
- package/dist/packages/transformer/src/Tiptap.d.ts +3 -3
- package/dist/packages/transformer/src/types.d.ts +1 -1
- package/dist/tests/utils/newHocuspocus.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProvider.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +4 -3
- package/dist/tests/utils/retryableAssertion.d.ts +1 -1
- package/package.json +2 -2
- package/src/EventEmitter.ts +3 -1
- package/src/HocuspocusProvider.ts +74 -200
- package/src/HocuspocusProviderWebsocket.ts +24 -75
- package/src/IncomingMessage.ts +5 -3
- package/src/MessageReceiver.ts +20 -19
- package/src/MessageSender.ts +3 -2
- package/src/OutgoingMessage.ts +3 -2
- package/src/OutgoingMessages/AuthenticationMessage.ts +2 -1
- package/src/OutgoingMessages/AwarenessMessage.ts +2 -1
- package/src/OutgoingMessages/CloseMessage.ts +2 -1
- package/src/OutgoingMessages/QueryAwarenessMessage.ts +2 -1
- package/src/OutgoingMessages/StatelessMessage.ts +2 -1
- package/src/OutgoingMessages/SyncStepOneMessage.ts +2 -1
- package/src/OutgoingMessages/SyncStepTwoMessage.ts +2 -1
- package/src/OutgoingMessages/UpdateMessage.ts +2 -1
- package/src/index.ts +0 -2
- package/src/types.ts +12 -12
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +0 -161
- package/dist/packages/provider/src/TiptapCollabProviderWebsocket.d.ts +0 -19
- package/dist/packages/server/src/Debugger.d.ts +0 -14
- package/dist/tests/server/getMessageLogs.d.ts +0 -1
- package/dist/tests/server/requiresAuthentication.d.ts +0 -1
- package/src/TiptapCollabProvider.ts +0 -505
- package/src/TiptapCollabProviderWebsocket.ts +0 -38
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
import type { AbstractType, YArrayEvent } from 'yjs'
|
|
2
|
-
import * as Y from 'yjs'
|
|
3
|
-
import { uuidv4 } from 'lib0/random'
|
|
4
|
-
import {
|
|
5
|
-
HocuspocusProvider,
|
|
6
|
-
HocuspocusProviderConfiguration,
|
|
7
|
-
} from './HocuspocusProvider.js'
|
|
8
|
-
|
|
9
|
-
import { TiptapCollabProviderWebsocket } from './TiptapCollabProviderWebsocket.js'
|
|
10
|
-
import {
|
|
11
|
-
type DeleteCommentOptions,
|
|
12
|
-
type DeleteThreadOptions,
|
|
13
|
-
type GetThreadsOptions,
|
|
14
|
-
type TCollabComment, type TCollabThread, type THistoryVersion,
|
|
15
|
-
} from './types.js'
|
|
16
|
-
|
|
17
|
-
const defaultDeleteCommentOptions: DeleteCommentOptions = {
|
|
18
|
-
deleteContent: false,
|
|
19
|
-
deleteThread: false,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const defaultGetThreadsOptions: GetThreadsOptions = {
|
|
23
|
-
types: ['unarchived'],
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const defaultDeleteThreadOptions: DeleteThreadOptions = {
|
|
27
|
-
deleteComments: false,
|
|
28
|
-
force: false,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type TiptapCollabProviderConfiguration =
|
|
32
|
-
Required<Pick<HocuspocusProviderConfiguration, 'name'>> &
|
|
33
|
-
Partial<HocuspocusProviderConfiguration> &
|
|
34
|
-
(Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'websocketProvider'>> |
|
|
35
|
-
Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'appId'>>|
|
|
36
|
-
Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'baseUrl'>>) &
|
|
37
|
-
Pick<AdditionalTiptapCollabProviderConfiguration, 'user'> & {
|
|
38
|
-
/**
|
|
39
|
-
* Pass `true` if you want to delete a thread when the first comment is deleted.
|
|
40
|
-
*/
|
|
41
|
-
deleteThreadOnFirstCommentDelete?: boolean,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface AdditionalTiptapCollabProviderConfiguration {
|
|
45
|
-
/**
|
|
46
|
-
* A Hocuspocus Cloud App ID, get one here: https://cloud.tiptap.dev
|
|
47
|
-
*/
|
|
48
|
-
appId?: string,
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* If you are using the on-premise version of TiptapCollab, put your baseUrl here (e.g. https://collab.yourdomain.com)
|
|
52
|
-
*/
|
|
53
|
-
baseUrl?: string
|
|
54
|
-
|
|
55
|
-
websocketProvider?: TiptapCollabProviderWebsocket
|
|
56
|
-
|
|
57
|
-
user?: string
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export class TiptapCollabProvider extends HocuspocusProvider {
|
|
61
|
-
tiptapCollabConfigurationPrefix = '__tiptapcollab__'
|
|
62
|
-
|
|
63
|
-
userData?: Y.PermanentUserData
|
|
64
|
-
|
|
65
|
-
constructor(configuration: TiptapCollabProviderConfiguration) {
|
|
66
|
-
if (!configuration.websocketProvider) {
|
|
67
|
-
configuration.websocketProvider = new TiptapCollabProviderWebsocket({ appId: (configuration as Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'appId'>>).appId, baseUrl: (configuration as Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'baseUrl'>>).baseUrl })
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!configuration.token) {
|
|
71
|
-
configuration.token = 'notoken' // need to send a token anyway (which will be ignored)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
super(configuration as HocuspocusProviderConfiguration)
|
|
75
|
-
|
|
76
|
-
if (configuration.user) {
|
|
77
|
-
this.userData = new Y.PermanentUserData(this.document, this.document.getMap('__tiptapcollab__users'))
|
|
78
|
-
this.userData.setUserMapping(this.document, this.document.clientID, configuration.user)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
84
|
-
*/
|
|
85
|
-
createVersion(name?: string) {
|
|
86
|
-
return this.sendStateless(JSON.stringify({ action: 'version.create', name }))
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
91
|
-
*/
|
|
92
|
-
revertToVersion(targetVersion: number) {
|
|
93
|
-
return this.sendStateless(JSON.stringify({ action: 'document.revert', version: targetVersion }))
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
98
|
-
*
|
|
99
|
-
* The server will reply with a stateless message (THistoryVersionPreviewEvent)
|
|
100
|
-
*/
|
|
101
|
-
previewVersion(targetVersion: number) {
|
|
102
|
-
return this.sendStateless(JSON.stringify({ action: 'version.preview', version: targetVersion }))
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
107
|
-
*/
|
|
108
|
-
getVersions(): THistoryVersion[] {
|
|
109
|
-
return this.configuration.document.getArray<THistoryVersion>(`${this.tiptapCollabConfigurationPrefix}versions`).toArray()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
watchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['observe']>[0]) {
|
|
113
|
-
return this.configuration.document.getArray<THistoryVersion>('__tiptapcollab__versions').observe(callback)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
unwatchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['unobserve']>[0]) {
|
|
117
|
-
return this.configuration.document.getArray<THistoryVersion>('__tiptapcollab__versions').unobserve(callback)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
isAutoVersioning(): boolean {
|
|
121
|
-
return !!this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).get('autoVersioning')
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
enableAutoVersioning() {
|
|
125
|
-
return this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 1)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
disableAutoVersioning() {
|
|
129
|
-
return this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Returns all users in the document as Y.Map objects
|
|
134
|
-
* @returns An array of Y.Map objects
|
|
135
|
-
*/
|
|
136
|
-
private getYThreads() {
|
|
137
|
-
return this.configuration.document.getArray<Y.Map<any>>(`${this.tiptapCollabConfigurationPrefix}threads`)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Finds all threads in the document and returns them as JSON objects
|
|
142
|
-
* @options Options to control the output of the threads (e.g. include deleted threads)
|
|
143
|
-
* @returns An array of threads as JSON objects
|
|
144
|
-
*/
|
|
145
|
-
getThreads<Data, CommentData>(options?: GetThreadsOptions): TCollabThread<Data, CommentData>[] {
|
|
146
|
-
const { types } = { ...defaultGetThreadsOptions, ...options } as GetThreadsOptions
|
|
147
|
-
|
|
148
|
-
const threads = this.getYThreads().toJSON() as TCollabThread<Data, CommentData>[]
|
|
149
|
-
|
|
150
|
-
if (types?.includes('archived') && types?.includes('unarchived')) {
|
|
151
|
-
return threads
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return threads.filter(currentThead => {
|
|
155
|
-
if (types?.includes('archived') && currentThead.deletedAt) {
|
|
156
|
-
return true
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (types?.includes('unarchived') && !currentThead.deletedAt) {
|
|
160
|
-
return true
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return false
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Find the index of a thread by its id
|
|
169
|
-
* @param id The thread id
|
|
170
|
-
* @returns The index of the thread or null if not found
|
|
171
|
-
*/
|
|
172
|
-
private getThreadIndex(id: string): number | null {
|
|
173
|
-
let index = null
|
|
174
|
-
|
|
175
|
-
let i = 0
|
|
176
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
177
|
-
for (const thread of this.getThreads({ types: ['archived', 'unarchived'] })) {
|
|
178
|
-
if (thread.id === id) {
|
|
179
|
-
index = i
|
|
180
|
-
break
|
|
181
|
-
}
|
|
182
|
-
i += 1
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return index
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Gets a single thread by its id
|
|
190
|
-
* @param id The thread id
|
|
191
|
-
* @returns The thread as a JSON object or null if not found
|
|
192
|
-
*/
|
|
193
|
-
getThread<Data, CommentData>(id: string): TCollabThread<Data, CommentData> | null {
|
|
194
|
-
const index = this.getThreadIndex(id)
|
|
195
|
-
|
|
196
|
-
if (index === null) {
|
|
197
|
-
return null
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return this.getYThreads().get(index).toJSON() as TCollabThread<Data, CommentData>
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Gets a single thread by its id as a Y.Map object
|
|
205
|
-
* @param id The thread id
|
|
206
|
-
* @returns The thread as a Y.Map object or null if not found
|
|
207
|
-
*/
|
|
208
|
-
private getYThread(id: string) {
|
|
209
|
-
const index = this.getThreadIndex(id)
|
|
210
|
-
|
|
211
|
-
if (index === null) {
|
|
212
|
-
return null
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return this.getYThreads().get(index)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Create a new thread
|
|
220
|
-
* @param data The thread data
|
|
221
|
-
* @returns The created thread
|
|
222
|
-
*/
|
|
223
|
-
createThread(data: Omit<TCollabThread, 'id' | 'createdAt' | 'updatedAt' | 'deletedAt' | 'comments' | 'deletedComments'>) {
|
|
224
|
-
let createdThread: TCollabThread = {} as TCollabThread
|
|
225
|
-
|
|
226
|
-
this.document.transact(() => {
|
|
227
|
-
const thread = new Y.Map()
|
|
228
|
-
thread.set('id', uuidv4())
|
|
229
|
-
thread.set('createdAt', (new Date()).toISOString())
|
|
230
|
-
thread.set('comments', new Y.Array())
|
|
231
|
-
thread.set('deletedComments', new Y.Array())
|
|
232
|
-
thread.set('deletedAt', null)
|
|
233
|
-
|
|
234
|
-
this.getYThreads().push([thread])
|
|
235
|
-
createdThread = this.updateThread(String(thread.get('id')), data)
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
return createdThread
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Update a specific thread
|
|
243
|
-
* @param id The thread id
|
|
244
|
-
* @param data New data for the thread
|
|
245
|
-
* @returns The updated thread or null if the thread is not found
|
|
246
|
-
*/
|
|
247
|
-
updateThread(id: TCollabThread['id'], data: Partial<Pick<TCollabThread, 'data'> & {
|
|
248
|
-
resolvedAt: TCollabThread['resolvedAt'] | null
|
|
249
|
-
}>) {
|
|
250
|
-
let updatedThread: TCollabThread = {} as TCollabThread
|
|
251
|
-
|
|
252
|
-
this.document.transact(() => {
|
|
253
|
-
const thread = this.getYThread(id)
|
|
254
|
-
|
|
255
|
-
if (thread === null) {
|
|
256
|
-
return null
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
thread.set('updatedAt', (new Date()).toISOString())
|
|
260
|
-
|
|
261
|
-
if (data.data) {
|
|
262
|
-
thread.set('data', data.data)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (data.resolvedAt || data.resolvedAt === null) {
|
|
266
|
-
thread.set('resolvedAt', data.resolvedAt)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
updatedThread = thread.toJSON() as TCollabThread
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
return updatedThread
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Handle the deletion of a thread. By default, the thread and it's comments are not deleted, but marked as deleted
|
|
277
|
-
* via the `deletedAt` property. Forceful deletion can be enabled by setting the `force` option to `true`.
|
|
278
|
-
*
|
|
279
|
-
* If you only want to delete the comments of a thread, you can set the `deleteComments` option to `true`.
|
|
280
|
-
* @param id The thread id
|
|
281
|
-
* @param options A set of options that control how the thread is deleted
|
|
282
|
-
* @returns The deleted thread or null if the thread is not found
|
|
283
|
-
*/
|
|
284
|
-
deleteThread(id: TCollabThread['id'], options?: DeleteThreadOptions) {
|
|
285
|
-
const { deleteComments, force } = { ...defaultDeleteThreadOptions, ...options }
|
|
286
|
-
|
|
287
|
-
const index = this.getThreadIndex(id)
|
|
288
|
-
|
|
289
|
-
if (index === null) {
|
|
290
|
-
return null
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (force) {
|
|
294
|
-
this.getYThreads().delete(index, 1)
|
|
295
|
-
return
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const thread = this.getYThreads().get(index)
|
|
299
|
-
|
|
300
|
-
thread.set('deletedAt', (new Date()).toISOString())
|
|
301
|
-
|
|
302
|
-
if (deleteComments) {
|
|
303
|
-
thread.set('comments', new Y.Array())
|
|
304
|
-
thread.set('deletedComments', new Y.Array())
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return thread.toJSON() as TCollabThread
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Tries to restore a deleted thread
|
|
312
|
-
* @param id The thread id
|
|
313
|
-
* @returns The restored thread or null if the thread is not found
|
|
314
|
-
*/
|
|
315
|
-
restoreThread(id: TCollabThread['id']) {
|
|
316
|
-
const index = this.getThreadIndex(id)
|
|
317
|
-
|
|
318
|
-
if (index === null) {
|
|
319
|
-
return null
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const thread = this.getYThreads().get(index)
|
|
323
|
-
|
|
324
|
-
thread.set('deletedAt', null)
|
|
325
|
-
|
|
326
|
-
return thread.toJSON() as TCollabThread
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Returns comments from a thread, either deleted or not
|
|
331
|
-
* @param threadId The thread id
|
|
332
|
-
* @param includeDeleted If you want to include deleted comments, defaults to `false`
|
|
333
|
-
* @returns The comments or null if the thread is not found
|
|
334
|
-
*/
|
|
335
|
-
getThreadComments(threadId: TCollabThread['id'], includeDeleted?: boolean): TCollabComment[] | null {
|
|
336
|
-
const index = this.getThreadIndex(threadId)
|
|
337
|
-
|
|
338
|
-
if (index === null) {
|
|
339
|
-
return null
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const comments = !includeDeleted ? this.getThread(threadId)?.comments : [...(this.getThread(threadId)?.comments || []), ...(this.getThread(threadId)?.deletedComments || [])].sort((a, b) => {
|
|
343
|
-
return a.createdAt.localeCompare(b.createdAt)
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
return comments ?? []
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Get a single comment from a specific thread
|
|
351
|
-
* @param threadId The thread id
|
|
352
|
-
* @param commentId The comment id
|
|
353
|
-
* @param includeDeleted If you want to include deleted comments in the search
|
|
354
|
-
* @returns The comment or null if not found
|
|
355
|
-
*/
|
|
356
|
-
getThreadComment(threadId: TCollabThread['id'], commentId: TCollabComment['id'], includeDeleted?: boolean): TCollabComment | null {
|
|
357
|
-
const index = this.getThreadIndex(threadId)
|
|
358
|
-
|
|
359
|
-
if (index === null) {
|
|
360
|
-
return null
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const comments = this.getThreadComments(threadId, includeDeleted)
|
|
364
|
-
|
|
365
|
-
return comments?.find(comment => comment.id === commentId) ?? null
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Adds a comment to a thread
|
|
370
|
-
* @param threadId The thread id
|
|
371
|
-
* @param data The comment data
|
|
372
|
-
* @returns The updated thread or null if the thread is not found
|
|
373
|
-
* @example addComment('123', { content: 'Hello world', data: { author: 'Maria Doe' } })
|
|
374
|
-
*/
|
|
375
|
-
addComment(threadId: TCollabThread['id'], data: Omit<TCollabComment, 'id' | 'updatedAt' | 'createdAt'>) {
|
|
376
|
-
let updatedThread: TCollabThread = {} as TCollabThread
|
|
377
|
-
|
|
378
|
-
this.document.transact(() => {
|
|
379
|
-
const thread = this.getYThread(threadId)
|
|
380
|
-
|
|
381
|
-
if (thread === null) return null
|
|
382
|
-
|
|
383
|
-
const commentMap = new Y.Map()
|
|
384
|
-
commentMap.set('id', uuidv4())
|
|
385
|
-
commentMap.set('createdAt', (new Date()).toISOString())
|
|
386
|
-
thread.get('comments').push([commentMap])
|
|
387
|
-
|
|
388
|
-
this.updateComment(threadId, String(commentMap.get('id')), data)
|
|
389
|
-
|
|
390
|
-
updatedThread = thread.toJSON() as TCollabThread
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
return updatedThread
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Update a comment in a thread
|
|
398
|
-
* @param threadId The thread id
|
|
399
|
-
* @param commentId The comment id
|
|
400
|
-
* @param data The new comment data
|
|
401
|
-
* @returns The updated thread or null if the thread or comment is not found
|
|
402
|
-
* @example updateComment('123', { content: 'The new content', data: { attachments: ['file1.jpg'] }})
|
|
403
|
-
*/
|
|
404
|
-
updateComment(threadId: TCollabThread['id'], commentId: TCollabComment['id'], data: Partial<Pick<TCollabComment, 'data' | 'content'>>) {
|
|
405
|
-
let updatedThread: TCollabThread = {} as TCollabThread
|
|
406
|
-
|
|
407
|
-
this.document.transact(() => {
|
|
408
|
-
const thread = this.getYThread(threadId)
|
|
409
|
-
|
|
410
|
-
if (thread === null) return null
|
|
411
|
-
|
|
412
|
-
let comment = null
|
|
413
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
414
|
-
for (const c of thread.get('comments')) {
|
|
415
|
-
if (c.get('id') === commentId) {
|
|
416
|
-
comment = c
|
|
417
|
-
break
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (comment === null) return null
|
|
422
|
-
|
|
423
|
-
comment.set('updatedAt', (new Date()).toISOString())
|
|
424
|
-
|
|
425
|
-
if (data.data) {
|
|
426
|
-
comment.set('data', data.data)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
if (data.content) {
|
|
430
|
-
comment.set('content', data.content)
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
updatedThread = thread.toJSON() as TCollabThread
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
return updatedThread
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Deletes a comment from a thread
|
|
441
|
-
* @param threadId The thread id
|
|
442
|
-
* @param commentId The comment id
|
|
443
|
-
* @param options A set of options that control how the comment is deleted
|
|
444
|
-
* @returns The updated thread or null if the thread or comment is not found
|
|
445
|
-
*/
|
|
446
|
-
deleteComment(threadId: TCollabThread['id'], commentId: TCollabComment['id'], options?: DeleteCommentOptions) {
|
|
447
|
-
const { deleteContent, deleteThread } = { ...defaultDeleteCommentOptions, ...options }
|
|
448
|
-
|
|
449
|
-
const thread = this.getYThread(threadId)
|
|
450
|
-
|
|
451
|
-
if (thread === null) return null
|
|
452
|
-
|
|
453
|
-
let commentIndex = 0
|
|
454
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
455
|
-
for (const c of thread.get('comments')) {
|
|
456
|
-
if (c.get('id') === commentId) {
|
|
457
|
-
break
|
|
458
|
-
}
|
|
459
|
-
commentIndex += 1
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// if the first comment of a thread is deleted we also
|
|
463
|
-
// delete the thread itself as the source comment is gone
|
|
464
|
-
if (commentIndex === 0 && (deleteThread || (this.configuration as TiptapCollabProviderConfiguration).deleteThreadOnFirstCommentDelete)) {
|
|
465
|
-
this.deleteThread(threadId)
|
|
466
|
-
return
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const comment = thread.get('comments').get(commentIndex)
|
|
470
|
-
const newComment = new Y.Map()
|
|
471
|
-
|
|
472
|
-
newComment.set('id', comment.get('id'))
|
|
473
|
-
newComment.set('createdAt', comment.get('createdAt'))
|
|
474
|
-
newComment.set('updatedAt', (new Date()).toISOString())
|
|
475
|
-
newComment.set('deletedAt', (new Date()).toISOString())
|
|
476
|
-
newComment.set('data', comment.get('data'))
|
|
477
|
-
newComment.set('content', deleteContent ? null : comment.get('content'))
|
|
478
|
-
|
|
479
|
-
if (!thread.get('deletedComments')) {
|
|
480
|
-
thread.set('deletedComments', new Y.Array())
|
|
481
|
-
}
|
|
482
|
-
thread.get('deletedComments').push([newComment])
|
|
483
|
-
|
|
484
|
-
thread.get('comments').delete(commentIndex)
|
|
485
|
-
|
|
486
|
-
return thread.toJSON() as TCollabThread
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Start watching threads for changes
|
|
491
|
-
* @param callback The callback function to be called when a thread changes
|
|
492
|
-
*/
|
|
493
|
-
watchThreads(callback: () => void) {
|
|
494
|
-
this.getYThreads().observeDeep(callback)
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Stop watching threads for changes
|
|
499
|
-
* @param callback The callback function to be removed
|
|
500
|
-
*/
|
|
501
|
-
unwatchThreads(callback: () => void) {
|
|
502
|
-
this.getYThreads().unobserveDeep(callback)
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CompleteHocuspocusProviderWebsocketConfiguration,
|
|
3
|
-
HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration,
|
|
4
|
-
} from './HocuspocusProviderWebsocket.js'
|
|
5
|
-
|
|
6
|
-
export type TiptapCollabProviderWebsocketConfiguration =
|
|
7
|
-
Partial<CompleteHocuspocusProviderWebsocketConfiguration> &
|
|
8
|
-
AdditionalTiptapCollabProviderWebsocketConfiguration
|
|
9
|
-
|
|
10
|
-
export interface AdditionalTiptapCollabProviderWebsocketConfiguration {
|
|
11
|
-
/**
|
|
12
|
-
* A Hocuspocus Cloud App ID, get one here: https://cloud.tiptap.dev
|
|
13
|
-
*/
|
|
14
|
-
appId?: string,
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* If you are using the on-premise version of TiptapCollab, put your baseUrl here (e.g. https://collab.yourdomain.com)
|
|
18
|
-
*/
|
|
19
|
-
baseUrl?: string
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Only fill this if you are using Tiptap Collab HA.
|
|
23
|
-
*/
|
|
24
|
-
shardKey?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
|
|
28
|
-
constructor(configuration: TiptapCollabProviderWebsocketConfiguration) {
|
|
29
|
-
let url = configuration.baseUrl ?? `wss://${configuration.appId}.collab.tiptap.cloud`
|
|
30
|
-
|
|
31
|
-
if (configuration.shardKey) {
|
|
32
|
-
url += url.includes('?') ? '&' : '?'
|
|
33
|
-
url += `shard=${configuration.shardKey}`
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
super({ ...configuration as HocuspocusProviderWebsocketConfiguration, url })
|
|
37
|
-
}
|
|
38
|
-
}
|