@lotics/app-sdk 0.81.0 → 0.82.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.
@@ -1,8 +1,6 @@
1
1
  /** A file attached to a comment, in the resolved (serving) shape the list returns. */
2
2
  export interface AppCommentFile {
3
3
  id: string;
4
- /** Storage key — needed to re-send the file when editing a comment. */
5
- file_storage_key: string;
6
4
  filename: string;
7
5
  mime_type: string;
8
6
  url?: string;
@@ -18,22 +18,30 @@
18
18
  * signed-in member: `available` is false, lists stay empty, and a mutation
19
19
  * rejects. Check `available` before rendering a composer.
20
20
  *
21
- * Freshness mirrors `useQuery`: SWR-cached, revalidate on focus / reconnect, and
22
- * an explicit refetch after every mutation. There is no realtime push to apps,
23
- * so a thread updated by another viewer appears on the next focus / refetch.
21
+ * Freshness: SWR-cached, revalidate on focus / reconnect, and an explicit
22
+ * refetch after every mutation so another viewer's comment appears on the
23
+ * next focus or `refetch()`, not on its own.
24
+ *
25
+ * This is the one read hook that does NOT keep itself current, and the reason
26
+ * is worth knowing rather than guessing: the realtime channel carries **record**
27
+ * changes, and a comment is its own entity that emits no record event. Your
28
+ * QUERIES do get pushed (`docs/data_fetching.md`) — do not read this as "apps
29
+ * have no realtime" and go build a poll.
24
30
  */
25
31
  import { useCallback } from "react";
26
32
  import useSWR from "swr";
27
33
  import { rpc } from "./rpc.js";
28
34
  import { useAppContext } from "./viewer.js";
29
- /** The reduced storage shape the update endpoint accepts for `files`. */
30
- function toStorageFiles(files) {
31
- return (files ?? []).map((f) => ({
32
- id: f.id,
33
- file_storage_key: f.file_storage_key,
34
- filename: f.filename,
35
- mime_type: f.mime_type,
36
- }));
35
+ /**
36
+ * The attachment set as ids — what the update endpoint wants.
37
+ *
38
+ * Sending whole file objects instead would be a full-snapshot write of metadata
39
+ * this client does not own: a filename or a storage key echoed back from a read
40
+ * lands in the row verbatim, so a stale copy overwrites what is stored. An id
41
+ * the server resolves for itself cannot do that.
42
+ */
43
+ function toFileIds(files) {
44
+ return (files ?? []).map((f) => f.id);
37
45
  }
38
46
  let optimisticCounter = 0;
39
47
  /**
@@ -116,7 +124,7 @@ export function useComments(args) {
116
124
  record_id,
117
125
  comment_id: id,
118
126
  content,
119
- files: toStorageFiles(currentFiles),
127
+ file_ids: toFileIds(currentFiles),
120
128
  });
121
129
  return rpc("comments.list", { record_id });
122
130
  }, {
@@ -279,8 +279,9 @@ updateComment, deleteComment, refetch }`.
279
279
  - `comments` — newest first on the wire (server order). Pass the array as-is to `@lotics/ui`'s
280
280
  `CommentList`, which re-sorts oldest-first for display. Each `AppComment`: `{ id, record_id,
281
281
  table_id, member_id, content, files, workspace_id, created_at, updated_at }`. Attachments
282
- (`AppCommentFile`) carry `id` / `filename` / `mime_type` plus `file_storage_key` (needed when
283
- re-sending files on edit). The `url` / `thumbnail_url` / `preview_url` fields exist on the type
282
+ (`AppCommentFile`) carry `id` / `filename` / `mime_type` a file's identity is its `id`, and
283
+ the server re-reads every attachment from storage by that id, so nothing else you hold about a
284
+ file can affect what is stored. The `url` / `thumbnail_url` / `preview_url` fields exist on the type
284
285
  but the server does not populate them today — render attachments by name and type (what
285
286
  `CommentList`'s default file row does), never by counting on a fetchable URL.
286
287
  - `createComment({ content, file_ids? })` — posts as the viewing member. `file_ids` come from
@@ -297,8 +298,11 @@ updateComment, deleteComment, refetch }`.
297
298
  member source: the `useMembers` roster (requires the member-access declaration above) or member
298
299
  cells in your own data. An unresolvable id should render a fallback (`CommentList` has an
299
300
  `unknownMember` label for exactly this).
300
- - **Freshness:** SWR-cached, revalidates on focus/reconnect; there is **no realtime push** to apps,
301
- so another viewer's comment appears on the next focus or explicit `refetch()`.
301
+ - **Freshness:** SWR-cached, revalidates on focus/reconnect, so another viewer's comment appears on
302
+ the next focus or explicit `refetch()` — not on its own. Comments are the one read that does not
303
+ keep itself current, and not because apps lack realtime: the channel carries **record** changes,
304
+ and a comment is its own entity that emits no record event. Your queries DO get pushed
305
+ ([data_fetching.md](./data_fetching.md)) — don't read this line as "no push" and build a poll.
302
306
 
303
307
  ### Counts
304
308
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/app-sdk",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "description": "Runtime SDK for Lotics custom-code apps \u2014 typed hooks, postMessage bridge, mount entry point",
5
5
  "type": "module",
6
6
  "exports": {