@hocuspocus/provider 2.13.6 → 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.
@@ -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.documentUpdateHandler.bind(this));
2520
- (_c = this.awareness) === null || _c === void 0 ? void 0 : _c.on('update', this.awarenessUpdateHandler.bind(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.awarenessUpdateHandler);
2713
+ this.awareness.off('update', this.boundAwarenessUpdateHandler);
2712
2714
  this.awareness.destroy();
2713
2715
  }
2714
- this.document.off('update', this.documentUpdateHandler);
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,10 @@ class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
2842
2844
  }
2843
2845
  }
2844
2846
 
2847
+ const defaultDeleteCommentOptions = {
2848
+ deleteContent: false,
2849
+ deleteThread: false,
2850
+ };
2845
2851
  class TiptapCollabProvider extends HocuspocusProvider {
2846
2852
  constructor(configuration) {
2847
2853
  if (!configuration.websocketProvider) {
@@ -2898,12 +2904,25 @@ class TiptapCollabProvider extends HocuspocusProvider {
2898
2904
  disableAutoVersioning() {
2899
2905
  return this.configuration.document.getMap(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0);
2900
2906
  }
2907
+ /**
2908
+ * Returns all users in the document as Y.Map objects
2909
+ * @returns An array of Y.Map objects
2910
+ */
2901
2911
  getYThreads() {
2902
2912
  return this.configuration.document.getArray(`${this.tiptapCollabConfigurationPrefix}threads`);
2903
2913
  }
2914
+ /**
2915
+ * Finds all threads in the document and returns them as JSON objects
2916
+ * @returns An array of threads as JSON objects
2917
+ */
2904
2918
  getThreads() {
2905
2919
  return this.getYThreads().toJSON();
2906
2920
  }
2921
+ /**
2922
+ * Find the index of a thread by its id
2923
+ * @param id The thread id
2924
+ * @returns The index of the thread or null if not found
2925
+ */
2907
2926
  getThreadIndex(id) {
2908
2927
  let index = null;
2909
2928
  let i = 0;
@@ -2917,6 +2936,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2917
2936
  }
2918
2937
  return index;
2919
2938
  }
2939
+ /**
2940
+ * Gets a single thread by its id
2941
+ * @param id The thread id
2942
+ * @returns The thread as a JSON object or null if not found
2943
+ */
2920
2944
  getThread(id) {
2921
2945
  const index = this.getThreadIndex(id);
2922
2946
  if (index === null) {
@@ -2924,6 +2948,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2924
2948
  }
2925
2949
  return this.getYThreads().get(index).toJSON();
2926
2950
  }
2951
+ /**
2952
+ * Gets a single thread by its id as a Y.Map object
2953
+ * @param id The thread id
2954
+ * @returns The thread as a Y.Map object or null if not found
2955
+ */
2927
2956
  getYThread(id) {
2928
2957
  const index = this.getThreadIndex(id);
2929
2958
  if (index === null) {
@@ -2931,6 +2960,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2931
2960
  }
2932
2961
  return this.getYThreads().get(index);
2933
2962
  }
2963
+ /**
2964
+ * Create a new thread
2965
+ * @param data The thread data
2966
+ * @returns The created thread
2967
+ */
2934
2968
  createThread(data) {
2935
2969
  let createdThread = {};
2936
2970
  this.document.transact(() => {
@@ -2938,11 +2972,18 @@ class TiptapCollabProvider extends HocuspocusProvider {
2938
2972
  thread.set('id', uuidv4());
2939
2973
  thread.set('createdAt', (new Date()).toISOString());
2940
2974
  thread.set('comments', new Y__namespace.Array());
2975
+ thread.set('deletedComments', new Y__namespace.Array());
2941
2976
  this.getYThreads().push([thread]);
2942
2977
  createdThread = this.updateThread(String(thread.get('id')), data);
2943
2978
  });
2944
2979
  return createdThread;
2945
2980
  }
2981
+ /**
2982
+ * Update a specific thread
2983
+ * @param id The thread id
2984
+ * @param data New data for the thread
2985
+ * @returns The updated thread or null if the thread is not found
2986
+ */
2946
2987
  updateThread(id, data) {
2947
2988
  let updatedThread = {};
2948
2989
  this.document.transact(() => {
@@ -2961,6 +3002,11 @@ class TiptapCollabProvider extends HocuspocusProvider {
2961
3002
  });
2962
3003
  return updatedThread;
2963
3004
  }
3005
+ /**
3006
+ * Delete a specific thread and all its comments
3007
+ * @param id The thread id
3008
+ * @returns void
3009
+ */
2964
3010
  deleteThread(id) {
2965
3011
  const index = this.getThreadIndex(id);
2966
3012
  if (index === null) {
@@ -2968,22 +3014,46 @@ class TiptapCollabProvider extends HocuspocusProvider {
2968
3014
  }
2969
3015
  this.getYThreads().delete(index, 1);
2970
3016
  }
2971
- getThreadComments(threadId) {
2972
- var _a, _b;
3017
+ /**
3018
+ * Returns comments from a thread, either deleted or not
3019
+ * @param threadId The thread id
3020
+ * @param includeDeleted If you want to include deleted comments, defaults to `false`
3021
+ * @returns The comments or null if the thread is not found
3022
+ */
3023
+ getThreadComments(threadId, includeDeleted) {
3024
+ var _a, _b, _c;
2973
3025
  const index = this.getThreadIndex(threadId);
2974
3026
  if (index === null) {
2975
3027
  return null;
2976
3028
  }
2977
- return (_b = (_a = this.getThread(threadId)) === null || _a === void 0 ? void 0 : _a.comments) !== null && _b !== void 0 ? _b : [];
3029
+ 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) => {
3030
+ return a.createdAt.localeCompare(b.createdAt);
3031
+ });
3032
+ return comments !== null && comments !== void 0 ? comments : [];
2978
3033
  }
2979
- getThreadComment(threadId, commentId) {
2980
- var _a, _b;
3034
+ /**
3035
+ * Get a single comment from a specific thread
3036
+ * @param threadId The thread id
3037
+ * @param commentId The comment id
3038
+ * @param includeDeleted If you want to include deleted comments in the search
3039
+ * @returns The comment or null if not found
3040
+ */
3041
+ getThreadComment(threadId, commentId, includeDeleted) {
3042
+ var _a;
2981
3043
  const index = this.getThreadIndex(threadId);
2982
3044
  if (index === null) {
2983
3045
  return null;
2984
3046
  }
2985
- 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;
3047
+ const comments = this.getThreadComments(threadId, includeDeleted);
3048
+ return (_a = comments === null || comments === void 0 ? void 0 : comments.find(comment => comment.id === commentId)) !== null && _a !== void 0 ? _a : null;
2986
3049
  }
3050
+ /**
3051
+ * Adds a comment to a thread
3052
+ * @param threadId The thread id
3053
+ * @param data The comment data
3054
+ * @returns The updated thread or null if the thread is not found
3055
+ * @example addComment('123', { content: 'Hello world', data: { author: 'Maria Doe' } })
3056
+ */
2987
3057
  addComment(threadId, data) {
2988
3058
  let updatedThread = {};
2989
3059
  this.document.transact(() => {
@@ -2999,6 +3069,14 @@ class TiptapCollabProvider extends HocuspocusProvider {
2999
3069
  });
3000
3070
  return updatedThread;
3001
3071
  }
3072
+ /**
3073
+ * Update a comment in a thread
3074
+ * @param threadId The thread id
3075
+ * @param commentId The comment id
3076
+ * @param data The new comment data
3077
+ * @returns The updated thread or null if the thread or comment is not found
3078
+ * @example updateComment('123', { content: 'The new content', data: { attachments: ['file1.jpg'] }})
3079
+ */
3002
3080
  updateComment(threadId, commentId, data) {
3003
3081
  let updatedThread = {};
3004
3082
  this.document.transact(() => {
@@ -3026,7 +3104,15 @@ class TiptapCollabProvider extends HocuspocusProvider {
3026
3104
  });
3027
3105
  return updatedThread;
3028
3106
  }
3029
- deleteComment(threadId, commentId) {
3107
+ /**
3108
+ * Deletes a comment from a thread
3109
+ * @param threadId The thread id
3110
+ * @param commentId The comment id
3111
+ * @param options A set of options that control how the comment is deleted
3112
+ * @returns The updated thread or null if the thread or comment is not found
3113
+ */
3114
+ deleteComment(threadId, commentId, options) {
3115
+ const { deleteContent, deleteThread } = { ...defaultDeleteCommentOptions, ...options };
3030
3116
  const thread = this.getYThread(threadId);
3031
3117
  if (thread === null)
3032
3118
  return null;
@@ -3040,18 +3126,33 @@ class TiptapCollabProvider extends HocuspocusProvider {
3040
3126
  }
3041
3127
  // if the first comment of a thread is deleted we also
3042
3128
  // delete the thread itself as the source comment is gone
3043
- if (commentIndex === 0) {
3129
+ if (commentIndex === 0 && (deleteThread || this.configuration.deleteThreadOnFirstCommentDelete)) {
3044
3130
  this.deleteThread(threadId);
3045
3131
  return;
3046
3132
  }
3047
- if (commentIndex > 0) {
3048
- thread.get('comments').delete(commentIndex);
3049
- }
3133
+ const comment = thread.get('comments').get(commentIndex);
3134
+ const newComment = new Y__namespace.Map();
3135
+ newComment.set('id', comment.get('id'));
3136
+ newComment.set('createdAt', comment.get('createdAt'));
3137
+ newComment.set('updatedAt', (new Date()).toISOString());
3138
+ newComment.set('deletedAt', (new Date()).toISOString());
3139
+ newComment.set('data', comment.get('data'));
3140
+ newComment.set('content', deleteContent ? null : comment.get('content'));
3141
+ thread.get('deletedComments').push([newComment]);
3142
+ thread.get('comments').delete(commentIndex);
3050
3143
  return thread.toJSON();
3051
3144
  }
3145
+ /**
3146
+ * Start watching threads for changes
3147
+ * @param callback The callback function to be called when a thread changes
3148
+ */
3052
3149
  watchThreads(callback) {
3053
3150
  this.getYThreads().observeDeep(callback);
3054
3151
  }
3152
+ /**
3153
+ * Stop watching threads for changes
3154
+ * @param callback The callback function to be removed
3155
+ */
3055
3156
  unwatchThreads(callback) {
3056
3157
  this.getYThreads().unobserveDeep(callback);
3057
3158
  }