@blocksdiy/react-common 1.70.2 → 1.71.1

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.
Files changed (75) hide show
  1. package/dist/comments/anchor.d.ts +96 -0
  2. package/dist/comments/anchor.d.ts.map +1 -0
  3. package/dist/comments/anchor.js +346 -0
  4. package/dist/comments/api.d.ts +38 -0
  5. package/dist/comments/api.d.ts.map +1 -0
  6. package/dist/comments/api.js +62 -0
  7. package/dist/comments/comment-agents.d.ts +28 -0
  8. package/dist/comments/comment-agents.d.ts.map +1 -0
  9. package/dist/comments/comment-agents.js +32 -0
  10. package/dist/comments/comment-mode.d.ts +115 -0
  11. package/dist/comments/comment-mode.d.ts.map +1 -0
  12. package/dist/comments/comment-mode.js +176 -0
  13. package/dist/comments/composer.d.ts +133 -0
  14. package/dist/comments/composer.d.ts.map +1 -0
  15. package/dist/comments/composer.js +273 -0
  16. package/dist/comments/index.d.ts +19 -0
  17. package/dist/comments/index.d.ts.map +1 -0
  18. package/dist/comments/index.js +23 -0
  19. package/dist/comments/layout.d.ts +25 -0
  20. package/dist/comments/layout.d.ts.map +1 -0
  21. package/dist/comments/layout.js +83 -0
  22. package/dist/comments/mentions.d.ts +32 -0
  23. package/dist/comments/mentions.d.ts.map +1 -0
  24. package/dist/comments/mentions.js +30 -0
  25. package/dist/comments/menu.d.ts +33 -0
  26. package/dist/comments/menu.d.ts.map +1 -0
  27. package/dist/comments/menu.js +47 -0
  28. package/dist/comments/open-comment.d.ts +28 -0
  29. package/dist/comments/open-comment.d.ts.map +1 -0
  30. package/dist/comments/open-comment.js +9 -0
  31. package/dist/comments/page-pick.d.ts +76 -0
  32. package/dist/comments/page-pick.d.ts.map +1 -0
  33. package/dist/comments/page-pick.js +283 -0
  34. package/dist/comments/pick-message.d.ts +11 -0
  35. package/dist/comments/pick-message.d.ts.map +1 -0
  36. package/dist/comments/pick-message.js +33 -0
  37. package/dist/comments/pin-view.d.ts +107 -0
  38. package/dist/comments/pin-view.d.ts.map +1 -0
  39. package/dist/comments/pin-view.js +248 -0
  40. package/dist/comments/pins.d.ts +126 -0
  41. package/dist/comments/pins.d.ts.map +1 -0
  42. package/dist/comments/pins.js +218 -0
  43. package/dist/comments/text-pick.d.ts +69 -0
  44. package/dist/comments/text-pick.d.ts.map +1 -0
  45. package/dist/comments/text-pick.js +307 -0
  46. package/dist/comments/threads-store.d.ts +119 -0
  47. package/dist/comments/threads-store.d.ts.map +1 -0
  48. package/dist/comments/threads-store.js +286 -0
  49. package/dist/comments/types.d.ts +235 -0
  50. package/dist/comments/types.d.ts.map +1 -0
  51. package/dist/comments/types.js +109 -0
  52. package/dist/comments/use-comment-menu.d.ts +69 -0
  53. package/dist/comments/use-comment-menu.d.ts.map +1 -0
  54. package/dist/comments/use-comment-menu.js +140 -0
  55. package/dist/comments/use-held.d.ts +3 -0
  56. package/dist/comments/use-held.d.ts.map +1 -0
  57. package/dist/comments/use-held.js +16 -0
  58. package/dist/comments/use-thread-scroll.d.ts +23 -0
  59. package/dist/comments/use-thread-scroll.d.ts.map +1 -0
  60. package/dist/comments/use-thread-scroll.js +89 -0
  61. package/dist/hooks/agent-conversation-context.d.ts +12 -1
  62. package/dist/hooks/agent-conversation-context.d.ts.map +1 -1
  63. package/dist/hooks/agent-conversation-context.js +12 -5
  64. package/dist/hooks/agent-thread-live-update.d.ts +5 -2
  65. package/dist/hooks/agent-thread-live-update.d.ts.map +1 -1
  66. package/dist/hooks/agent-thread-live-update.js +14 -0
  67. package/dist/hooks/use-agent-conversation.d.ts +8 -0
  68. package/dist/hooks/use-agent-conversation.d.ts.map +1 -1
  69. package/dist/hooks/use-agent-conversation.js +10 -0
  70. package/dist/hooks/use-agent-threads.d.ts +1 -0
  71. package/dist/hooks/use-agent-threads.d.ts.map +1 -1
  72. package/dist/hooks/use-agent-threads.js +4 -1
  73. package/dist/iframe-communication/types.d.ts +25 -0
  74. package/dist/iframe-communication/types.d.ts.map +1 -1
  75. package/package.json +5 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-pick.d.ts","sourceRoot":"","sources":["../../src/comments/text-pick.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAU,MAAM,UAAU,CAAC;AAE7C;;;;;GAKG;AAEH,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAgBnC;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAoC7G,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,cAAc,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,SAAS,IAAI,EAAE,EAAE,aAAQ,KAAG,OAGrF,CAAC;AAEJ,mDAAmD;AACnD,eAAO,MAAM,OAAO,GAAI,GAAG,QAAQ,EAAE,GAAG,QAAQ,KAAG,OAMlD,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,YAAY,GAAI,KAAK,QAAQ,EAAE,GAAG,QAAQ,EAAE,GAAG,QAAQ,KAAG,KAMtE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,KAAG,OAAO,GAAG,IAGjD,CAAC;AAEF,yGAAyG;AACzG,eAAO,MAAM,WAAW,GAAI,MAAM,OAAO,EAAE,OAAO,QAAQ,KAAG,QAM5D,CAAC;AAOF,+FAA+F;AAC/F,eAAO,MAAM,cAAc,MAAM,CAAC;AA6IlC;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,SAAS,GAAG,IAYhF,CAAC;AAkBF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,KAAK,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,SAAS,GAAG,IAgB7E,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,WAAW,GAAI,KAAK,QAAQ,EAAE,OAAO,SAAS,KAAG,KAAK,GAAG,IASrE,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,WAAW,GAAI,KAAK,QAAQ,EAAE,OAAO,SAAS,KAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAmB1G,CAAC"}
@@ -0,0 +1,307 @@
1
+ import { isPickIgnored, ITEM_ATTRIBUTE, pickTarget } from "./anchor.js";
2
+ import { toRect } from "./layout.js";
3
+ /** How far the pointer moves from a press on text before it is a drag, not a click. */
4
+ export const DRAG_THRESHOLD_PX = 4;
5
+ const segmenters = new Map();
6
+ const wordSegmenter = (locale) => {
7
+ if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") {
8
+ return null;
9
+ }
10
+ const key = locale ?? "";
11
+ let segmenter = segmenters.get(key);
12
+ if (!segmenter) {
13
+ segmenter = new Intl.Segmenter(locale, { granularity: "word" });
14
+ segmenters.set(key, segmenter);
15
+ }
16
+ return segmenter;
17
+ };
18
+ /**
19
+ * The word around `offset` in `text` — the one it is in, else the one ending right before it —
20
+ * as `[start, end)`; null over spaces and punctuation. Words in any script (`Intl.Segmenter`), a
21
+ * whitespace split where it is missing.
22
+ */
23
+ export const wordBoundsAt = (text, offset, locale) => {
24
+ if (!text) {
25
+ return null;
26
+ }
27
+ const at = Math.max(0, Math.min(offset, text.length));
28
+ const segmenter = wordSegmenter(locale);
29
+ if (segmenter) {
30
+ let before = null;
31
+ for (const segment of segmenter.segment(text)) {
32
+ const start = segment.index;
33
+ const end = start + segment.segment.length;
34
+ if (!segment.isWordLike) {
35
+ continue;
36
+ }
37
+ if (at >= start && at < end) {
38
+ return { start, end };
39
+ }
40
+ if (end === at) {
41
+ before = { start, end };
42
+ }
43
+ }
44
+ return before;
45
+ }
46
+ const isWordChar = (char) => !!char && !/[\s.,;:!?()[\]{}"'«»“”‘’…-]/.test(char);
47
+ let start = isWordChar(text[at]) ? at : isWordChar(text[at - 1]) ? at - 1 : -1;
48
+ if (start < 0) {
49
+ return null;
50
+ }
51
+ let end = start;
52
+ while (start > 0 && isWordChar(text[start - 1])) {
53
+ start -= 1;
54
+ }
55
+ while (end < text.length && isWordChar(text[end])) {
56
+ end += 1;
57
+ }
58
+ return { start, end };
59
+ };
60
+ /** Whether `(x, y)` is on one of `rects`, with `slop` px around each. */
61
+ export const isPointInRects = (x, y, rects, slop = 1) => rects.some((rect) => x >= rect.left - slop && x <= rect.right + slop && y >= rect.top - slop && y <= rect.bottom + slop);
62
+ /** Whether `a` comes after `b` in the document. */
63
+ export const isAfter = (a, b) => {
64
+ const doc = b.node.ownerDocument ?? b.node;
65
+ const at = doc.createRange();
66
+ at.setStart(b.node, b.offset);
67
+ at.collapse(true);
68
+ return at.comparePoint(a.node, a.offset) > 0;
69
+ };
70
+ /** The range between two positions, in document order. */
71
+ export const rangeBetween = (doc, a, b) => {
72
+ const [start, end] = isAfter(a, b) ? [b, a] : [a, b];
73
+ const range = doc.createRange();
74
+ range.setStart(start.node, start.offset);
75
+ range.setEnd(end.node, end.offset);
76
+ return range;
77
+ };
78
+ /**
79
+ * What a drag that starts at `node` may cover: the record it is in (the nearest `itemAnchorProps`
80
+ * element), else the element a click there would pick. A quote never leaves it.
81
+ */
82
+ export const dragRootOf = (node) => {
83
+ const element = node instanceof Element ? node : node.parentElement;
84
+ return element?.closest(`[${ITEM_ATTRIBUTE}]`) ?? pickTarget(node);
85
+ };
86
+ /** `point` kept inside `root`: a position past its end is its end, one before its start is its start. */
87
+ export const clampToRoot = (root, point) => {
88
+ if (root.contains(point.node)) {
89
+ return point;
90
+ }
91
+ const end = { node: root, offset: root.childNodes.length };
92
+ return isAfter(point, end) ? end : { node: root, offset: 0 };
93
+ };
94
+ /** The most characters a lookup measures to correct the browser's answer before keeping it. */
95
+ export const CARET_SCAN_MAX = 256;
96
+ let graphemes;
97
+ const graphemeSegmenter = () => {
98
+ if (graphemes === undefined) {
99
+ graphemes =
100
+ typeof Intl !== "undefined" && typeof Intl.Segmenter === "function"
101
+ ? new Intl.Segmenter(undefined, { granularity: "grapheme" })
102
+ : null;
103
+ }
104
+ return graphemes;
105
+ };
106
+ const clustersByNode = new WeakMap();
107
+ /**
108
+ * Where each character of `node`'s text starts as a reader sees it (a grapheme cluster: an emoji,
109
+ * a letter with its marks), then the text's end. Without `Intl.Segmenter`, code points. Kept per
110
+ * node while its text is the same.
111
+ */
112
+ const clusterBounds = (node) => {
113
+ const data = node.data;
114
+ const known = clustersByNode.get(node);
115
+ if (known?.data === data) {
116
+ return known.bounds;
117
+ }
118
+ const bounds = [];
119
+ const segmenter = graphemeSegmenter();
120
+ if (segmenter) {
121
+ for (const segment of segmenter.segment(data)) {
122
+ bounds.push(segment.index);
123
+ }
124
+ }
125
+ else {
126
+ for (let index = 0; index < data.length; index += data.codePointAt(index) > 0xffff ? 2 : 1) {
127
+ bounds.push(index);
128
+ }
129
+ }
130
+ bounds.push(data.length);
131
+ clustersByNode.set(node, { data, bounds });
132
+ return bounds;
133
+ };
134
+ /** The character `offset` is in or starts (its index in `bounds`); the count of them at the end. */
135
+ const clusterAt = (bounds, offset) => {
136
+ let low = 0;
137
+ let high = bounds.length - 1;
138
+ while (low < high) {
139
+ const middle = (low + high + 1) >> 1;
140
+ if (bounds[middle] <= offset) {
141
+ low = middle;
142
+ }
143
+ else {
144
+ high = middle - 1;
145
+ }
146
+ }
147
+ return low;
148
+ };
149
+ /** The box of character `index` (as `clusterBounds` counts them), if it has one with width. */
150
+ const clusterRect = (doc, node, bounds, index) => {
151
+ if (index < 0 || index >= bounds.length - 1) {
152
+ return null;
153
+ }
154
+ const range = doc.createRange();
155
+ range.setStart(node, bounds[index]);
156
+ range.setEnd(node, bounds[index + 1]);
157
+ const rects = Array.from(range.getClientRects());
158
+ return rects.find((rect) => rect.right > rect.left) ?? null;
159
+ };
160
+ const holds = (rect, x, y) => !!rect && x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
161
+ /**
162
+ * Whether character `index` (its box `rect`) is in a run read left to right: from a neighbour
163
+ * touching it on its line, else from its element's direction.
164
+ */
165
+ const readsLeftToRight = (doc, node, bounds, index, rect) => {
166
+ for (const neighbour of [index + 1, index - 1]) {
167
+ const box = clusterRect(doc, node, bounds, neighbour);
168
+ if (!box || Math.abs(box.top - rect.top) > 1) {
169
+ continue;
170
+ }
171
+ if (Math.abs(box.left - rect.right) < 1) {
172
+ return neighbour > index;
173
+ }
174
+ if (Math.abs(box.right - rect.left) < 1) {
175
+ return neighbour < index;
176
+ }
177
+ }
178
+ const element = node.parentElement;
179
+ return !element || doc.defaultView?.getComputedStyle(element).direction !== "rtl";
180
+ };
181
+ /**
182
+ * The caret position at `(x, y)` from the boxes of `node`'s characters, looked for outward from
183
+ * the browser's answer (`near`, its character index): before the character under it when on its
184
+ * leading half, else after it. Null when the pointer isn't on `node`'s text, or no character
185
+ * within `CARET_SCAN_MAX` of the answer is under it.
186
+ */
187
+ const caretOnCharacters = (doc, node, bounds, near, x, y) => {
188
+ const whole = doc.createRange();
189
+ whole.selectNodeContents(node);
190
+ if (!isPointInRects(x, y, Array.from(whole.getClientRects(), toRect), 0)) {
191
+ return null;
192
+ }
193
+ const count = bounds.length - 1;
194
+ let measured = 0;
195
+ for (let distance = 0; measured < CARET_SCAN_MAX; distance += 1) {
196
+ const after = near + distance;
197
+ const before = near - distance - 1;
198
+ if (after >= count && before < 0) {
199
+ return null;
200
+ }
201
+ for (const index of [after, before]) {
202
+ if (index < 0 || index >= count || measured >= CARET_SCAN_MAX) {
203
+ continue;
204
+ }
205
+ measured += 1;
206
+ const rect = clusterRect(doc, node, bounds, index);
207
+ if (rect && holds(rect, x, y)) {
208
+ const isLeading = x < (rect.left + rect.right) / 2 === readsLeftToRight(doc, node, bounds, index, rect);
209
+ return { node, offset: bounds[isLeading ? index : index + 1] };
210
+ }
211
+ }
212
+ }
213
+ return null;
214
+ };
215
+ /**
216
+ * The caret position the browser puts under `(x, y)`, when it is in a text node. Where a run reads
217
+ * the other way than its line (Latin words on a right-to-left page), the browser can answer at a
218
+ * run's edge with a position beside other characters, far from the pointer (Chrome: the line's
219
+ * end for the left half of the run's first letter, the run's start for the right half of its
220
+ * last): when neither character beside its answer is under the pointer, the one that is decides,
221
+ * found near the answer (`CARET_SCAN_MAX`), else the answer stands. Characters are what a reader
222
+ * sees as one (an emoji, a letter with its marks): a corrected position is never inside one.
223
+ */
224
+ export const caretFromPoint = (doc, x, y) => {
225
+ const point = browserCaretAt(doc, x, y);
226
+ if (!point) {
227
+ return null;
228
+ }
229
+ const bounds = clusterBounds(point.node);
230
+ const index = clusterAt(bounds, point.offset);
231
+ const beside = bounds[index] === point.offset ? [index - 1, index] : [index];
232
+ if (beside.some((at) => holds(clusterRect(doc, point.node, bounds, at), x, y))) {
233
+ return point;
234
+ }
235
+ return caretOnCharacters(doc, point.node, bounds, index, x, y) ?? point;
236
+ };
237
+ const browserCaretAt = (doc, x, y) => {
238
+ const caretDoc = doc;
239
+ let node = null;
240
+ let offset = 0;
241
+ if (typeof caretDoc.caretPositionFromPoint === "function") {
242
+ const position = caretDoc.caretPositionFromPoint(x, y);
243
+ node = position?.offsetNode ?? null;
244
+ offset = position?.offset ?? 0;
245
+ }
246
+ else if (typeof caretDoc.caretRangeFromPoint === "function") {
247
+ const range = caretDoc.caretRangeFromPoint(x, y);
248
+ node = range?.startContainer ?? null;
249
+ offset = range?.startOffset ?? 0;
250
+ }
251
+ return node && node.nodeType === Node.TEXT_NODE ? { node: node, offset } : null;
252
+ };
253
+ /**
254
+ * The text position under the pointer, only when the pointer is ON the text: the browser answers
255
+ * with the nearest text from anywhere in an element, so the characters beside the caret decide
256
+ * (a space between words counts, so the cursor doesn't flicker along a line). Null over empty
257
+ * space, in ignored UI or an editable field.
258
+ */
259
+ export const textPointAt = (doc, x, y) => {
260
+ const point = caretFromPoint(doc, x, y);
261
+ if (!point || isPickIgnored(point.node)) {
262
+ return null;
263
+ }
264
+ if (point.node.parentElement?.closest("input, textarea, [contenteditable]")) {
265
+ return null;
266
+ }
267
+ const length = point.node.data.length;
268
+ if (length === 0) {
269
+ return null;
270
+ }
271
+ const range = doc.createRange();
272
+ range.setStart(point.node, Math.max(0, Math.min(point.offset, length) - 1));
273
+ range.setEnd(point.node, Math.min(length, point.offset + 1));
274
+ return isPointInRects(x, y, Array.from(range.getClientRects(), toRect)) ? point : null;
275
+ };
276
+ /** The word under a text position, as a range: what the hover tints. */
277
+ export const wordRangeAt = (doc, point) => {
278
+ const word = wordBoundsAt(point.node.data, point.offset, doc.documentElement.lang || undefined);
279
+ if (!word) {
280
+ return null;
281
+ }
282
+ const range = doc.createRange();
283
+ range.setStart(point.node, word.start);
284
+ range.setEnd(point.node, word.end);
285
+ return range;
286
+ };
287
+ /** Where the caret bar goes for a text position: the edge of the character beside it. */
288
+ export const caretRectAt = (doc, point) => {
289
+ const range = doc.createRange();
290
+ const length = point.node.data.length;
291
+ if (length === 0) {
292
+ return null;
293
+ }
294
+ const after = point.offset < length;
295
+ range.setStart(point.node, after ? point.offset : point.offset - 1);
296
+ range.setEnd(point.node, after ? point.offset + 1 : point.offset);
297
+ const rect = range.getClientRects()[0];
298
+ if (!rect) {
299
+ return null;
300
+ }
301
+ const isRtl = point.node.parentElement
302
+ ? doc.defaultView?.getComputedStyle(point.node.parentElement).direction === "rtl"
303
+ : false;
304
+ // Before a character: its leading edge; after the last one: its trailing edge.
305
+ const x = after !== isRtl ? rect.left : rect.right;
306
+ return { x, top: rect.top, bottom: rect.bottom };
307
+ };
@@ -0,0 +1,119 @@
1
+ import { type CommentsApi } from "./api.js";
2
+ import { type CommentAnchor, type CommentsChangedPayload, type CommentThreadDto, type CommentThreadSummaryDto, type MentionableUserDto } from "./types.js";
3
+ /**
4
+ * The comment threads of one page of an app, kept current: the page's threads, the open thread's
5
+ * comments, and the people who can be mentioned. Every change another tab makes, and every step of
6
+ * an agent's reply (this tab's mention included), arrives as an ids-only `COMMENTS_CHANGED` ping,
7
+ * and the store refetches that thread by id, which applies who may see it (a 404 removes it).
8
+ * Writes return the thread as the server saved it; either way a thread is replaced by id, and an
9
+ * answer that crossed a newer one is read again rather than set the thread back.
10
+ */
11
+ /** A page with more threads than this shows the newest ones. */
12
+ export declare const MAX_THREADS_PER_PAGE = 500;
13
+ /** Pings arriving together are refetched together. */
14
+ export declare const LIVE_BATCH_MS = 150;
15
+ /**
16
+ * Where opening a thread stands while its comments aren't in: `loading`, `not_found` (it was
17
+ * deleted, or the reader may not see it: a 404) or `failed` (a request that may work again).
18
+ */
19
+ export type CommentThreadLoadState = "loading" | "not_found" | "failed";
20
+ export interface CommentThreadsSnapshot {
21
+ status: "idle" | "loading" | "ready" | "error";
22
+ /** Newest activity first. */
23
+ threads: CommentThreadSummaryDto[];
24
+ /** The threads whose comments were loaded, by id. */
25
+ details: ReadonlyMap<number, CommentThreadDto>;
26
+ /** Threads asked for whose comments aren't in (none once they are), by id. */
27
+ threadLoads: ReadonlyMap<number, CommentThreadLoadState>;
28
+ mentionableUsers: MentionableUserDto[];
29
+ }
30
+ /** Subscribes to the app channel's comment pings; returns the unsubscribe. */
31
+ export type SubscribeToCommentPings = (onPing: (payload: CommentsChangedPayload) => void) => () => void;
32
+ export interface CommentThreadsStoreOptions {
33
+ api: CommentsApi;
34
+ appId: string;
35
+ subscribe: SubscribeToCommentPings;
36
+ setTimer?: (callback: () => void, ms: number) => ReturnType<typeof setTimeout>;
37
+ clearTimer?: (id: ReturnType<typeof setTimeout>) => void;
38
+ }
39
+ export declare class CommentThreadsStore {
40
+ private readonly options;
41
+ private snapshot;
42
+ private readonly listeners;
43
+ private routePath;
44
+ /** Bumps on every route change: an answer for an older route is dropped. */
45
+ private generation;
46
+ private stopPings;
47
+ private pendingIds;
48
+ private batchTimer;
49
+ /** Answers taken in so far; `takenAt` holds the count as of each thread's latest one. */
50
+ private taken;
51
+ private readonly takenAt;
52
+ private readonly setTimer;
53
+ private readonly clearTimer;
54
+ constructor(options: CommentThreadsStoreOptions);
55
+ getSnapshot: () => CommentThreadsSnapshot;
56
+ subscribe: (listener: () => void) => (() => void);
57
+ /** Shows the threads of `routePath` (null: none, and stop listening). */
58
+ setRoute(routePath: string | null): Promise<void>;
59
+ /**
60
+ * Loads a thread's comments (when it is opened). Until they are in, `threadLoads` says where it
61
+ * stands; a thread already loaded stays shown while it is read again.
62
+ */
63
+ loadThread(threadId: number): Promise<CommentThreadDto | null>;
64
+ createThread(anchor: CommentAnchor, body: string): Promise<CommentThreadDto>;
65
+ addComment(threadId: number, body: string): Promise<CommentThreadDto>;
66
+ updateComment(commentId: number, body: string): Promise<CommentThreadDto>;
67
+ /**
68
+ * Deletes one of the reader's comments. Null when it was the thread's last (no person's or agent's
69
+ * left): the thread is gone, and goes from the store as a 404 takes it (`not_found`).
70
+ */
71
+ deleteComment(commentId: number): Promise<CommentThreadDto | null>;
72
+ setResolved(threadId: number, resolved: boolean): Promise<CommentThreadDto>;
73
+ dispose(): void;
74
+ private loadAll;
75
+ private onPing;
76
+ private cancelBatch;
77
+ /** Whether the thread took an answer after a request sent at `sentAt` (a count of answers taken). */
78
+ private isCrossed;
79
+ /**
80
+ * Reads a thread and takes in the answer, by the rule above (`mayRetry` false: this is the read
81
+ * again, taken as it is). A 404 removes the thread. Returns the thread as the store has it now.
82
+ */
83
+ private refetch;
84
+ /**
85
+ * Sends a write and takes in its answer, by the rule above. Should reading the thread again fail,
86
+ * the write's answer is taken all the same, unless the thread is gone: the person's own change
87
+ * must show, and their tab gets no ping for it.
88
+ */
89
+ private write;
90
+ /** Takes in a thread as the server returned it. A thread of another page is kept out of the list. */
91
+ private apply;
92
+ private setLoad;
93
+ private remove;
94
+ private set;
95
+ }
96
+ /** Subscribes through the SDK's websocket connection (the app channel the app already joined). */
97
+ export declare const subscribeWithWebsockets: (service: {
98
+ listen(event: string, callback: (data: unknown) => Promise<void>, options?: {
99
+ filter?: Record<string, unknown>;
100
+ }): void;
101
+ unlisten(event: string, callback: (data: unknown) => Promise<void>): void;
102
+ }) => ((appId: string) => SubscribeToCommentPings);
103
+ /** The app's comment pings, on the websocket connection the SDK opened for the app. */
104
+ export declare const commentPingsFor: (appId: string) => SubscribeToCommentPings;
105
+ /**
106
+ * The threads of the current page while `isActive` (comment mode on): nothing is loaded and nothing
107
+ * listened to otherwise.
108
+ */
109
+ export declare const useCommentThreads: ({ api, appId, subscribe, routePath, isActive, }: {
110
+ api: CommentsApi;
111
+ appId: string;
112
+ subscribe: SubscribeToCommentPings;
113
+ routePath: string;
114
+ isActive: boolean;
115
+ }) => {
116
+ store: CommentThreadsStore;
117
+ snapshot: CommentThreadsSnapshot;
118
+ };
119
+ //# sourceMappingURL=threads-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads-store.d.ts","sourceRoot":"","sources":["../../src/comments/threads-store.ts"],"names":[],"mappings":"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"}