@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
|
@@ -2451,6 +2451,8 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2451
2451
|
forceSync: null,
|
|
2452
2452
|
};
|
|
2453
2453
|
this.isConnected = true;
|
|
2454
|
+
this.boundDocumentUpdateHandler = this.documentUpdateHandler.bind(this);
|
|
2455
|
+
this.boundAwarenessUpdateHandler = this.awarenessUpdateHandler.bind(this);
|
|
2454
2456
|
this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
|
|
2455
2457
|
this.boundPageHide = this.pageHide.bind(this);
|
|
2456
2458
|
this.boundOnOpen = this.onOpen.bind(this);
|
|
@@ -2492,8 +2494,8 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2492
2494
|
(_b = this.awareness) === null || _b === void 0 ? void 0 : _b.on('change', () => {
|
|
2493
2495
|
this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) });
|
|
2494
2496
|
});
|
|
2495
|
-
this.document.on('update', this.
|
|
2496
|
-
(_c = this.awareness) === null || _c === void 0 ? void 0 : _c.on('update', this.
|
|
2497
|
+
this.document.on('update', this.boundDocumentUpdateHandler);
|
|
2498
|
+
(_c = this.awareness) === null || _c === void 0 ? void 0 : _c.on('update', this.boundAwarenessUpdateHandler);
|
|
2497
2499
|
this.registerEventListeners();
|
|
2498
2500
|
if (this.configuration.forceSyncInterval
|
|
2499
2501
|
&& typeof this.configuration.forceSyncInterval === 'number') {
|
|
@@ -2684,10 +2686,10 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2684
2686
|
}
|
|
2685
2687
|
if (this.awareness) {
|
|
2686
2688
|
removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
|
|
2687
|
-
this.awareness.off('update', this.
|
|
2689
|
+
this.awareness.off('update', this.boundAwarenessUpdateHandler);
|
|
2688
2690
|
this.awareness.destroy();
|
|
2689
2691
|
}
|
|
2690
|
-
this.document.off('update', this.
|
|
2692
|
+
this.document.off('update', this.boundDocumentUpdateHandler);
|
|
2691
2693
|
this.removeAllListeners();
|
|
2692
2694
|
this.configuration.websocketProvider.off('connect', this.configuration.onConnect);
|
|
2693
2695
|
this.configuration.websocketProvider.off('connect', this.forwardConnect);
|
|
@@ -2818,6 +2820,17 @@ class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
|
|
|
2818
2820
|
}
|
|
2819
2821
|
}
|
|
2820
2822
|
|
|
2823
|
+
const defaultDeleteCommentOptions = {
|
|
2824
|
+
deleteContent: false,
|
|
2825
|
+
deleteThread: false,
|
|
2826
|
+
};
|
|
2827
|
+
const defaultGetThreadsOptions = {
|
|
2828
|
+
types: ['unarchived'],
|
|
2829
|
+
};
|
|
2830
|
+
const defaultDeleteThreadOptions = {
|
|
2831
|
+
deleteComments: false,
|
|
2832
|
+
force: false,
|
|
2833
|
+
};
|
|
2821
2834
|
class TiptapCollabProvider extends HocuspocusProvider {
|
|
2822
2835
|
constructor(configuration) {
|
|
2823
2836
|
if (!configuration.websocketProvider) {
|
|
@@ -2874,17 +2887,44 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2874
2887
|
disableAutoVersioning() {
|
|
2875
2888
|
return this.configuration.document.getMap(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0);
|
|
2876
2889
|
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Returns all users in the document as Y.Map objects
|
|
2892
|
+
* @returns An array of Y.Map objects
|
|
2893
|
+
*/
|
|
2877
2894
|
getYThreads() {
|
|
2878
2895
|
return this.configuration.document.getArray(`${this.tiptapCollabConfigurationPrefix}threads`);
|
|
2879
2896
|
}
|
|
2880
|
-
|
|
2881
|
-
|
|
2897
|
+
/**
|
|
2898
|
+
* Finds all threads in the document and returns them as JSON objects
|
|
2899
|
+
* @options Options to control the output of the threads (e.g. include deleted threads)
|
|
2900
|
+
* @returns An array of threads as JSON objects
|
|
2901
|
+
*/
|
|
2902
|
+
getThreads(options) {
|
|
2903
|
+
const { types } = { ...defaultGetThreadsOptions, ...options };
|
|
2904
|
+
const threads = this.getYThreads().toJSON();
|
|
2905
|
+
if ((types === null || types === void 0 ? void 0 : types.includes('archived')) && (types === null || types === void 0 ? void 0 : types.includes('unarchived'))) {
|
|
2906
|
+
return threads;
|
|
2907
|
+
}
|
|
2908
|
+
return threads.filter(currentThead => {
|
|
2909
|
+
if ((types === null || types === void 0 ? void 0 : types.includes('archived')) && currentThead.deletedAt) {
|
|
2910
|
+
return true;
|
|
2911
|
+
}
|
|
2912
|
+
if ((types === null || types === void 0 ? void 0 : types.includes('unarchived')) && !currentThead.deletedAt) {
|
|
2913
|
+
return true;
|
|
2914
|
+
}
|
|
2915
|
+
return false;
|
|
2916
|
+
});
|
|
2882
2917
|
}
|
|
2918
|
+
/**
|
|
2919
|
+
* Find the index of a thread by its id
|
|
2920
|
+
* @param id The thread id
|
|
2921
|
+
* @returns The index of the thread or null if not found
|
|
2922
|
+
*/
|
|
2883
2923
|
getThreadIndex(id) {
|
|
2884
2924
|
let index = null;
|
|
2885
2925
|
let i = 0;
|
|
2886
2926
|
// eslint-disable-next-line no-restricted-syntax
|
|
2887
|
-
for (const thread of this.getThreads()) {
|
|
2927
|
+
for (const thread of this.getThreads({ types: ['archived', 'unarchived'] })) {
|
|
2888
2928
|
if (thread.id === id) {
|
|
2889
2929
|
index = i;
|
|
2890
2930
|
break;
|
|
@@ -2893,6 +2933,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2893
2933
|
}
|
|
2894
2934
|
return index;
|
|
2895
2935
|
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Gets a single thread by its id
|
|
2938
|
+
* @param id The thread id
|
|
2939
|
+
* @returns The thread as a JSON object or null if not found
|
|
2940
|
+
*/
|
|
2896
2941
|
getThread(id) {
|
|
2897
2942
|
const index = this.getThreadIndex(id);
|
|
2898
2943
|
if (index === null) {
|
|
@@ -2900,6 +2945,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2900
2945
|
}
|
|
2901
2946
|
return this.getYThreads().get(index).toJSON();
|
|
2902
2947
|
}
|
|
2948
|
+
/**
|
|
2949
|
+
* Gets a single thread by its id as a Y.Map object
|
|
2950
|
+
* @param id The thread id
|
|
2951
|
+
* @returns The thread as a Y.Map object or null if not found
|
|
2952
|
+
*/
|
|
2903
2953
|
getYThread(id) {
|
|
2904
2954
|
const index = this.getThreadIndex(id);
|
|
2905
2955
|
if (index === null) {
|
|
@@ -2907,6 +2957,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2907
2957
|
}
|
|
2908
2958
|
return this.getYThreads().get(index);
|
|
2909
2959
|
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Create a new thread
|
|
2962
|
+
* @param data The thread data
|
|
2963
|
+
* @returns The created thread
|
|
2964
|
+
*/
|
|
2910
2965
|
createThread(data) {
|
|
2911
2966
|
let createdThread = {};
|
|
2912
2967
|
this.document.transact(() => {
|
|
@@ -2914,11 +2969,19 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2914
2969
|
thread.set('id', uuidv4());
|
|
2915
2970
|
thread.set('createdAt', (new Date()).toISOString());
|
|
2916
2971
|
thread.set('comments', new Y.Array());
|
|
2972
|
+
thread.set('deletedComments', new Y.Array());
|
|
2973
|
+
thread.set('deletedAt', null);
|
|
2917
2974
|
this.getYThreads().push([thread]);
|
|
2918
2975
|
createdThread = this.updateThread(String(thread.get('id')), data);
|
|
2919
2976
|
});
|
|
2920
2977
|
return createdThread;
|
|
2921
2978
|
}
|
|
2979
|
+
/**
|
|
2980
|
+
* Update a specific thread
|
|
2981
|
+
* @param id The thread id
|
|
2982
|
+
* @param data New data for the thread
|
|
2983
|
+
* @returns The updated thread or null if the thread is not found
|
|
2984
|
+
*/
|
|
2922
2985
|
updateThread(id, data) {
|
|
2923
2986
|
let updatedThread = {};
|
|
2924
2987
|
this.document.transact(() => {
|
|
@@ -2937,29 +3000,87 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2937
3000
|
});
|
|
2938
3001
|
return updatedThread;
|
|
2939
3002
|
}
|
|
2940
|
-
|
|
3003
|
+
/**
|
|
3004
|
+
* Handle the deletion of a thread. By default, the thread and it's comments are not deleted, but marked as deleted
|
|
3005
|
+
* via the `deletedAt` property. Forceful deletion can be enabled by setting the `force` option to `true`.
|
|
3006
|
+
*
|
|
3007
|
+
* If you only want to delete the comments of a thread, you can set the `deleteComments` option to `true`.
|
|
3008
|
+
* @param id The thread id
|
|
3009
|
+
* @param options A set of options that control how the thread is deleted
|
|
3010
|
+
* @returns The deleted thread or null if the thread is not found
|
|
3011
|
+
*/
|
|
3012
|
+
deleteThread(id, options) {
|
|
3013
|
+
const { deleteComments, force } = { ...defaultDeleteThreadOptions, ...options };
|
|
2941
3014
|
const index = this.getThreadIndex(id);
|
|
2942
3015
|
if (index === null) {
|
|
3016
|
+
return null;
|
|
3017
|
+
}
|
|
3018
|
+
if (force) {
|
|
3019
|
+
this.getYThreads().delete(index, 1);
|
|
2943
3020
|
return;
|
|
2944
3021
|
}
|
|
2945
|
-
this.getYThreads().
|
|
3022
|
+
const thread = this.getYThreads().get(index);
|
|
3023
|
+
thread.set('deletedAt', (new Date()).toISOString());
|
|
3024
|
+
if (deleteComments) {
|
|
3025
|
+
thread.set('comments', new Y.Array());
|
|
3026
|
+
thread.set('deletedComments', new Y.Array());
|
|
3027
|
+
}
|
|
3028
|
+
return thread.toJSON();
|
|
3029
|
+
}
|
|
3030
|
+
/**
|
|
3031
|
+
* Tries to restore a deleted thread
|
|
3032
|
+
* @param id The thread id
|
|
3033
|
+
* @returns The restored thread or null if the thread is not found
|
|
3034
|
+
*/
|
|
3035
|
+
restoreThread(id) {
|
|
3036
|
+
const index = this.getThreadIndex(id);
|
|
3037
|
+
if (index === null) {
|
|
3038
|
+
return null;
|
|
3039
|
+
}
|
|
3040
|
+
const thread = this.getYThreads().get(index);
|
|
3041
|
+
thread.set('deletedAt', null);
|
|
3042
|
+
return thread.toJSON();
|
|
2946
3043
|
}
|
|
2947
|
-
|
|
2948
|
-
|
|
3044
|
+
/**
|
|
3045
|
+
* Returns comments from a thread, either deleted or not
|
|
3046
|
+
* @param threadId The thread id
|
|
3047
|
+
* @param includeDeleted If you want to include deleted comments, defaults to `false`
|
|
3048
|
+
* @returns The comments or null if the thread is not found
|
|
3049
|
+
*/
|
|
3050
|
+
getThreadComments(threadId, includeDeleted) {
|
|
3051
|
+
var _a, _b, _c;
|
|
2949
3052
|
const index = this.getThreadIndex(threadId);
|
|
2950
3053
|
if (index === null) {
|
|
2951
3054
|
return null;
|
|
2952
3055
|
}
|
|
2953
|
-
|
|
3056
|
+
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) => {
|
|
3057
|
+
return a.createdAt.localeCompare(b.createdAt);
|
|
3058
|
+
});
|
|
3059
|
+
return comments !== null && comments !== void 0 ? comments : [];
|
|
2954
3060
|
}
|
|
2955
|
-
|
|
2956
|
-
|
|
3061
|
+
/**
|
|
3062
|
+
* Get a single comment from a specific thread
|
|
3063
|
+
* @param threadId The thread id
|
|
3064
|
+
* @param commentId The comment id
|
|
3065
|
+
* @param includeDeleted If you want to include deleted comments in the search
|
|
3066
|
+
* @returns The comment or null if not found
|
|
3067
|
+
*/
|
|
3068
|
+
getThreadComment(threadId, commentId, includeDeleted) {
|
|
3069
|
+
var _a;
|
|
2957
3070
|
const index = this.getThreadIndex(threadId);
|
|
2958
3071
|
if (index === null) {
|
|
2959
3072
|
return null;
|
|
2960
3073
|
}
|
|
2961
|
-
|
|
3074
|
+
const comments = this.getThreadComments(threadId, includeDeleted);
|
|
3075
|
+
return (_a = comments === null || comments === void 0 ? void 0 : comments.find(comment => comment.id === commentId)) !== null && _a !== void 0 ? _a : null;
|
|
2962
3076
|
}
|
|
3077
|
+
/**
|
|
3078
|
+
* Adds a comment to a thread
|
|
3079
|
+
* @param threadId The thread id
|
|
3080
|
+
* @param data The comment data
|
|
3081
|
+
* @returns The updated thread or null if the thread is not found
|
|
3082
|
+
* @example addComment('123', { content: 'Hello world', data: { author: 'Maria Doe' } })
|
|
3083
|
+
*/
|
|
2963
3084
|
addComment(threadId, data) {
|
|
2964
3085
|
let updatedThread = {};
|
|
2965
3086
|
this.document.transact(() => {
|
|
@@ -2975,6 +3096,14 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
2975
3096
|
});
|
|
2976
3097
|
return updatedThread;
|
|
2977
3098
|
}
|
|
3099
|
+
/**
|
|
3100
|
+
* Update a comment in a thread
|
|
3101
|
+
* @param threadId The thread id
|
|
3102
|
+
* @param commentId The comment id
|
|
3103
|
+
* @param data The new comment data
|
|
3104
|
+
* @returns The updated thread or null if the thread or comment is not found
|
|
3105
|
+
* @example updateComment('123', { content: 'The new content', data: { attachments: ['file1.jpg'] }})
|
|
3106
|
+
*/
|
|
2978
3107
|
updateComment(threadId, commentId, data) {
|
|
2979
3108
|
let updatedThread = {};
|
|
2980
3109
|
this.document.transact(() => {
|
|
@@ -3002,7 +3131,15 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
3002
3131
|
});
|
|
3003
3132
|
return updatedThread;
|
|
3004
3133
|
}
|
|
3005
|
-
|
|
3134
|
+
/**
|
|
3135
|
+
* Deletes a comment from a thread
|
|
3136
|
+
* @param threadId The thread id
|
|
3137
|
+
* @param commentId The comment id
|
|
3138
|
+
* @param options A set of options that control how the comment is deleted
|
|
3139
|
+
* @returns The updated thread or null if the thread or comment is not found
|
|
3140
|
+
*/
|
|
3141
|
+
deleteComment(threadId, commentId, options) {
|
|
3142
|
+
const { deleteContent, deleteThread } = { ...defaultDeleteCommentOptions, ...options };
|
|
3006
3143
|
const thread = this.getYThread(threadId);
|
|
3007
3144
|
if (thread === null)
|
|
3008
3145
|
return null;
|
|
@@ -3016,18 +3153,33 @@ class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
3016
3153
|
}
|
|
3017
3154
|
// if the first comment of a thread is deleted we also
|
|
3018
3155
|
// delete the thread itself as the source comment is gone
|
|
3019
|
-
if (commentIndex === 0) {
|
|
3156
|
+
if (commentIndex === 0 && (deleteThread || this.configuration.deleteThreadOnFirstCommentDelete)) {
|
|
3020
3157
|
this.deleteThread(threadId);
|
|
3021
3158
|
return;
|
|
3022
3159
|
}
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3160
|
+
const comment = thread.get('comments').get(commentIndex);
|
|
3161
|
+
const newComment = new Y.Map();
|
|
3162
|
+
newComment.set('id', comment.get('id'));
|
|
3163
|
+
newComment.set('createdAt', comment.get('createdAt'));
|
|
3164
|
+
newComment.set('updatedAt', (new Date()).toISOString());
|
|
3165
|
+
newComment.set('deletedAt', (new Date()).toISOString());
|
|
3166
|
+
newComment.set('data', comment.get('data'));
|
|
3167
|
+
newComment.set('content', deleteContent ? null : comment.get('content'));
|
|
3168
|
+
thread.get('deletedComments').push([newComment]);
|
|
3169
|
+
thread.get('comments').delete(commentIndex);
|
|
3026
3170
|
return thread.toJSON();
|
|
3027
3171
|
}
|
|
3172
|
+
/**
|
|
3173
|
+
* Start watching threads for changes
|
|
3174
|
+
* @param callback The callback function to be called when a thread changes
|
|
3175
|
+
*/
|
|
3028
3176
|
watchThreads(callback) {
|
|
3029
3177
|
this.getYThreads().observeDeep(callback);
|
|
3030
3178
|
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Stop watching threads for changes
|
|
3181
|
+
* @param callback The callback function to be removed
|
|
3182
|
+
*/
|
|
3031
3183
|
unwatchThreads(callback) {
|
|
3032
3184
|
this.getYThreads().unobserveDeep(callback);
|
|
3033
3185
|
}
|