@hocuspocus/provider 2.13.7 → 2.15.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 +172 -20
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +172 -20
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +2 -0
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +106 -8
- package/dist/packages/provider/src/types.d.ts +42 -2
- package/package.json +2 -2
- package/src/HocuspocusProvider.ts +8 -4
- package/src/TiptapCollabProvider.ts +199 -18
- package/src/types.ts +48 -2
|
@@ -2475,6 +2475,8 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2475
2475
|
forceSync: null,
|
|
2476
2476
|
};
|
|
2477
2477
|
this.isConnected = true;
|
|
2478
|
+
this.boundDocumentUpdateHandler = this.documentUpdateHandler.bind(this);
|
|
2479
|
+
this.boundAwarenessUpdateHandler = this.awarenessUpdateHandler.bind(this);
|
|
2478
2480
|
this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
|
|
2479
2481
|
this.boundPageHide = this.pageHide.bind(this);
|
|
2480
2482
|
this.boundOnOpen = this.onOpen.bind(this);
|
|
@@ -2516,8 +2518,8 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2516
2518
|
(_b = this.awareness) === null || _b === void 0 ? void 0 : _b.on('change', () => {
|
|
2517
2519
|
this.emit('awarenessChange', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
|
|
2518
2520
|
});
|
|
2519
|
-
this.document.on('update', this.
|
|
2520
|
-
(_c = this.awareness) === null || _c === void 0 ? void 0 : _c.on('update', this.
|
|
2521
|
+
this.document.on('update', this.boundDocumentUpdateHandler);
|
|
2522
|
+
(_c = this.awareness) === null || _c === void 0 ? void 0 : _c.on('update', this.boundAwarenessUpdateHandler);
|
|
2521
2523
|
this.registerEventListeners();
|
|
2522
2524
|
if (this.configuration.forceSyncInterval
|
|
2523
2525
|
&& typeof this.configuration.forceSyncInterval === 'number') {
|
|
@@ -2708,10 +2710,10 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2708
2710
|
}
|
|
2709
2711
|
if (this.awareness) {
|
|
2710
2712
|
removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
|
|
2711
|
-
this.awareness.off('update', this.
|
|
2713
|
+
this.awareness.off('update', this.boundAwarenessUpdateHandler);
|
|
2712
2714
|
this.awareness.destroy();
|
|
2713
2715
|
}
|
|
2714
|
-
this.document.off('update', this.
|
|
2716
|
+
this.document.off('update', this.boundDocumentUpdateHandler);
|
|
2715
2717
|
this.removeAllListeners();
|
|
2716
2718
|
this.configuration.websocketProvider.off('connect', this.configuration.onConnect);
|
|
2717
2719
|
this.configuration.websocketProvider.off('connect', this.forwardConnect);
|
|
@@ -2842,6 +2844,17 @@ class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
|
|
|
2842
2844
|
}
|
|
2843
2845
|
}
|
|
2844
2846
|
|
|
2847
|
+
const defaultDeleteCommentOptions = {
|
|
2848
|
+
deleteContent: false,
|
|
2849
|
+
deleteThread: false,
|
|
2850
|
+
};
|
|
2851
|
+
const defaultGetThreadsOptions = {
|
|
2852
|
+
types: ['unarchived'],
|
|
2853
|
+
};
|
|
2854
|
+
const defaultDeleteThreadOptions = {
|
|
2855
|
+
deleteComments: false,
|
|
2856
|
+
force: false,
|
|
2857
|
+
};
|
|
2845
2858
|
class TiptapCollabProvider extends HocuspocusProvider {
|
|
2846
2859
|
constructor(configuration) {
|
|
2847
2860
|
if (!configuration.websocketProvider) {
|
|
@@ -2898,17 +2911,44 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2898
2911
|
disableAutoVersioning() {
|
|
2899
2912
|
return this.configuration.document.getMap(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0);
|
|
2900
2913
|
}
|
|
2914
|
+
/**
|
|
2915
|
+
* Returns all users in the document as Y.Map objects
|
|
2916
|
+
* @returns An array of Y.Map objects
|
|
2917
|
+
*/
|
|
2901
2918
|
getYThreads() {
|
|
2902
2919
|
return this.configuration.document.getArray(`${this.tiptapCollabConfigurationPrefix}threads`);
|
|
2903
2920
|
}
|
|
2904
|
-
|
|
2905
|
-
|
|
2921
|
+
/**
|
|
2922
|
+
* Finds all threads in the document and returns them as JSON objects
|
|
2923
|
+
* @options Options to control the output of the threads (e.g. include deleted threads)
|
|
2924
|
+
* @returns An array of threads as JSON objects
|
|
2925
|
+
*/
|
|
2926
|
+
getThreads(options) {
|
|
2927
|
+
const { types } = { ...defaultGetThreadsOptions, ...options };
|
|
2928
|
+
const threads = this.getYThreads().toJSON();
|
|
2929
|
+
if ((types === null || types === void 0 ? void 0 : types.includes('archived')) && (types === null || types === void 0 ? void 0 : types.includes('unarchived'))) {
|
|
2930
|
+
return threads;
|
|
2931
|
+
}
|
|
2932
|
+
return threads.filter(currentThead => {
|
|
2933
|
+
if ((types === null || types === void 0 ? void 0 : types.includes('archived')) && currentThead.deletedAt) {
|
|
2934
|
+
return true;
|
|
2935
|
+
}
|
|
2936
|
+
if ((types === null || types === void 0 ? void 0 : types.includes('unarchived')) && !currentThead.deletedAt) {
|
|
2937
|
+
return true;
|
|
2938
|
+
}
|
|
2939
|
+
return false;
|
|
2940
|
+
});
|
|
2906
2941
|
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Find the index of a thread by its id
|
|
2944
|
+
* @param id The thread id
|
|
2945
|
+
* @returns The index of the thread or null if not found
|
|
2946
|
+
*/
|
|
2907
2947
|
getThreadIndex(id) {
|
|
2908
2948
|
let index = null;
|
|
2909
2949
|
let i = 0;
|
|
2910
2950
|
// eslint-disable-next-line no-restricted-syntax
|
|
2911
|
-
for (const thread of this.getThreads()) {
|
|
2951
|
+
for (const thread of this.getThreads({ types: ['archived', 'unarchived'] })) {
|
|
2912
2952
|
if (thread.id === id) {
|
|
2913
2953
|
index = i;
|
|
2914
2954
|
break;
|
|
@@ -2917,6 +2957,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2917
2957
|
}
|
|
2918
2958
|
return index;
|
|
2919
2959
|
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Gets a single thread by its id
|
|
2962
|
+
* @param id The thread id
|
|
2963
|
+
* @returns The thread as a JSON object or null if not found
|
|
2964
|
+
*/
|
|
2920
2965
|
getThread(id) {
|
|
2921
2966
|
const index = this.getThreadIndex(id);
|
|
2922
2967
|
if (index === null) {
|
|
@@ -2924,6 +2969,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2924
2969
|
}
|
|
2925
2970
|
return this.getYThreads().get(index).toJSON();
|
|
2926
2971
|
}
|
|
2972
|
+
/**
|
|
2973
|
+
* Gets a single thread by its id as a Y.Map object
|
|
2974
|
+
* @param id The thread id
|
|
2975
|
+
* @returns The thread as a Y.Map object or null if not found
|
|
2976
|
+
*/
|
|
2927
2977
|
getYThread(id) {
|
|
2928
2978
|
const index = this.getThreadIndex(id);
|
|
2929
2979
|
if (index === null) {
|
|
@@ -2931,6 +2981,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2931
2981
|
}
|
|
2932
2982
|
return this.getYThreads().get(index);
|
|
2933
2983
|
}
|
|
2984
|
+
/**
|
|
2985
|
+
* Create a new thread
|
|
2986
|
+
* @param data The thread data
|
|
2987
|
+
* @returns The created thread
|
|
2988
|
+
*/
|
|
2934
2989
|
createThread(data) {
|
|
2935
2990
|
let createdThread = {};
|
|
2936
2991
|
this.document.transact(() => {
|
|
@@ -2938,11 +2993,19 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2938
2993
|
thread.set('id', uuidv4());
|
|
2939
2994
|
thread.set('createdAt', (new Date()).toISOString());
|
|
2940
2995
|
thread.set('comments', new Y__namespace.Array());
|
|
2996
|
+
thread.set('deletedComments', new Y__namespace.Array());
|
|
2997
|
+
thread.set('deletedAt', null);
|
|
2941
2998
|
this.getYThreads().push([thread]);
|
|
2942
2999
|
createdThread = this.updateThread(String(thread.get('id')), data);
|
|
2943
3000
|
});
|
|
2944
3001
|
return createdThread;
|
|
2945
3002
|
}
|
|
3003
|
+
/**
|
|
3004
|
+
* Update a specific thread
|
|
3005
|
+
* @param id The thread id
|
|
3006
|
+
* @param data New data for the thread
|
|
3007
|
+
* @returns The updated thread or null if the thread is not found
|
|
3008
|
+
*/
|
|
2946
3009
|
updateThread(id, data) {
|
|
2947
3010
|
let updatedThread = {};
|
|
2948
3011
|
this.document.transact(() => {
|
|
@@ -2961,29 +3024,87 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2961
3024
|
});
|
|
2962
3025
|
return updatedThread;
|
|
2963
3026
|
}
|
|
2964
|
-
|
|
3027
|
+
/**
|
|
3028
|
+
* Handle the deletion of a thread. By default, the thread and it's comments are not deleted, but marked as deleted
|
|
3029
|
+
* via the `deletedAt` property. Forceful deletion can be enabled by setting the `force` option to `true`.
|
|
3030
|
+
*
|
|
3031
|
+
* If you only want to delete the comments of a thread, you can set the `deleteComments` option to `true`.
|
|
3032
|
+
* @param id The thread id
|
|
3033
|
+
* @param options A set of options that control how the thread is deleted
|
|
3034
|
+
* @returns The deleted thread or null if the thread is not found
|
|
3035
|
+
*/
|
|
3036
|
+
deleteThread(id, options) {
|
|
3037
|
+
const { deleteComments, force } = { ...defaultDeleteThreadOptions, ...options };
|
|
2965
3038
|
const index = this.getThreadIndex(id);
|
|
2966
3039
|
if (index === null) {
|
|
3040
|
+
return null;
|
|
3041
|
+
}
|
|
3042
|
+
if (force) {
|
|
3043
|
+
this.getYThreads().delete(index, 1);
|
|
2967
3044
|
return;
|
|
2968
3045
|
}
|
|
2969
|
-
this.getYThreads().
|
|
3046
|
+
const thread = this.getYThreads().get(index);
|
|
3047
|
+
thread.set('deletedAt', (new Date()).toISOString());
|
|
3048
|
+
if (deleteComments) {
|
|
3049
|
+
thread.set('comments', new Y__namespace.Array());
|
|
3050
|
+
thread.set('deletedComments', new Y__namespace.Array());
|
|
3051
|
+
}
|
|
3052
|
+
return thread.toJSON();
|
|
3053
|
+
}
|
|
3054
|
+
/**
|
|
3055
|
+
* Tries to restore a deleted thread
|
|
3056
|
+
* @param id The thread id
|
|
3057
|
+
* @returns The restored thread or null if the thread is not found
|
|
3058
|
+
*/
|
|
3059
|
+
restoreThread(id) {
|
|
3060
|
+
const index = this.getThreadIndex(id);
|
|
3061
|
+
if (index === null) {
|
|
3062
|
+
return null;
|
|
3063
|
+
}
|
|
3064
|
+
const thread = this.getYThreads().get(index);
|
|
3065
|
+
thread.set('deletedAt', null);
|
|
3066
|
+
return thread.toJSON();
|
|
2970
3067
|
}
|
|
2971
|
-
|
|
2972
|
-
|
|
3068
|
+
/**
|
|
3069
|
+
* Returns comments from a thread, either deleted or not
|
|
3070
|
+
* @param threadId The thread id
|
|
3071
|
+
* @param includeDeleted If you want to include deleted comments, defaults to `false`
|
|
3072
|
+
* @returns The comments or null if the thread is not found
|
|
3073
|
+
*/
|
|
3074
|
+
getThreadComments(threadId, includeDeleted) {
|
|
3075
|
+
var _a, _b, _c;
|
|
2973
3076
|
const index = this.getThreadIndex(threadId);
|
|
2974
3077
|
if (index === null) {
|
|
2975
3078
|
return null;
|
|
2976
3079
|
}
|
|
2977
|
-
|
|
3080
|
+
const comments = !includeDeleted ? (_a = this.getThread(threadId)) === null || _a === void 0 ? void 0 : _a.comments : [...(((_b = this.getThread(threadId)) === null || _b === void 0 ? void 0 : _b.comments) || []), ...(((_c = this.getThread(threadId)) === null || _c === void 0 ? void 0 : _c.deletedComments) || [])].sort((a, b) => {
|
|
3081
|
+
return a.createdAt.localeCompare(b.createdAt);
|
|
3082
|
+
});
|
|
3083
|
+
return comments !== null && comments !== void 0 ? comments : [];
|
|
2978
3084
|
}
|
|
2979
|
-
|
|
2980
|
-
|
|
3085
|
+
/**
|
|
3086
|
+
* Get a single comment from a specific thread
|
|
3087
|
+
* @param threadId The thread id
|
|
3088
|
+
* @param commentId The comment id
|
|
3089
|
+
* @param includeDeleted If you want to include deleted comments in the search
|
|
3090
|
+
* @returns The comment or null if not found
|
|
3091
|
+
*/
|
|
3092
|
+
getThreadComment(threadId, commentId, includeDeleted) {
|
|
3093
|
+
var _a;
|
|
2981
3094
|
const index = this.getThreadIndex(threadId);
|
|
2982
3095
|
if (index === null) {
|
|
2983
3096
|
return null;
|
|
2984
3097
|
}
|
|
2985
|
-
|
|
3098
|
+
const comments = this.getThreadComments(threadId, includeDeleted);
|
|
3099
|
+
return (_a = comments === null || comments === void 0 ? void 0 : comments.find(comment => comment.id === commentId)) !== null && _a !== void 0 ? _a : null;
|
|
2986
3100
|
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Adds a comment to a thread
|
|
3103
|
+
* @param threadId The thread id
|
|
3104
|
+
* @param data The comment data
|
|
3105
|
+
* @returns The updated thread or null if the thread is not found
|
|
3106
|
+
* @example addComment('123', { content: 'Hello world', data: { author: 'Maria Doe' } })
|
|
3107
|
+
*/
|
|
2987
3108
|
addComment(threadId, data) {
|
|
2988
3109
|
let updatedThread = {};
|
|
2989
3110
|
this.document.transact(() => {
|
|
@@ -2999,6 +3120,14 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2999
3120
|
});
|
|
3000
3121
|
return updatedThread;
|
|
3001
3122
|
}
|
|
3123
|
+
/**
|
|
3124
|
+
* Update a comment in a thread
|
|
3125
|
+
* @param threadId The thread id
|
|
3126
|
+
* @param commentId The comment id
|
|
3127
|
+
* @param data The new comment data
|
|
3128
|
+
* @returns The updated thread or null if the thread or comment is not found
|
|
3129
|
+
* @example updateComment('123', { content: 'The new content', data: { attachments: ['file1.jpg'] }})
|
|
3130
|
+
*/
|
|
3002
3131
|
updateComment(threadId, commentId, data) {
|
|
3003
3132
|
let updatedThread = {};
|
|
3004
3133
|
this.document.transact(() => {
|
|
@@ -3026,7 +3155,15 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
3026
3155
|
});
|
|
3027
3156
|
return updatedThread;
|
|
3028
3157
|
}
|
|
3029
|
-
|
|
3158
|
+
/**
|
|
3159
|
+
* Deletes a comment from a thread
|
|
3160
|
+
* @param threadId The thread id
|
|
3161
|
+
* @param commentId The comment id
|
|
3162
|
+
* @param options A set of options that control how the comment is deleted
|
|
3163
|
+
* @returns The updated thread or null if the thread or comment is not found
|
|
3164
|
+
*/
|
|
3165
|
+
deleteComment(threadId, commentId, options) {
|
|
3166
|
+
const { deleteContent, deleteThread } = { ...defaultDeleteCommentOptions, ...options };
|
|
3030
3167
|
const thread = this.getYThread(threadId);
|
|
3031
3168
|
if (thread === null)
|
|
3032
3169
|
return null;
|
|
@@ -3040,18 +3177,33 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
3040
3177
|
}
|
|
3041
3178
|
// if the first comment of a thread is deleted we also
|
|
3042
3179
|
// delete the thread itself as the source comment is gone
|
|
3043
|
-
if (commentIndex === 0) {
|
|
3180
|
+
if (commentIndex === 0 && (deleteThread || this.configuration.deleteThreadOnFirstCommentDelete)) {
|
|
3044
3181
|
this.deleteThread(threadId);
|
|
3045
3182
|
return;
|
|
3046
3183
|
}
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3184
|
+
const comment = thread.get('comments').get(commentIndex);
|
|
3185
|
+
const newComment = new Y__namespace.Map();
|
|
3186
|
+
newComment.set('id', comment.get('id'));
|
|
3187
|
+
newComment.set('createdAt', comment.get('createdAt'));
|
|
3188
|
+
newComment.set('updatedAt', (new Date()).toISOString());
|
|
3189
|
+
newComment.set('deletedAt', (new Date()).toISOString());
|
|
3190
|
+
newComment.set('data', comment.get('data'));
|
|
3191
|
+
newComment.set('content', deleteContent ? null : comment.get('content'));
|
|
3192
|
+
thread.get('deletedComments').push([newComment]);
|
|
3193
|
+
thread.get('comments').delete(commentIndex);
|
|
3050
3194
|
return thread.toJSON();
|
|
3051
3195
|
}
|
|
3196
|
+
/**
|
|
3197
|
+
* Start watching threads for changes
|
|
3198
|
+
* @param callback The callback function to be called when a thread changes
|
|
3199
|
+
*/
|
|
3052
3200
|
watchThreads(callback) {
|
|
3053
3201
|
this.getYThreads().observeDeep(callback);
|
|
3054
3202
|
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Stop watching threads for changes
|
|
3205
|
+
* @param callback The callback function to be removed
|
|
3206
|
+
*/
|
|
3055
3207
|
unwatchThreads(callback) {
|
|
3056
3208
|
this.getYThreads().unobserveDeep(callback);
|
|
3057
3209
|
}
|