@tldraw/commenting 0.0.0-bootstrap → 5.3.0-canary.04044ed9e96d
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/README.md +0 -2
- package/commenting.css +1152 -350
- package/dist-cjs/canvas/anchor-lifecycle.js +148 -0
- package/dist-cjs/canvas/anchor-lifecycle.js.map +7 -0
- package/dist-cjs/{ui/avatar.js → canvas/canvas-events.js} +14 -22
- package/dist-cjs/canvas/canvas-events.js.map +7 -0
- package/dist-cjs/canvas/cluster-badge.js +121 -0
- package/dist-cjs/canvas/cluster-badge.js.map +7 -0
- package/dist-cjs/canvas/cluster-fade.js +85 -0
- package/dist-cjs/canvas/cluster-fade.js.map +7 -0
- package/dist-cjs/canvas/cluster-input.js +53 -3
- package/dist-cjs/canvas/cluster-input.js.map +2 -2
- package/dist-cjs/canvas/cluster-model.js +252 -0
- package/dist-cjs/canvas/cluster-model.js.map +7 -0
- package/dist-cjs/canvas/comment-body.js +1 -1
- package/dist-cjs/canvas/comment-body.js.map +2 -2
- package/dist-cjs/canvas/comment-drafts.js +54 -0
- package/dist-cjs/canvas/comment-drafts.js.map +7 -0
- package/dist-cjs/canvas/comment-mutations.js +152 -0
- package/dist-cjs/canvas/comment-mutations.js.map +7 -0
- package/dist-cjs/canvas/comment-reactions.js +160 -0
- package/dist-cjs/canvas/comment-reactions.js.map +7 -0
- package/dist-cjs/canvas/comment-render.js +9 -6
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-store.js +19 -9
- package/dist-cjs/canvas/comment-store.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +63 -14
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +17 -37
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +25 -22
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overlay.js +151 -989
- package/dist-cjs/canvas/comments-overlay.js.map +3 -3
- package/dist-cjs/canvas/comments-sidebar.js +49 -41
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +45 -0
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
- package/dist-cjs/canvas/context.js +17 -0
- package/dist-cjs/canvas/context.js.map +7 -0
- package/dist-cjs/canvas/hooks.js +28 -3
- package/dist-cjs/canvas/hooks.js.map +2 -2
- package/dist-cjs/canvas/license.js.map +1 -1
- package/dist-cjs/canvas/mobile-placement.js +91 -0
- package/dist-cjs/canvas/mobile-placement.js.map +7 -0
- package/dist-cjs/canvas/options.js +57 -2
- package/dist-cjs/canvas/options.js.map +2 -2
- package/dist-cjs/canvas/pending-composer.js +134 -0
- package/dist-cjs/canvas/pending-composer.js.map +7 -0
- package/dist-cjs/canvas/pin-stacking.js +81 -0
- package/dist-cjs/canvas/pin-stacking.js.map +7 -0
- package/dist-cjs/canvas/region-box.js +113 -0
- package/dist-cjs/canvas/region-box.js.map +7 -0
- package/dist-cjs/canvas/sidebar-filters.js +2 -2
- package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
- package/dist-cjs/canvas/state.js +28 -14
- package/dist-cjs/canvas/state.js.map +2 -2
- package/dist-cjs/canvas/thread-pin.js +304 -0
- package/dist-cjs/canvas/thread-pin.js.map +7 -0
- package/dist-cjs/canvas/thread-preview.js +177 -0
- package/dist-cjs/canvas/thread-preview.js.map +7 -0
- package/dist-cjs/canvas/thread-stack.js +199 -0
- package/dist-cjs/canvas/thread-stack.js.map +7 -0
- package/dist-cjs/canvas/thread-state.js +82 -21
- package/dist-cjs/canvas/thread-state.js.map +2 -2
- package/dist-cjs/canvas/thread-view.js +450 -0
- package/dist-cjs/canvas/thread-view.js.map +7 -0
- package/dist-cjs/clustering/computeClusterTable.js +6 -6
- package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
- package/dist-cjs/clustering/replay.js +89 -22
- package/dist-cjs/clustering/replay.js.map +3 -3
- package/dist-cjs/clustering/runtime.js +126 -7
- package/dist-cjs/clustering/runtime.js.map +2 -2
- package/dist-cjs/clustering/types.js.map +1 -1
- package/dist-cjs/index.d.ts +904 -353
- package/dist-cjs/index.js +52 -28
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/byline.js +10 -6
- package/dist-cjs/ui/byline.js.map +2 -2
- package/dist-cjs/ui/comment-card.js +15 -8
- package/dist-cjs/ui/comment-card.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +124 -39
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/comment-pin.js +11 -2
- package/dist-cjs/ui/comment-pin.js.map +2 -2
- package/dist-cjs/ui/comment-thread.js +9 -7
- package/dist-cjs/ui/comment-thread.js.map +2 -2
- package/dist-cjs/ui/comments-list.js +39 -40
- package/dist-cjs/ui/comments-list.js.map +2 -2
- package/dist-cjs/ui/count-badge.js +3 -2
- package/dist-cjs/ui/count-badge.js.map +2 -2
- package/dist-cjs/ui/emoji-picker.js +54 -0
- package/dist-cjs/ui/emoji-picker.js.map +7 -0
- package/dist-cjs/ui/empty-state.js +3 -3
- package/dist-cjs/ui/empty-state.js.map +2 -2
- package/dist-cjs/ui/format-time.js +16 -1
- package/dist-cjs/ui/format-time.js.map +2 -2
- package/dist-cjs/ui/icons.js +115 -0
- package/dist-cjs/ui/icons.js.map +7 -0
- package/dist-cjs/ui/reaction-picker.js +71 -0
- package/dist-cjs/ui/reaction-picker.js.map +7 -0
- package/dist-cjs/ui/reaction.js +58 -6
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/reactions.js +20 -7
- package/dist-cjs/ui/reactions.js.map +2 -2
- package/dist-cjs/ui/{mention.js → reply-count.js} +9 -11
- package/dist-cjs/ui/reply-count.js.map +7 -0
- package/dist-cjs/ui/send-button.js +13 -1
- package/dist-cjs/ui/send-button.js.map +2 -2
- package/dist-cjs/ui/{comment-text.js → visual-viewport.js} +12 -9
- package/dist-cjs/ui/visual-viewport.js.map +7 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs +128 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs.map +7 -0
- package/dist-esm/canvas/canvas-events.mjs +15 -0
- package/dist-esm/canvas/canvas-events.mjs.map +7 -0
- package/dist-esm/canvas/cluster-badge.mjs +106 -0
- package/dist-esm/canvas/cluster-badge.mjs.map +7 -0
- package/dist-esm/canvas/cluster-fade.mjs +65 -0
- package/dist-esm/canvas/cluster-fade.mjs.map +7 -0
- package/dist-esm/canvas/cluster-input.mjs +54 -4
- package/dist-esm/canvas/cluster-input.mjs.map +2 -2
- package/dist-esm/canvas/cluster-model.mjs +236 -0
- package/dist-esm/canvas/cluster-model.mjs.map +7 -0
- package/dist-esm/canvas/comment-body.mjs +1 -1
- package/dist-esm/canvas/comment-body.mjs.map +2 -2
- package/dist-esm/canvas/comment-drafts.mjs +34 -0
- package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
- package/dist-esm/canvas/comment-mutations.mjs +132 -0
- package/dist-esm/canvas/comment-mutations.mjs.map +7 -0
- package/dist-esm/canvas/comment-reactions.mjs +145 -0
- package/dist-esm/canvas/comment-reactions.mjs.map +7 -0
- package/dist-esm/canvas/comment-render.mjs +10 -7
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-store.mjs +19 -9
- package/dist-esm/canvas/comment-store.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +66 -17
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +17 -36
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +27 -22
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overlay.mjs +157 -1010
- package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
- package/dist-esm/canvas/comments-sidebar.mjs +53 -46
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +25 -0
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
- package/dist-esm/canvas/context.mjs +1 -0
- package/dist-esm/canvas/context.mjs.map +7 -0
- package/dist-esm/canvas/hooks.mjs +35 -5
- package/dist-esm/canvas/hooks.mjs.map +2 -2
- package/dist-esm/canvas/license.mjs.map +1 -1
- package/dist-esm/canvas/mobile-placement.mjs +71 -0
- package/dist-esm/canvas/mobile-placement.mjs.map +7 -0
- package/dist-esm/canvas/options.mjs +59 -3
- package/dist-esm/canvas/options.mjs.map +2 -2
- package/dist-esm/canvas/pending-composer.mjs +126 -0
- package/dist-esm/canvas/pending-composer.mjs.map +7 -0
- package/dist-esm/canvas/pin-stacking.mjs +61 -0
- package/dist-esm/canvas/pin-stacking.mjs.map +7 -0
- package/dist-esm/canvas/region-box.mjs +93 -0
- package/dist-esm/canvas/region-box.mjs.map +7 -0
- package/dist-esm/canvas/sidebar-filters.mjs +2 -2
- package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
- package/dist-esm/canvas/state.mjs +29 -19
- package/dist-esm/canvas/state.mjs.map +2 -2
- package/dist-esm/canvas/thread-pin.mjs +310 -0
- package/dist-esm/canvas/thread-pin.mjs.map +7 -0
- package/dist-esm/canvas/thread-preview.mjs +163 -0
- package/dist-esm/canvas/thread-preview.mjs.map +7 -0
- package/dist-esm/canvas/thread-stack.mjs +189 -0
- package/dist-esm/canvas/thread-stack.mjs.map +7 -0
- package/dist-esm/canvas/thread-state.mjs +86 -22
- package/dist-esm/canvas/thread-state.mjs.map +2 -2
- package/dist-esm/canvas/thread-view.mjs +461 -0
- package/dist-esm/canvas/thread-view.mjs.map +7 -0
- package/dist-esm/clustering/computeClusterTable.mjs +6 -6
- package/dist-esm/clustering/computeClusterTable.mjs.map +2 -2
- package/dist-esm/clustering/replay.mjs +89 -22
- package/dist-esm/clustering/replay.mjs.map +3 -3
- package/dist-esm/clustering/runtime.mjs +126 -7
- package/dist-esm/clustering/runtime.mjs.map +2 -2
- package/dist-esm/index.d.mts +904 -353
- package/dist-esm/index.mjs +102 -43
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/byline.mjs +11 -7
- package/dist-esm/ui/byline.mjs.map +2 -2
- package/dist-esm/ui/comment-card.mjs +15 -8
- package/dist-esm/ui/comment-card.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +135 -39
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/comment-pin.mjs +11 -2
- package/dist-esm/ui/comment-pin.mjs.map +2 -2
- package/dist-esm/ui/comment-thread.mjs +9 -7
- package/dist-esm/ui/comment-thread.mjs.map +2 -2
- package/dist-esm/ui/comments-list.mjs +51 -50
- package/dist-esm/ui/comments-list.mjs.map +2 -2
- package/dist-esm/ui/count-badge.mjs +3 -2
- package/dist-esm/ui/count-badge.mjs.map +2 -2
- package/dist-esm/ui/emoji-picker.mjs +34 -0
- package/dist-esm/ui/emoji-picker.mjs.map +7 -0
- package/dist-esm/ui/empty-state.mjs +3 -3
- package/dist-esm/ui/empty-state.mjs.map +2 -2
- package/dist-esm/ui/format-time.mjs +16 -1
- package/dist-esm/ui/format-time.mjs.map +2 -2
- package/dist-esm/ui/icons.mjs +95 -0
- package/dist-esm/ui/icons.mjs.map +7 -0
- package/dist-esm/ui/reaction-picker.mjs +59 -0
- package/dist-esm/ui/reaction-picker.mjs.map +7 -0
- package/dist-esm/ui/reaction.mjs +59 -7
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/reactions.mjs +21 -8
- package/dist-esm/ui/reactions.mjs.map +2 -2
- package/dist-esm/ui/reply-count.mjs +9 -0
- package/dist-esm/ui/reply-count.mjs.map +7 -0
- package/dist-esm/ui/send-button.mjs +13 -1
- package/dist-esm/ui/send-button.mjs.map +2 -2
- package/dist-esm/ui/visual-viewport.mjs +12 -0
- package/dist-esm/ui/visual-viewport.mjs.map +7 -0
- package/package.json +6 -6
- package/src/canvas/anchor-lifecycle.test.ts +290 -0
- package/src/canvas/anchor-lifecycle.ts +191 -0
- package/src/canvas/canvas-events.ts +18 -0
- package/src/canvas/canvas.css +464 -55
- package/src/canvas/cluster-badge.tsx +133 -0
- package/src/canvas/cluster-fade.ts +102 -0
- package/src/canvas/cluster-input.test.ts +178 -36
- package/src/canvas/cluster-input.ts +95 -9
- package/src/canvas/cluster-model.ts +378 -0
- package/src/canvas/comment-body.tsx +1 -1
- package/src/canvas/comment-drafts.test.ts +55 -0
- package/src/canvas/comment-drafts.ts +46 -0
- package/src/canvas/comment-mutations.test.ts +452 -0
- package/src/canvas/comment-mutations.ts +299 -0
- package/src/canvas/comment-reactions.test.ts +154 -0
- package/src/canvas/comment-reactions.tsx +246 -0
- package/src/canvas/comment-render.test.ts +1 -1
- package/src/canvas/comment-render.ts +17 -13
- package/src/canvas/comment-store.ts +68 -33
- package/src/canvas/comment-tool.test.ts +162 -0
- package/src/canvas/comment-tool.tsx +103 -25
- package/src/canvas/comments-filter-menu.tsx +20 -36
- package/src/canvas/comments-overflow-menu.tsx +17 -19
- package/src/canvas/comments-overlay.tsx +252 -1369
- package/src/canvas/comments-sidebar.test.ts +45 -0
- package/src/canvas/comments-sidebar.tsx +112 -73
- package/src/canvas/comments-visibility-toggle.tsx +25 -0
- package/src/canvas/context.ts +48 -0
- package/src/canvas/hooks.test.ts +94 -0
- package/src/canvas/hooks.ts +66 -11
- package/src/canvas/license.ts +1 -1
- package/src/canvas/mobile-placement.ts +115 -0
- package/src/canvas/options.test.ts +176 -4
- package/src/canvas/options.ts +276 -18
- package/src/canvas/pending-composer.tsx +157 -0
- package/src/canvas/pin-stacking.test.ts +224 -0
- package/src/canvas/pin-stacking.ts +103 -0
- package/src/canvas/region-box.tsx +124 -0
- package/src/canvas/sidebar-filters.ts +4 -3
- package/src/canvas/state.ts +88 -39
- package/src/canvas/thread-pin.tsx +418 -0
- package/src/canvas/thread-preview.test.ts +91 -0
- package/src/canvas/thread-preview.tsx +294 -0
- package/src/canvas/thread-stack.tsx +242 -0
- package/src/canvas/thread-state.test.ts +260 -0
- package/src/canvas/thread-state.ts +171 -37
- package/src/canvas/thread-view.test.ts +72 -0
- package/src/canvas/thread-view.tsx +614 -0
- package/src/clustering/computeClusterTable.test.ts +24 -23
- package/src/clustering/computeClusterTable.ts +17 -8
- package/src/clustering/replay.test.ts +0 -7
- package/src/clustering/replay.ts +131 -32
- package/src/clustering/runtime.test.ts +267 -13
- package/src/clustering/runtime.ts +176 -19
- package/src/clustering/schedule.test.ts +0 -6
- package/src/clustering/screen-offsets.test.ts +171 -0
- package/src/clustering/types.ts +19 -9
- package/src/index.ts +91 -42
- package/src/ui/byline.tsx +21 -9
- package/src/ui/comment-card.tsx +20 -10
- package/src/ui/comment-composer.tsx +178 -56
- package/src/ui/comment-pin.tsx +13 -2
- package/src/ui/comment-thread.tsx +13 -6
- package/src/ui/comments-list.test.ts +31 -0
- package/src/ui/comments-list.tsx +82 -47
- package/src/ui/comments.css +577 -294
- package/src/ui/count-badge.tsx +9 -2
- package/src/ui/emoji-picker.test.ts +25 -0
- package/src/ui/emoji-picker.tsx +76 -0
- package/src/ui/empty-state.tsx +3 -3
- package/src/ui/format-time.test.ts +69 -0
- package/src/ui/format-time.ts +27 -2
- package/src/ui/icons.tsx +116 -0
- package/src/ui/reaction-picker.tsx +102 -0
- package/src/ui/reaction.tsx +125 -6
- package/src/ui/reactions.tsx +69 -10
- package/src/ui/reply-count.ts +16 -0
- package/src/ui/send-button.tsx +13 -3
- package/src/ui/visual-viewport.test.ts +36 -0
- package/src/ui/visual-viewport.ts +27 -0
- package/dist-cjs/canvas/region-options.js +0 -49
- package/dist-cjs/canvas/region-options.js.map +0 -7
- package/dist-cjs/ui/avatar.js.map +0 -7
- package/dist-cjs/ui/comment-mention.js +0 -42
- package/dist-cjs/ui/comment-mention.js.map +0 -7
- package/dist-cjs/ui/comment-text.js.map +0 -7
- package/dist-cjs/ui/mention-list.js +0 -70
- package/dist-cjs/ui/mention-list.js.map +0 -7
- package/dist-cjs/ui/mention-suggestion.js +0 -206
- package/dist-cjs/ui/mention-suggestion.js.map +0 -7
- package/dist-cjs/ui/mention.js.map +0 -7
- package/dist-cjs/ui/render-markdown.js +0 -63
- package/dist-cjs/ui/render-markdown.js.map +0 -7
- package/dist-esm/canvas/region-options.mjs +0 -29
- package/dist-esm/canvas/region-options.mjs.map +0 -7
- package/dist-esm/ui/avatar.mjs +0 -23
- package/dist-esm/ui/avatar.mjs.map +0 -7
- package/dist-esm/ui/comment-mention.mjs +0 -22
- package/dist-esm/ui/comment-mention.mjs.map +0 -7
- package/dist-esm/ui/comment-text.mjs +0 -9
- package/dist-esm/ui/comment-text.mjs.map +0 -7
- package/dist-esm/ui/mention-list.mjs +0 -50
- package/dist-esm/ui/mention-list.mjs.map +0 -7
- package/dist-esm/ui/mention-suggestion.mjs +0 -186
- package/dist-esm/ui/mention-suggestion.mjs.map +0 -7
- package/dist-esm/ui/mention.mjs +0 -11
- package/dist-esm/ui/mention.mjs.map +0 -7
- package/dist-esm/ui/render-markdown.mjs +0 -45
- package/dist-esm/ui/render-markdown.mjs.map +0 -7
- package/src/canvas/region-options.ts +0 -57
- package/src/ui/avatar.tsx +0 -31
- package/src/ui/comment-mention.ts +0 -47
- package/src/ui/comment-text.tsx +0 -12
- package/src/ui/mention-list.tsx +0 -106
- package/src/ui/mention-suggestion.test.ts +0 -18
- package/src/ui/mention-suggestion.tsx +0 -285
- package/src/ui/mention.tsx +0 -9
- package/src/ui/render-markdown.tsx +0 -72
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import {
|
|
2
|
+
commentSchemaRecords,
|
|
3
|
+
createCommentThread,
|
|
4
|
+
createShapeId,
|
|
5
|
+
createTLSchema,
|
|
6
|
+
createTLStore,
|
|
7
|
+
defaultBindingUtils,
|
|
8
|
+
defaultShapeUtils,
|
|
9
|
+
defaultTools,
|
|
10
|
+
Editor,
|
|
11
|
+
TLComment,
|
|
12
|
+
TLCommentThread,
|
|
13
|
+
toRichText,
|
|
14
|
+
createComment as tlCreateComment,
|
|
15
|
+
} from 'tldraw'
|
|
16
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
17
|
+
import {
|
|
18
|
+
commitCommentMutation,
|
|
19
|
+
deleteComment,
|
|
20
|
+
deleteThread,
|
|
21
|
+
editComment,
|
|
22
|
+
putCommentRecords,
|
|
23
|
+
removeCommentRecords,
|
|
24
|
+
reopenThread,
|
|
25
|
+
resolveThread,
|
|
26
|
+
} from './comment-mutations'
|
|
27
|
+
import {
|
|
28
|
+
getCommentRecord,
|
|
29
|
+
getComments,
|
|
30
|
+
getCommentThreads,
|
|
31
|
+
getLiveComments,
|
|
32
|
+
getLiveCommentThreads,
|
|
33
|
+
} from './comment-store'
|
|
34
|
+
import { CommentTool } from './comment-tool'
|
|
35
|
+
import { openThreadId } from './state'
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* These need a real editor: what's under test is how each write interacts with the store and the
|
|
39
|
+
* undo stack, which is exactly what the history option governs.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
let editors: Editor[] = []
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
for (const editor of editors) editor.dispose()
|
|
46
|
+
editors = []
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
function makeEditor(tool?: ReturnType<typeof CommentTool.configure>): Editor {
|
|
50
|
+
const editor = new Editor({
|
|
51
|
+
store: createTLStore({ schema: createTLSchema({ records: commentSchemaRecords }) }),
|
|
52
|
+
shapeUtils: defaultShapeUtils,
|
|
53
|
+
bindingUtils: defaultBindingUtils,
|
|
54
|
+
tools: tool ? [...defaultTools, tool] : defaultTools,
|
|
55
|
+
getContainer: () => document.body,
|
|
56
|
+
})
|
|
57
|
+
editors.push(editor)
|
|
58
|
+
return editor
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function makeThread(editor: Editor, createdBy = 'me') {
|
|
62
|
+
const thread = createCommentThread({
|
|
63
|
+
pageId: editor.getCurrentPageId(),
|
|
64
|
+
anchor: { type: 'point', x: 0, y: 0 },
|
|
65
|
+
createdBy,
|
|
66
|
+
})
|
|
67
|
+
const comment = tlCreateComment({
|
|
68
|
+
threadId: thread.id,
|
|
69
|
+
pageId: thread.pageId,
|
|
70
|
+
authorId: createdBy,
|
|
71
|
+
body: toRichText('hello'),
|
|
72
|
+
})
|
|
73
|
+
putCommentRecords(editor, [thread, comment])
|
|
74
|
+
return { thread, comment }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function addComment(editor: Editor, thread: TLCommentThread, authorId = 'me') {
|
|
78
|
+
const comment = tlCreateComment({
|
|
79
|
+
threadId: thread.id,
|
|
80
|
+
pageId: thread.pageId,
|
|
81
|
+
authorId,
|
|
82
|
+
body: toRichText('reply'),
|
|
83
|
+
})
|
|
84
|
+
putCommentRecords(editor, [comment])
|
|
85
|
+
return comment
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readComment(editor: Editor, comment: TLComment) {
|
|
89
|
+
return getCommentRecord(editor, comment.id) as TLComment | undefined
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function readThread(editor: Editor, thread: TLCommentThread) {
|
|
93
|
+
return getCommentRecord(editor, thread.id) as TLCommentThread | undefined
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
describe('editComment', () => {
|
|
97
|
+
it('replaces the body and stamps editedAt', () => {
|
|
98
|
+
const editor = makeEditor()
|
|
99
|
+
const { comment } = makeThread(editor)
|
|
100
|
+
expect(comment.editedAt).toBe(null)
|
|
101
|
+
|
|
102
|
+
editComment(editor, comment, toRichText('changed'))
|
|
103
|
+
|
|
104
|
+
const updated = readComment(editor, comment)!
|
|
105
|
+
expect(updated.body).toEqual(toRichText('changed'))
|
|
106
|
+
expect(typeof updated.editedAt).toBe('number')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// The caller's record is a snapshot. Writing it back would revert every field it doesn't mean
|
|
110
|
+
// to touch — here `meta`, set after the caller took its copy.
|
|
111
|
+
it('edits the version in the store, not the copy it was handed', () => {
|
|
112
|
+
const editor = makeEditor()
|
|
113
|
+
const { comment } = makeThread(editor)
|
|
114
|
+
putCommentRecords(editor, [{ ...comment, meta: { pinned: true } }])
|
|
115
|
+
|
|
116
|
+
editComment(editor, comment, toRichText('changed'))
|
|
117
|
+
|
|
118
|
+
expect(readComment(editor, comment)).toMatchObject({
|
|
119
|
+
body: toRichText('changed'),
|
|
120
|
+
meta: { pinned: true },
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// `put` is an upsert, so writing a snapshot back would re-create the record — with
|
|
125
|
+
// `isDeleted: false` — after someone else's delete had already taken it away.
|
|
126
|
+
it('does not resurrect a comment that has been removed since', () => {
|
|
127
|
+
const editor = makeEditor()
|
|
128
|
+
const { comment } = makeThread(editor)
|
|
129
|
+
removeCommentRecords(editor, [comment.id])
|
|
130
|
+
|
|
131
|
+
editComment(editor, comment, toRichText('changed'))
|
|
132
|
+
|
|
133
|
+
expect(readComment(editor, comment)).toBeUndefined()
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
describe('resolveThread and reopenThread', () => {
|
|
138
|
+
it('stamps who resolved the thread and when', () => {
|
|
139
|
+
const editor = makeEditor()
|
|
140
|
+
const { thread } = makeThread(editor)
|
|
141
|
+
|
|
142
|
+
resolveThread(editor, thread, 'ada')
|
|
143
|
+
|
|
144
|
+
const resolved = readThread(editor, thread)!.resolved!
|
|
145
|
+
expect(resolved.by).toBe('ada')
|
|
146
|
+
expect(typeof resolved.at).toBe('number')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('clears the resolution when reopened', () => {
|
|
150
|
+
const editor = makeEditor()
|
|
151
|
+
const { thread } = makeThread(editor)
|
|
152
|
+
resolveThread(editor, thread, 'ada')
|
|
153
|
+
|
|
154
|
+
reopenThread(editor, readThread(editor, thread)!)
|
|
155
|
+
|
|
156
|
+
expect(readThread(editor, thread)!.resolved).toBe(null)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// A thread's anchor moves without anyone touching the thread — a pin drag, or a pinned shape's
|
|
160
|
+
// delete converting it to a point. Resolving from a copy taken before that must not undo it.
|
|
161
|
+
it('resolves the version in the store, leaving an anchor moved since where it is', () => {
|
|
162
|
+
const editor = makeEditor()
|
|
163
|
+
const { thread } = makeThread(editor)
|
|
164
|
+
putCommentRecords(editor, [{ ...thread, anchor: { type: 'point', x: 50, y: 50 } }])
|
|
165
|
+
|
|
166
|
+
resolveThread(editor, thread, 'ada')
|
|
167
|
+
|
|
168
|
+
const updated = readThread(editor, thread)!
|
|
169
|
+
expect(updated.anchor).toEqual({ type: 'point', x: 50, y: 50 })
|
|
170
|
+
expect(updated.resolved!.by).toBe('ada')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('reopens the version in the store, leaving an anchor moved since where it is', () => {
|
|
174
|
+
const editor = makeEditor()
|
|
175
|
+
const { thread } = makeThread(editor)
|
|
176
|
+
resolveThread(editor, thread, 'ada')
|
|
177
|
+
putCommentRecords(editor, [
|
|
178
|
+
{ ...readThread(editor, thread)!, anchor: { type: 'point', x: 50, y: 50 } },
|
|
179
|
+
])
|
|
180
|
+
|
|
181
|
+
reopenThread(editor, thread)
|
|
182
|
+
|
|
183
|
+
const updated = readThread(editor, thread)!
|
|
184
|
+
expect(updated.anchor).toEqual({ type: 'point', x: 50, y: 50 })
|
|
185
|
+
expect(updated.resolved).toBe(null)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('does nothing to a thread that has been removed since', () => {
|
|
189
|
+
const editor = makeEditor()
|
|
190
|
+
const { thread } = makeThread(editor)
|
|
191
|
+
removeCommentRecords(editor, [thread.id])
|
|
192
|
+
|
|
193
|
+
resolveThread(editor, thread, 'ada')
|
|
194
|
+
reopenThread(editor, thread)
|
|
195
|
+
|
|
196
|
+
expect(readThread(editor, thread)).toBeUndefined()
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
describe('deleteComment', () => {
|
|
201
|
+
it('soft-deletes: the record stays put with its flag set, for the server to prune', () => {
|
|
202
|
+
const editor = makeEditor()
|
|
203
|
+
const { comment } = makeThread(editor)
|
|
204
|
+
|
|
205
|
+
deleteComment(editor, comment)
|
|
206
|
+
|
|
207
|
+
expect(readComment(editor, comment)).toMatchObject({ isDeleted: true })
|
|
208
|
+
expect(getComments(editor)).toHaveLength(1)
|
|
209
|
+
expect(getLiveComments(editor)).toHaveLength(0)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('closes the thread when its last comment goes', () => {
|
|
213
|
+
const editor = makeEditor()
|
|
214
|
+
const { thread, comment } = makeThread(editor)
|
|
215
|
+
openThreadId.set(editor, thread.id)
|
|
216
|
+
|
|
217
|
+
deleteComment(editor, comment)
|
|
218
|
+
|
|
219
|
+
expect(openThreadId.get(editor)).toBe(null)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('leaves the thread open while it still has comments', () => {
|
|
223
|
+
const editor = makeEditor()
|
|
224
|
+
const { thread, comment } = makeThread(editor)
|
|
225
|
+
addComment(editor, thread)
|
|
226
|
+
openThreadId.set(editor, thread.id)
|
|
227
|
+
|
|
228
|
+
deleteComment(editor, comment)
|
|
229
|
+
|
|
230
|
+
expect(openThreadId.get(editor)).toBe(thread.id)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
// Deleting the same comment twice — a double activation, a handler holding a copy taken before
|
|
234
|
+
// the first delete — shouldn't read as "the last comment went" while a sibling is still live.
|
|
235
|
+
it('leaves the thread open when the comment was already deleted', () => {
|
|
236
|
+
const editor = makeEditor()
|
|
237
|
+
const { thread, comment } = makeThread(editor)
|
|
238
|
+
addComment(editor, thread)
|
|
239
|
+
deleteComment(editor, comment)
|
|
240
|
+
openThreadId.set(editor, thread.id)
|
|
241
|
+
|
|
242
|
+
deleteComment(editor, comment)
|
|
243
|
+
|
|
244
|
+
expect(openThreadId.get(editor)).toBe(thread.id)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('does not resurrect a comment that has been removed since', () => {
|
|
248
|
+
const editor = makeEditor()
|
|
249
|
+
const { comment } = makeThread(editor)
|
|
250
|
+
removeCommentRecords(editor, [comment.id])
|
|
251
|
+
|
|
252
|
+
deleteComment(editor, comment)
|
|
253
|
+
|
|
254
|
+
expect(readComment(editor, comment)).toBeUndefined()
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('leaves a different open thread alone', () => {
|
|
258
|
+
const editor = makeEditor()
|
|
259
|
+
const { comment } = makeThread(editor)
|
|
260
|
+
const other = makeThread(editor)
|
|
261
|
+
openThreadId.set(editor, other.thread.id)
|
|
262
|
+
|
|
263
|
+
deleteComment(editor, comment)
|
|
264
|
+
|
|
265
|
+
expect(openThreadId.get(editor)).toBe(other.thread.id)
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
describe('deleteThread', () => {
|
|
270
|
+
it('soft-deletes the thread record', () => {
|
|
271
|
+
const editor = makeEditor()
|
|
272
|
+
const { thread } = makeThread(editor)
|
|
273
|
+
|
|
274
|
+
deleteThread(editor, thread)
|
|
275
|
+
|
|
276
|
+
expect(readThread(editor, thread)).toMatchObject({ isDeleted: true })
|
|
277
|
+
expect(getCommentThreads(editor)).toHaveLength(1)
|
|
278
|
+
expect(getLiveCommentThreads(editor)).toHaveLength(0)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('closes the thread when it is the open one', () => {
|
|
282
|
+
const editor = makeEditor()
|
|
283
|
+
const { thread } = makeThread(editor)
|
|
284
|
+
openThreadId.set(editor, thread.id)
|
|
285
|
+
|
|
286
|
+
deleteThread(editor, thread)
|
|
287
|
+
|
|
288
|
+
expect(openThreadId.get(editor)).toBe(null)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('flags the version in the store, leaving an anchor moved since where it is', () => {
|
|
292
|
+
const editor = makeEditor()
|
|
293
|
+
const { thread } = makeThread(editor)
|
|
294
|
+
putCommentRecords(editor, [{ ...thread, anchor: { type: 'point', x: 50, y: 50 } }])
|
|
295
|
+
|
|
296
|
+
deleteThread(editor, thread)
|
|
297
|
+
|
|
298
|
+
expect(readThread(editor, thread)).toMatchObject({
|
|
299
|
+
isDeleted: true,
|
|
300
|
+
anchor: { type: 'point', x: 50, y: 50 },
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('does not resurrect a thread that has been removed since', () => {
|
|
305
|
+
const editor = makeEditor()
|
|
306
|
+
const { thread } = makeThread(editor)
|
|
307
|
+
removeCommentRecords(editor, [thread.id])
|
|
308
|
+
|
|
309
|
+
deleteThread(editor, thread)
|
|
310
|
+
|
|
311
|
+
expect(readThread(editor, thread)).toBeUndefined()
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
describe('history', () => {
|
|
316
|
+
it('does not put comment writes on the undo stack by default', () => {
|
|
317
|
+
const editor = makeEditor()
|
|
318
|
+
editor.markHistoryStoppingPoint()
|
|
319
|
+
const { comment } = makeThread(editor)
|
|
320
|
+
|
|
321
|
+
editor.undo()
|
|
322
|
+
|
|
323
|
+
expect(readComment(editor, comment)).toBeDefined()
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
it('puts them on the undo stack when the history option asks for it', () => {
|
|
327
|
+
const editor = makeEditor(CommentTool.configure({ history: 'record' }))
|
|
328
|
+
editor.markHistoryStoppingPoint()
|
|
329
|
+
const { comment } = makeThread(editor)
|
|
330
|
+
|
|
331
|
+
editor.undo()
|
|
332
|
+
|
|
333
|
+
expect(readComment(editor, comment)).toBeUndefined()
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
// The flag is write-once server-side, so an undo clearing it would be vetoed and rebased —
|
|
337
|
+
// leaving the UI showing a comment the server has already pruned. The shape gives the undo
|
|
338
|
+
// something recorded to rewind, so what's under test is that the delete isn't on the stack
|
|
339
|
+
// rather than that the stack is empty.
|
|
340
|
+
it('never makes a delete undoable, even with history: record', () => {
|
|
341
|
+
const editor = makeEditor(CommentTool.configure({ history: 'record' }))
|
|
342
|
+
const { comment } = makeThread(editor)
|
|
343
|
+
editor.markHistoryStoppingPoint()
|
|
344
|
+
const shapeId = createShapeId()
|
|
345
|
+
editor.createShape({ id: shapeId, type: 'geo', x: 0, y: 0 })
|
|
346
|
+
|
|
347
|
+
deleteComment(editor, comment)
|
|
348
|
+
editor.undo()
|
|
349
|
+
|
|
350
|
+
expect(editor.getShape(shapeId)).toBeUndefined()
|
|
351
|
+
expect(readComment(editor, comment)).toMatchObject({ isDeleted: true })
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('never makes a thread delete undoable, even with history: record', () => {
|
|
355
|
+
const editor = makeEditor(CommentTool.configure({ history: 'record' }))
|
|
356
|
+
const { thread } = makeThread(editor)
|
|
357
|
+
editor.markHistoryStoppingPoint()
|
|
358
|
+
const shapeId = createShapeId()
|
|
359
|
+
editor.createShape({ id: shapeId, type: 'geo', x: 0, y: 0 })
|
|
360
|
+
|
|
361
|
+
deleteThread(editor, thread)
|
|
362
|
+
editor.undo()
|
|
363
|
+
|
|
364
|
+
expect(editor.getShape(shapeId)).toBeUndefined()
|
|
365
|
+
expect(readThread(editor, thread)).toMatchObject({ isDeleted: true })
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it('rejects a nested mutation that resolves to a different history mode', () => {
|
|
369
|
+
const editor = makeEditor(CommentTool.configure({ history: 'record' }))
|
|
370
|
+
const { comment } = makeThread(editor)
|
|
371
|
+
|
|
372
|
+
// A delete always ignores history, so it can't run inside a `record` commit.
|
|
373
|
+
expect(() => commitCommentMutation(editor, () => deleteComment(editor, comment))).toThrow(
|
|
374
|
+
"records history as 'ignore' can't run inside one recording it as 'record'"
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
deleteComment(editor, comment)
|
|
378
|
+
expect(readComment(editor, comment)).toMatchObject({ isDeleted: true })
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
// Store history flushes synchronously under test, so this listener writes from inside the commit
|
|
382
|
+
// that triggered it — where a side effect always sits, with no "after the mutation" to defer to.
|
|
383
|
+
// Matching modes have nothing to disagree about, so its write goes through.
|
|
384
|
+
it('lets a store listener write comments during a commit when the modes match', () => {
|
|
385
|
+
const editor = makeEditor()
|
|
386
|
+
const { thread, comment } = makeThread(editor)
|
|
387
|
+
let hasReacted = false
|
|
388
|
+
editor.store.listen(() => {
|
|
389
|
+
if (hasReacted) return
|
|
390
|
+
hasReacted = true
|
|
391
|
+
putCommentRecords(editor, [{ ...thread, meta: { lastEditedComment: comment.id } }])
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
editComment(editor, comment, toRichText('edited'))
|
|
395
|
+
|
|
396
|
+
expect(readThread(editor, thread)!.meta).toEqual({ lastEditedComment: comment.id })
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
it('rejects a writer used after its commit', () => {
|
|
400
|
+
const editor = makeEditor()
|
|
401
|
+
const { thread } = makeThread(editor)
|
|
402
|
+
let writeAfterCommit: () => void
|
|
403
|
+
|
|
404
|
+
commitCommentMutation(editor, ({ put }) => {
|
|
405
|
+
writeAfterCommit = () => put([{ ...thread, resolved: { at: 1, by: 'ada' } }])
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
expect(() => writeAfterCommit!()).toThrow('cannot be used after its commit has finished')
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
// A host can want pin drags undoable while posts and edits aren't. The drag owns its commit, and
|
|
412
|
+
// its records go through the writer, which keeps `dragHistory` in charge of them.
|
|
413
|
+
it('lets dragHistory govern a drag on its own', () => {
|
|
414
|
+
const editor = makeEditor(CommentTool.configure({ history: 'ignore', dragHistory: 'record' }))
|
|
415
|
+
const { thread } = makeThread(editor)
|
|
416
|
+
editor.markHistoryStoppingPoint()
|
|
417
|
+
|
|
418
|
+
commitCommentMutation(
|
|
419
|
+
editor,
|
|
420
|
+
({ put }) => put([{ ...thread, anchor: { type: 'point', x: 50, y: 50 } }]),
|
|
421
|
+
'drag'
|
|
422
|
+
)
|
|
423
|
+
editor.undo()
|
|
424
|
+
|
|
425
|
+
expect(readThread(editor, thread)!.anchor).toEqual({ type: 'point', x: 0, y: 0 })
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
describe('getLiveComments and getLiveCommentThreads', () => {
|
|
430
|
+
it('hides a thread whose comments have all been deleted', () => {
|
|
431
|
+
const editor = makeEditor()
|
|
432
|
+
const { thread, comment } = makeThread(editor)
|
|
433
|
+
|
|
434
|
+
deleteComment(editor, comment)
|
|
435
|
+
|
|
436
|
+
// The thread record itself is untouched — the deleter may not be its creator — but with no
|
|
437
|
+
// live comment left it has no surface.
|
|
438
|
+
expect(readThread(editor, thread)!.isDeleted).toBe(false)
|
|
439
|
+
expect(getLiveCommentThreads(editor)).toHaveLength(0)
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
it('keeps a thread that still has a live comment', () => {
|
|
443
|
+
const editor = makeEditor()
|
|
444
|
+
const { thread, comment } = makeThread(editor)
|
|
445
|
+
addComment(editor, thread)
|
|
446
|
+
|
|
447
|
+
deleteComment(editor, comment)
|
|
448
|
+
|
|
449
|
+
expect(getLiveComments(editor)).toHaveLength(1)
|
|
450
|
+
expect(getLiveCommentThreads(editor)).toEqual([readThread(editor, thread)])
|
|
451
|
+
})
|
|
452
|
+
})
|