@hocuspocus/provider 2.9.1-rc.0 → 2.9.2-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.
@@ -1,7 +1,7 @@
1
1
  import type { AbstractType, YArrayEvent } from 'yjs';
2
2
  import { HocuspocusProvider, HocuspocusProviderConfiguration } from './HocuspocusProvider.js';
3
3
  import { TiptapCollabProviderWebsocket } from './TiptapCollabProviderWebsocket.js';
4
- import type { THistoryVersion } from './types.js';
4
+ import type { TCollabComment, TCollabThread, THistoryVersion } from './types.js';
5
5
  export type TiptapCollabProviderConfiguration = Required<Pick<HocuspocusProviderConfiguration, 'name'>> & Partial<HocuspocusProviderConfiguration> & (Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'websocketProvider'>> | Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'appId'>> | Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'baseUrl'>>);
6
6
  export interface AdditionalTiptapCollabProviderConfiguration {
7
7
  /**
@@ -40,4 +40,19 @@ export declare class TiptapCollabProvider extends HocuspocusProvider {
40
40
  isAutoVersioning(): boolean;
41
41
  enableAutoVersioning(): 1;
42
42
  disableAutoVersioning(): 0;
43
+ private getYThreads;
44
+ getThreads<Data, CommentData>(): TCollabThread<Data, CommentData>[];
45
+ private getThreadIndex;
46
+ getThread<Data, CommentData>(id: string): TCollabThread<Data, CommentData> | null;
47
+ private getYThread;
48
+ createThread(data: TCollabThread): TCollabThread | null;
49
+ updateThread(id: TCollabThread['id'], data: Pick<TCollabThread, 'data'>): TCollabThread | null;
50
+ deleteThread(id: TCollabThread['id']): void;
51
+ getThreadComments(threadId: TCollabThread['id']): TCollabComment[] | null;
52
+ getThreadComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']): TCollabComment | null;
53
+ addComment(threadId: TCollabThread['id'], data: TCollabComment): TCollabThread | null;
54
+ updateComment(threadId: TCollabThread['id'], commentId: TCollabComment['id'], data: TCollabComment): TCollabThread | null;
55
+ deleteComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']): TCollabThread | null;
56
+ watchThreads(callback: () => void): void;
57
+ unwatchThreads(callback: () => void): void;
43
58
  }
@@ -84,6 +84,20 @@ export type StatesArray = {
84
84
  clientId: number;
85
85
  [key: string | number]: any;
86
86
  }[];
87
+ export type TCollabThread<Data = any, CommentData = any> = {
88
+ id: string;
89
+ createdAt: number;
90
+ updatedAt: number;
91
+ comments: TCollabComment<CommentData>[];
92
+ data: Data;
93
+ };
94
+ export type TCollabComment<Data = any> = {
95
+ id: string;
96
+ createdAt: number;
97
+ updatedAt: number;
98
+ data: Data;
99
+ content: any;
100
+ };
87
101
  export type THistoryVersion = {
88
102
  name?: string;
89
103
  version: number;
@@ -1,2 +1,2 @@
1
- declare const _default: import("vite").UserConfigExport;
1
+ declare const _default: import("vite").UserConfig;
2
2
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hocuspocus/provider",
3
- "version": "2.9.1-rc.0",
3
+ "version": "2.9.2-rc.0",
4
4
  "description": "hocuspocus provider",
5
5
  "homepage": "https://hocuspocus.dev",
6
6
  "keywords": [
@@ -29,9 +29,10 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@hocuspocus/common": "^2.9.1-rc.0",
32
+ "@hocuspocus/common": "^2.9.2-rc.0",
33
33
  "@lifeomic/attempt": "^3.0.2",
34
34
  "lib0": "^0.2.87",
35
+ "uuid": "^9.0.0",
35
36
  "ws": "^8.14.2"
36
37
  },
37
38
  "peerDependencies": {
@@ -1,11 +1,15 @@
1
1
  import type { AbstractType, YArrayEvent } from 'yjs'
2
+ import * as Y from 'yjs'
3
+ import { uuidv4 } from 'lib0/random'
2
4
  import {
3
5
  HocuspocusProvider,
4
6
  HocuspocusProviderConfiguration,
5
7
  } from './HocuspocusProvider.js'
6
8
 
7
9
  import { TiptapCollabProviderWebsocket } from './TiptapCollabProviderWebsocket.js'
8
- import type { THistoryVersion } from './types.js'
10
+ import type {
11
+ TCollabComment, TCollabThread, THistoryVersion,
12
+ } from './types.js'
9
13
 
10
14
  export type TiptapCollabProviderConfiguration =
11
15
  Required<Pick<HocuspocusProviderConfiguration, 'name'>> &
@@ -93,4 +97,168 @@ export class TiptapCollabProvider extends HocuspocusProvider {
93
97
  return this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0)
94
98
  }
95
99
 
100
+ private getYThreads() {
101
+ return this.configuration.document.getArray<Y.Map<any>>(`${this.tiptapCollabConfigurationPrefix}threads`)
102
+ }
103
+
104
+ getThreads<Data, CommentData>(): TCollabThread<Data, CommentData>[] {
105
+ return this.getYThreads().toJSON() as TCollabThread<Data, CommentData>[]
106
+ }
107
+
108
+ private getThreadIndex(id: string): number | null {
109
+ let index = null
110
+
111
+ let i = 0
112
+ // eslint-disable-next-line no-restricted-syntax
113
+ for (const thread of this.getThreads()) {
114
+ if (thread.id === id) {
115
+ index = i
116
+ break
117
+ }
118
+ i += 1
119
+ }
120
+
121
+ return index
122
+ }
123
+
124
+ getThread<Data, CommentData>(id: string): TCollabThread<Data, CommentData> | null {
125
+ const index = this.getThreadIndex(id)
126
+
127
+ if (index === null) {
128
+ return null
129
+ }
130
+
131
+ return this.getYThreads().get(index).toJSON() as TCollabThread<Data, CommentData>
132
+ }
133
+
134
+ private getYThread(id: string) {
135
+ const index = this.getThreadIndex(id)
136
+
137
+ if (index === null) {
138
+ return null
139
+ }
140
+
141
+ return this.getYThreads().get(index)
142
+ }
143
+
144
+ createThread(data: TCollabThread) {
145
+ const thread = new Y.Map()
146
+ thread.set('id', uuidv4())
147
+ thread.set('createdAt', (new Date()).toISOString())
148
+ thread.set('comments', new Y.Array())
149
+
150
+ this.getYThreads().push([thread])
151
+ return this.updateThread(String(thread.get('id')), data)
152
+ }
153
+
154
+ updateThread(id: TCollabThread['id'], data: Pick<TCollabThread, 'data'>) {
155
+ const thread = this.getYThread(id)
156
+
157
+ if (thread === null) {
158
+ return null
159
+ }
160
+
161
+ thread.set('updatedAt', (new Date()).toISOString())
162
+ thread.set('data', data.data)
163
+
164
+ return thread.toJSON() as TCollabThread
165
+ }
166
+
167
+ deleteThread(id: TCollabThread['id']) {
168
+ const index = this.getThreadIndex(id)
169
+
170
+ if (index === null) {
171
+ return
172
+ }
173
+
174
+ this.getYThreads().delete(index, 1)
175
+ }
176
+
177
+ getThreadComments(threadId: TCollabThread['id']): TCollabComment[] | null {
178
+ const index = this.getThreadIndex(threadId)
179
+
180
+ if (index === null) {
181
+ return null
182
+ }
183
+
184
+ return this.getThread(threadId)?.comments ?? []
185
+ }
186
+
187
+ getThreadComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']): TCollabComment | null {
188
+ const index = this.getThreadIndex(threadId)
189
+
190
+ if (index === null) {
191
+ return null
192
+ }
193
+
194
+ return this.getThread(threadId)?.comments.find(comment => comment.id === commentId) ?? null
195
+ }
196
+
197
+ addComment(threadId: TCollabThread['id'], data: TCollabComment) {
198
+ const thread = this.getYThread(threadId)
199
+
200
+ if (thread === null) return null
201
+
202
+ const commentMap = new Y.Map()
203
+ commentMap.set('id', uuidv4())
204
+ commentMap.set('createdAt', (new Date()).toISOString())
205
+ thread.get('comments').push([commentMap])
206
+
207
+ this.updateComment(threadId, String(commentMap.get('id')), data)
208
+
209
+ return thread.toJSON() as TCollabThread
210
+ }
211
+
212
+ updateComment(threadId: TCollabThread['id'], commentId: TCollabComment['id'], data: TCollabComment) {
213
+ const thread = this.getYThread(threadId)
214
+
215
+ if (thread === null) return null
216
+
217
+ let comment = null
218
+ // eslint-disable-next-line no-restricted-syntax
219
+ for (const c of thread.get('comments')) {
220
+ if (c.get('id') === commentId) {
221
+ comment = c
222
+ break
223
+ }
224
+ }
225
+
226
+ if (comment === null) return null
227
+
228
+ comment.set('updatedAt', (new Date()).toISOString())
229
+ comment.set('data', data.data)
230
+ comment.set('content', data.content)
231
+
232
+ return thread.toJSON() as TCollabThread
233
+ }
234
+
235
+ deleteComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']) {
236
+ const thread = this.getYThread(threadId)
237
+
238
+ if (thread === null) return null
239
+
240
+ let commentIndex = 0
241
+ // eslint-disable-next-line no-restricted-syntax
242
+ for (const c of thread.get('comments')) {
243
+ if (c.get('id') === commentId) {
244
+ break
245
+ }
246
+ commentIndex += 1
247
+ }
248
+
249
+ if (commentIndex >= 0) {
250
+ thread.get('comments').delete(commentIndex)
251
+ }
252
+
253
+ return thread.toJSON() as TCollabThread
254
+ }
255
+
256
+ watchThreads(callback: () => void) {
257
+ this.getYThreads().observeDeep(callback)
258
+ }
259
+
260
+ unwatchThreads(callback: () => void) {
261
+ this.getYThreads().unobserveDeep(callback)
262
+ }
263
+
96
264
  }
package/src/types.ts CHANGED
@@ -106,6 +106,22 @@ export type StatesArray = { clientId: number, [key: string | number]: any }[]
106
106
 
107
107
  // hocuspocus-pro types
108
108
 
109
+ export type TCollabThread<Data = any, CommentData = any> = {
110
+ id: string;
111
+ createdAt: number;
112
+ updatedAt: number;
113
+ comments: TCollabComment<CommentData>[];
114
+ data: Data
115
+ }
116
+
117
+ export type TCollabComment<Data = any> = {
118
+ id: string;
119
+ createdAt: number;
120
+ updatedAt: number;
121
+ data: Data
122
+ content: any
123
+ }
124
+
109
125
  export type THistoryVersion = {
110
126
  name?: string;
111
127
  version: number;