@hocuspocus/provider 2.13.7 → 2.14.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.
@@ -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.documentUpdateHandler.bind(this));
2496
- (_c = this.awareness) === null || _c === void 0 ? void 0 : _c.on('update', this.awarenessUpdateHandler.bind(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.awarenessUpdateHandler);
2689
+ this.awareness.off('update', this.boundAwarenessUpdateHandler);
2688
2690
  this.awareness.destroy();
2689
2691
  }
2690
- this.document.off('update', this.documentUpdateHandler);
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,10 @@ class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
2818
2820
  }
2819
2821
  }
2820
2822
 
2823
+ const defaultDeleteCommentOptions = {
2824
+ deleteContent: false,
2825
+ deleteThread: false,
2826
+ };
2821
2827
  class TiptapCollabProvider extends HocuspocusProvider {
2822
2828
  constructor(configuration) {
2823
2829
  if (!configuration.websocketProvider) {
@@ -2874,12 +2880,25 @@ class TiptapCollabProvider extends HocuspocusProvider {
2874
2880
  disableAutoVersioning() {
2875
2881
  return this.configuration.document.getMap(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0);
2876
2882
  }
2883
+ /**
2884
+ * Returns all users in the document as Y.Map objects
2885
+ * @returns An array of Y.Map objects
2886
+ */
2877
2887
  getYThreads() {
2878
2888
  return this.configuration.document.getArray(`${this.tiptapCollabConfigurationPrefix}threads`);
2879
2889
  }
2890
+ /**
2891
+ * Finds all threads in the document and returns them as JSON objects
2892
+ * @returns An array of threads as JSON objects
2893
+ */
2880
2894
  getThreads() {
2881
2895
  return this.getYThreads().toJSON();
2882
2896
  }
2897
+ /**
2898
+ * Find the index of a thread by its id
2899
+ * @param id The thread id
2900
+ * @returns The index of the thread or null if not found
2901
+ */
2883
2902
  getThreadIndex(id) {
2884
2903
  let index = null;
2885
2904
  let i = 0;
@@ -2893,6 +2912,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2893
2912
  }
2894
2913
  return index;
2895
2914
  }
2915
+ /**
2916
+ * Gets a single thread by its id
2917
+ * @param id The thread id
2918
+ * @returns The thread as a JSON object or null if not found
2919
+ */
2896
2920
  getThread(id) {
2897
2921
  const index = this.getThreadIndex(id);
2898
2922
  if (index === null) {
@@ -2900,6 +2924,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2900
2924
  }
2901
2925
  return this.getYThreads().get(index).toJSON();
2902
2926
  }
2927
+ /**
2928
+ * Gets a single thread by its id as a Y.Map object
2929
+ * @param id The thread id
2930
+ * @returns The thread as a Y.Map object or null if not found
2931
+ */
2903
2932
  getYThread(id) {
2904
2933
  const index = this.getThreadIndex(id);
2905
2934
  if (index === null) {
@@ -2907,6 +2936,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2907
2936
  }
2908
2937
  return this.getYThreads().get(index);
2909
2938
  }
2939
+ /**
2940
+ * Create a new thread
2941
+ * @param data The thread data
2942
+ * @returns The created thread
2943
+ */
2910
2944
  createThread(data) {
2911
2945
  let createdThread = {};
2912
2946
  this.document.transact(() => {
@@ -2914,11 +2948,18 @@ class TiptapCollabProvider extends HocuspocusProvider {
2914
2948
  thread.set('id', uuidv4());
2915
2949
  thread.set('createdAt', (new Date()).toISOString());
2916
2950
  thread.set('comments', new Y.Array());
2951
+ thread.set('deletedComments', new Y.Array());
2917
2952
  this.getYThreads().push([thread]);
2918
2953
  createdThread = this.updateThread(String(thread.get('id')), data);
2919
2954
  });
2920
2955
  return createdThread;
2921
2956
  }
2957
+ /**
2958
+ * Update a specific thread
2959
+ * @param id The thread id
2960
+ * @param data New data for the thread
2961
+ * @returns The updated thread or null if the thread is not found
2962
+ */
2922
2963
  updateThread(id, data) {
2923
2964
  let updatedThread = {};
2924
2965
  this.document.transact(() => {
@@ -2937,6 +2978,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2937
2978
  });
2938
2979
  return updatedThread;
2939
2980
  }
2981
+ /**
2982
+ * Delete a specific thread and all its comments
2983
+ * @param id The thread id
2984
+ * @returns void
2985
+ */
2940
2986
  deleteThread(id) {
2941
2987
  const index = this.getThreadIndex(id);
2942
2988
  if (index === null) {
@@ -2944,22 +2990,46 @@ class TiptapCollabProvider extends HocuspocusProvider {
2944
2990
  }
2945
2991
  this.getYThreads().delete(index, 1);
2946
2992
  }
2947
- getThreadComments(threadId) {
2948
- var _a, _b;
2993
+ /**
2994
+ * Returns comments from a thread, either deleted or not
2995
+ * @param threadId The thread id
2996
+ * @param includeDeleted If you want to include deleted comments, defaults to `false`
2997
+ * @returns The comments or null if the thread is not found
2998
+ */
2999
+ getThreadComments(threadId, includeDeleted) {
3000
+ var _a, _b, _c;
2949
3001
  const index = this.getThreadIndex(threadId);
2950
3002
  if (index === null) {
2951
3003
  return null;
2952
3004
  }
2953
- return (_b = (_a = this.getThread(threadId)) === null || _a === void 0 ? void 0 : _a.comments) !== null && _b !== void 0 ? _b : [];
3005
+ 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) => {
3006
+ return a.createdAt.localeCompare(b.createdAt);
3007
+ });
3008
+ return comments !== null && comments !== void 0 ? comments : [];
2954
3009
  }
2955
- getThreadComment(threadId, commentId) {
2956
- var _a, _b;
3010
+ /**
3011
+ * Get a single comment from a specific thread
3012
+ * @param threadId The thread id
3013
+ * @param commentId The comment id
3014
+ * @param includeDeleted If you want to include deleted comments in the search
3015
+ * @returns The comment or null if not found
3016
+ */
3017
+ getThreadComment(threadId, commentId, includeDeleted) {
3018
+ var _a;
2957
3019
  const index = this.getThreadIndex(threadId);
2958
3020
  if (index === null) {
2959
3021
  return null;
2960
3022
  }
2961
- return (_b = (_a = this.getThread(threadId)) === null || _a === void 0 ? void 0 : _a.comments.find(comment => comment.id === commentId)) !== null && _b !== void 0 ? _b : null;
3023
+ const comments = this.getThreadComments(threadId, includeDeleted);
3024
+ return (_a = comments === null || comments === void 0 ? void 0 : comments.find(comment => comment.id === commentId)) !== null && _a !== void 0 ? _a : null;
2962
3025
  }
3026
+ /**
3027
+ * Adds a comment to a thread
3028
+ * @param threadId The thread id
3029
+ * @param data The comment data
3030
+ * @returns The updated thread or null if the thread is not found
3031
+ * @example addComment('123', { content: 'Hello world', data: { author: 'Maria Doe' } })
3032
+ */
2963
3033
  addComment(threadId, data) {
2964
3034
  let updatedThread = {};
2965
3035
  this.document.transact(() => {
@@ -2975,6 +3045,14 @@ class TiptapCollabProvider extends HocuspocusProvider {
2975
3045
  });
2976
3046
  return updatedThread;
2977
3047
  }
3048
+ /**
3049
+ * Update a comment in a thread
3050
+ * @param threadId The thread id
3051
+ * @param commentId The comment id
3052
+ * @param data The new comment data
3053
+ * @returns The updated thread or null if the thread or comment is not found
3054
+ * @example updateComment('123', { content: 'The new content', data: { attachments: ['file1.jpg'] }})
3055
+ */
2978
3056
  updateComment(threadId, commentId, data) {
2979
3057
  let updatedThread = {};
2980
3058
  this.document.transact(() => {
@@ -3002,7 +3080,15 @@ class TiptapCollabProvider extends HocuspocusProvider {
3002
3080
  });
3003
3081
  return updatedThread;
3004
3082
  }
3005
- deleteComment(threadId, commentId) {
3083
+ /**
3084
+ * Deletes a comment from a thread
3085
+ * @param threadId The thread id
3086
+ * @param commentId The comment id
3087
+ * @param options A set of options that control how the comment is deleted
3088
+ * @returns The updated thread or null if the thread or comment is not found
3089
+ */
3090
+ deleteComment(threadId, commentId, options) {
3091
+ const { deleteContent, deleteThread } = { ...defaultDeleteCommentOptions, ...options };
3006
3092
  const thread = this.getYThread(threadId);
3007
3093
  if (thread === null)
3008
3094
  return null;
@@ -3016,18 +3102,33 @@ class TiptapCollabProvider extends HocuspocusProvider {
3016
3102
  }
3017
3103
  // if the first comment of a thread is deleted we also
3018
3104
  // delete the thread itself as the source comment is gone
3019
- if (commentIndex === 0) {
3105
+ if (commentIndex === 0 && (deleteThread || this.configuration.deleteThreadOnFirstCommentDelete)) {
3020
3106
  this.deleteThread(threadId);
3021
3107
  return;
3022
3108
  }
3023
- if (commentIndex > 0) {
3024
- thread.get('comments').delete(commentIndex);
3025
- }
3109
+ const comment = thread.get('comments').get(commentIndex);
3110
+ const newComment = new Y.Map();
3111
+ newComment.set('id', comment.get('id'));
3112
+ newComment.set('createdAt', comment.get('createdAt'));
3113
+ newComment.set('updatedAt', (new Date()).toISOString());
3114
+ newComment.set('deletedAt', (new Date()).toISOString());
3115
+ newComment.set('data', comment.get('data'));
3116
+ newComment.set('content', deleteContent ? null : comment.get('content'));
3117
+ thread.get('deletedComments').push([newComment]);
3118
+ thread.get('comments').delete(commentIndex);
3026
3119
  return thread.toJSON();
3027
3120
  }
3121
+ /**
3122
+ * Start watching threads for changes
3123
+ * @param callback The callback function to be called when a thread changes
3124
+ */
3028
3125
  watchThreads(callback) {
3029
3126
  this.getYThreads().observeDeep(callback);
3030
3127
  }
3128
+ /**
3129
+ * Stop watching threads for changes
3130
+ * @param callback The callback function to be removed
3131
+ */
3031
3132
  unwatchThreads(callback) {
3032
3133
  this.getYThreads().unobserveDeep(callback);
3033
3134
  }