@lotics/app-sdk 0.30.0 → 0.32.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.
- package/dist/src/comments.d.ts +26 -0
- package/dist/src/comments.js +39 -11
- package/dist/src/hooks.d.ts +4 -3
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.js +2 -2
- package/dist/src/row.d.ts +9 -0
- package/dist/src/row.js +13 -0
- package/dist/src/rpc.d.ts +1 -1
- package/dist/src/rpc.js +1 -0
- package/package.json +2 -2
package/dist/src/comments.d.ts
CHANGED
|
@@ -69,3 +69,29 @@ export interface UseCommentsArgs {
|
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
71
|
export declare function useComments(args: UseCommentsArgs): CommentsState;
|
|
72
|
+
export interface CommentCountsState {
|
|
73
|
+
/** `{ record_id: count }`. Empty while loading or unavailable. */
|
|
74
|
+
counts: Record<string, number>;
|
|
75
|
+
/** True on the initial load only — false during background revalidation. */
|
|
76
|
+
loading: boolean;
|
|
77
|
+
error: string | null;
|
|
78
|
+
/** Same gate as `useComments` — member signed in AND the app declared `comments`. */
|
|
79
|
+
available: boolean;
|
|
80
|
+
/** Re-pull the counts (e.g. after posting a comment). */
|
|
81
|
+
refetch: () => void;
|
|
82
|
+
}
|
|
83
|
+
export interface UseCommentCountsArgs {
|
|
84
|
+
/** The table whose per-record comment counts to read. */
|
|
85
|
+
table_id: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Per-record comment counts for a whole table, as `{ record_id: count }` — for
|
|
89
|
+
* row badges (a count beside each row) without loading any comment content. One
|
|
90
|
+
* server-side `GROUP BY`. App-authority like {@link useComments}.
|
|
91
|
+
*
|
|
92
|
+
* ```tsx
|
|
93
|
+
* const { counts } = useCommentCounts({ table_id });
|
|
94
|
+
* // counts[row.__source_record_id] ?? 0
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function useCommentCounts(args: UseCommentCountsArgs): CommentCountsState;
|
package/dist/src/comments.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `useComments` — read and write the comments on a record from a custom-code app.
|
|
3
3
|
*
|
|
4
|
-
* Comments are a *members-only* surface
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* author identity, so they run under the VIEWING MEMBER's own authority instead:
|
|
4
|
+
* Comments are a *members-only* surface but, like `useQuery` / `useWorkflow`,
|
|
5
|
+
* run under the app's authority — NOT the member's table IAM. An app user with
|
|
6
|
+
* the app open (and the app declaring `comments`) may read/write comments on any
|
|
7
|
+
* record the app reaches, regardless of their own table access:
|
|
9
8
|
*
|
|
10
|
-
* - read / write
|
|
11
|
-
* - the comment's author is the
|
|
9
|
+
* - read / write gated by `app:use` + the `comments` capability (no table grant)
|
|
10
|
+
* - the comment's author is still the real viewing member (correct attribution)
|
|
12
11
|
* - edit / delete are author-only, checked against the viewer
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* `useCommentCounts` returns per-record counts for a table (row badges) — a
|
|
14
|
+
* single server-side `GROUP BY`, no comment content shipped.
|
|
15
|
+
*
|
|
16
|
+
* The bridged host (`app_iframe_host`) proxies each op to the app-scoped
|
|
17
|
+
* `/v1/apps/{id}/…/comments` endpoints. A standalone (public) app has no
|
|
18
|
+
* signed-in member: `available` is false, lists stay empty, and a mutation
|
|
19
|
+
* rejects. Check `available` before rendering a composer.
|
|
18
20
|
*
|
|
19
21
|
* Freshness mirrors `useQuery`: SWR-cached, revalidate on focus / reconnect, and
|
|
20
22
|
* an explicit refetch after every mutation. There is no realtime push to apps,
|
|
@@ -150,3 +152,29 @@ export function useComments(args) {
|
|
|
150
152
|
refetch,
|
|
151
153
|
};
|
|
152
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Per-record comment counts for a whole table, as `{ record_id: count }` — for
|
|
157
|
+
* row badges (a count beside each row) without loading any comment content. One
|
|
158
|
+
* server-side `GROUP BY`. App-authority like {@link useComments}.
|
|
159
|
+
*
|
|
160
|
+
* ```tsx
|
|
161
|
+
* const { counts } = useCommentCounts({ table_id });
|
|
162
|
+
* // counts[row.__source_record_id] ?? 0
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export function useCommentCounts(args) {
|
|
166
|
+
const { table_id } = args;
|
|
167
|
+
const { memberId, commentsEnabled, resolved } = useAppContext();
|
|
168
|
+
const available = memberId != null && commentsEnabled;
|
|
169
|
+
const swr = useSWR(available ? ["app-comment-counts", table_id] : null, () => rpc("comments.counts", { table_id }), { shouldRetryOnError: false });
|
|
170
|
+
const refetch = useCallback(() => {
|
|
171
|
+
void swr.mutate();
|
|
172
|
+
}, [swr]);
|
|
173
|
+
return {
|
|
174
|
+
counts: swr.data ?? {},
|
|
175
|
+
loading: available ? swr.isLoading : !resolved,
|
|
176
|
+
error: swr.error ? swr.error.message : null,
|
|
177
|
+
available,
|
|
178
|
+
refetch,
|
|
179
|
+
};
|
|
180
|
+
}
|
package/dist/src/hooks.d.ts
CHANGED
|
@@ -157,9 +157,10 @@ type UseWorkflowFn<K extends keyof AppWorkflows & string> = AppWorkflows[K] exte
|
|
|
157
157
|
* Result of an app-workflow run — the execute endpoint's response. `files` holds
|
|
158
158
|
* any document a workflow step generated (e.g. via a `generate_*_from_template`
|
|
159
159
|
* tool), resolved for download: read `files[0].url` and pass it to `openExternal`.
|
|
160
|
-
* A workflow that generates no file resolves with `files` absent.
|
|
161
|
-
*
|
|
162
|
-
*
|
|
160
|
+
* A workflow that generates no file resolves with `files` absent.
|
|
161
|
+
*
|
|
162
|
+
* `data` is the structured value the workflow returned via `return({ data })`,
|
|
163
|
+
* typed per the alias's declared `outputs` schema (`unknown` when none was declared).
|
|
163
164
|
*/
|
|
164
165
|
export interface WorkflowResult<TData = unknown> {
|
|
165
166
|
status: "success" | "error";
|
package/dist/src/index.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ export { mount } from "./mount.js";
|
|
|
18
18
|
export type { MountOptions } from "./mount.js";
|
|
19
19
|
export { useWorkflow, useQuery, useInfiniteQuery, usePaginatedQuery, useFileUpload, useMembers, } from "./hooks.js";
|
|
20
20
|
export type { UploadedFile, BaseQueryOptions, QueryOptions, InfiniteQueryOptions, PaginatedQueryOptions, QuerySortKey, QueryFilter, QueryFilterCondition, QueryFilterGroup, WorkflowResult, MembersOptions, } from "./hooks.js";
|
|
21
|
-
export { useComments } from "./comments.js";
|
|
22
|
-
export type { AppComment, AppCommentFile, CommentsState, UseCommentsArgs } from "./comments.js";
|
|
21
|
+
export { useComments, useCommentCounts } from "./comments.js";
|
|
22
|
+
export type { AppComment, AppCommentFile, CommentsState, UseCommentsArgs, CommentCountsState, UseCommentCountsArgs, } from "./comments.js";
|
|
23
23
|
export { useViewer } from "./viewer.js";
|
|
24
24
|
export { requestGeofencedLocation, isWithinZone } from "./geolocation.js";
|
|
25
25
|
export type { GeofenceZone, GeoCoords, GeofenceOutcome, GeofenceOptions } from "./geolocation.js";
|
|
@@ -32,7 +32,7 @@ export { readSelect } from "./select.js";
|
|
|
32
32
|
export type { ResolvedOption } from "./select.js";
|
|
33
33
|
export type { AppFixture } from "./mock.js";
|
|
34
34
|
export type { AppWorkflows, AppQueries } from "./types.js";
|
|
35
|
-
export { row, readLinks, readFiles } from "./row.js";
|
|
35
|
+
export { row, readLinks, readFiles, readLocked } from "./row.js";
|
|
36
36
|
export type { ResolvedLink, AppFile } from "./row.js";
|
|
37
37
|
export { useOptimistic } from "./use_optimistic.js";
|
|
38
38
|
export type { OptimisticApi } from "./use_optimistic.js";
|
package/dist/src/index.js
CHANGED
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export { mount } from "./mount.js";
|
|
18
18
|
export { useWorkflow, useQuery, useInfiniteQuery, usePaginatedQuery, useFileUpload, useMembers, } from "./hooks.js";
|
|
19
|
-
export { useComments } from "./comments.js";
|
|
19
|
+
export { useComments, useCommentCounts } from "./comments.js";
|
|
20
20
|
export { useViewer } from "./viewer.js";
|
|
21
21
|
export { requestGeofencedLocation, isWithinZone } from "./geolocation.js";
|
|
22
22
|
export { rpc } from "./rpc.js";
|
|
23
23
|
export { openExternal } from "./open_external.js";
|
|
24
24
|
export { readMembers } from "./members.js";
|
|
25
25
|
export { readSelect } from "./select.js";
|
|
26
|
-
export { row, readLinks, readFiles } from "./row.js";
|
|
26
|
+
export { row, readLinks, readFiles, readLocked } from "./row.js";
|
|
27
27
|
export { useOptimistic } from "./use_optimistic.js";
|
|
28
28
|
export { useRecents } from "./use_recents.js";
|
package/dist/src/row.d.ts
CHANGED
|
@@ -65,6 +65,15 @@ export interface AppFile {
|
|
|
65
65
|
* an unservable file. Map to `@lotics/ui` `DisplayFile` for FileThumbnail/Gallery.
|
|
66
66
|
*/
|
|
67
67
|
export declare function readFiles(v: unknown): AppFile[];
|
|
68
|
+
/**
|
|
69
|
+
* Record lock state for a `useQuery` row. The query layer emits a row-level
|
|
70
|
+
* `__source_locked` addressing column (alongside `__source_record_id`). A locked
|
|
71
|
+
* record rejects direct writes, so an app reads this to show a locked state and
|
|
72
|
+
* route edits through a `request_locked_field_change` workflow instead of an
|
|
73
|
+
* `update_records` save. Pass the whole row (not a cell). False for any
|
|
74
|
+
* non-object / absent flag.
|
|
75
|
+
*/
|
|
76
|
+
export declare function readLocked(rowValue: unknown): boolean;
|
|
68
77
|
export declare const row: {
|
|
69
78
|
opt: typeof opt;
|
|
70
79
|
text: typeof text;
|
package/dist/src/row.js
CHANGED
|
@@ -118,4 +118,17 @@ export function readFiles(v) {
|
|
|
118
118
|
}
|
|
119
119
|
return out;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Record lock state for a `useQuery` row. The query layer emits a row-level
|
|
123
|
+
* `__source_locked` addressing column (alongside `__source_record_id`). A locked
|
|
124
|
+
* record rejects direct writes, so an app reads this to show a locked state and
|
|
125
|
+
* route edits through a `request_locked_field_change` workflow instead of an
|
|
126
|
+
* `update_records` save. Pass the whole row (not a cell). False for any
|
|
127
|
+
* non-object / absent flag.
|
|
128
|
+
*/
|
|
129
|
+
export function readLocked(rowValue) {
|
|
130
|
+
if (!rowValue || typeof rowValue !== "object")
|
|
131
|
+
return false;
|
|
132
|
+
return bool(rowValue["__source_locked"]);
|
|
133
|
+
}
|
|
121
134
|
export const row = { opt, text, num, bool, date, link };
|
package/dist/src/rpc.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* app → host: { id, op, payload }
|
|
19
19
|
* host → app: { id, type: "result", data } | { id, type: "error", message }
|
|
20
20
|
*/
|
|
21
|
-
export type RpcOp = "query" | "workflow" | "upload" | "members" | "context" | "openExternal" | "comments.list" | "comments.create" | "comments.update" | "comments.delete";
|
|
21
|
+
export type RpcOp = "query" | "workflow" | "upload" | "members" | "context" | "openExternal" | "comments.list" | "comments.create" | "comments.update" | "comments.delete" | "comments.counts";
|
|
22
22
|
/**
|
|
23
23
|
* The app's identity, resolved once at startup to tag PostHog events.
|
|
24
24
|
* Assembled by whichever transport is active:
|
package/dist/src/rpc.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/app-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Runtime SDK for Lotics custom-code apps — typed hooks, postMessage bridge, mount entry point",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
"url": "https://github.com/lotics/lotics.git",
|
|
47
47
|
"directory": "packages/app-sdk"
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|