@agent-native/core 0.135.0 → 0.135.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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/sharing/access.ts +44 -2
- package/corpus/templates/content/actions/_database-utils.ts +113 -4
- package/corpus/templates/content/actions/get-document.ts +84 -3
- package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +9 -2
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +29 -13
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +5 -12
- package/corpus/templates/content/app/components/editor/body-hydration.ts +12 -6
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2 -3
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -12
- package/corpus/templates/content/app/hooks/use-content-database.ts +16 -27
- package/corpus/templates/content/app/hooks/use-create-page.ts +3 -6
- package/corpus/templates/content/app/hooks/use-document-properties.ts +9 -16
- package/corpus/templates/content/app/hooks/use-document-versions.ts +3 -3
- package/corpus/templates/content/app/hooks/use-documents.ts +85 -29
- package/corpus/templates/content/app/hooks/use-notion.ts +15 -13
- package/corpus/templates/content/app/i18n-data.ts +32 -1
- package/corpus/templates/content/app/lib/document-query.ts +36 -0
- package/corpus/templates/content/changelog/2026-08-02-pages-opened-from-a-database-now-keep-that-database-s-fields.md +6 -0
- package/corpus/templates/content/server/lib/document-context.ts +11 -6
- package/corpus/templates/content/shared/api.ts +8 -0
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +32 -2
- package/dist/sharing/access.js.map +1 -1
- package/package.json +1 -1
- package/src/sharing/access.ts +44 -2
|
@@ -57,6 +57,8 @@ import type {
|
|
|
57
57
|
import type { Query, QueryClient } from "@tanstack/react-query";
|
|
58
58
|
import { useQueryClient } from "@tanstack/react-query";
|
|
59
59
|
|
|
60
|
+
import { documentQueryFilter } from "../lib/document-query";
|
|
61
|
+
|
|
60
62
|
export function contentDatabaseQueryKey(documentId: string) {
|
|
61
63
|
return ["action", "get-content-database", { documentId }] as const;
|
|
62
64
|
}
|
|
@@ -421,9 +423,7 @@ export function clearDeletedContentDatabaseFromCache(
|
|
|
421
423
|
documentId: string,
|
|
422
424
|
) {
|
|
423
425
|
queryClient.removeQueries(contentDatabaseQueryFilter(documentId));
|
|
424
|
-
queryClient.removeQueries(
|
|
425
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
426
|
-
});
|
|
426
|
+
queryClient.removeQueries(documentQueryFilter(documentId));
|
|
427
427
|
queryClient.invalidateQueries({
|
|
428
428
|
queryKey: ["action", "get-content-database"],
|
|
429
429
|
});
|
|
@@ -713,20 +713,14 @@ export function useCreateContentDatabase(documentId: string | null) {
|
|
|
713
713
|
{
|
|
714
714
|
onSuccess: (data) => {
|
|
715
715
|
if (documentId) {
|
|
716
|
-
queryClient.invalidateQueries(
|
|
717
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
718
|
-
});
|
|
716
|
+
queryClient.invalidateQueries(documentQueryFilter(documentId));
|
|
719
717
|
queryClient.invalidateQueries({
|
|
720
718
|
queryKey: contentDatabaseQueryKey(documentId),
|
|
721
719
|
});
|
|
722
720
|
}
|
|
723
|
-
queryClient.invalidateQueries(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
"get-document",
|
|
727
|
-
{ id: data.database.documentId },
|
|
728
|
-
],
|
|
729
|
-
});
|
|
721
|
+
queryClient.invalidateQueries(
|
|
722
|
+
documentQueryFilter(data.database.documentId),
|
|
723
|
+
);
|
|
730
724
|
queryClient.invalidateQueries({
|
|
731
725
|
queryKey: ["action", "list-documents"],
|
|
732
726
|
});
|
|
@@ -743,17 +737,11 @@ export function useCreateInlineContentDatabase(hostDocumentId: string | null) {
|
|
|
743
737
|
>("create-inline-content-database", {
|
|
744
738
|
onSuccess: (data) => {
|
|
745
739
|
if (hostDocumentId) {
|
|
746
|
-
queryClient.invalidateQueries(
|
|
747
|
-
queryKey: ["action", "get-document", { id: hostDocumentId }],
|
|
748
|
-
});
|
|
740
|
+
queryClient.invalidateQueries(documentQueryFilter(hostDocumentId));
|
|
749
741
|
}
|
|
750
|
-
queryClient.invalidateQueries(
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
"get-document",
|
|
754
|
-
{ id: data.block.databaseDocumentId },
|
|
755
|
-
],
|
|
756
|
-
});
|
|
742
|
+
queryClient.invalidateQueries(
|
|
743
|
+
documentQueryFilter(data.block.databaseDocumentId),
|
|
744
|
+
);
|
|
757
745
|
queryClient.invalidateQueries({
|
|
758
746
|
queryKey: contentDatabaseQueryKey(data.block.databaseDocumentId),
|
|
759
747
|
});
|
|
@@ -1488,7 +1476,10 @@ export function useProcessBuilderBodyHydration(documentId: string) {
|
|
|
1488
1476
|
|
|
1489
1477
|
export function invalidateBuilderBodyHydrationQueries(
|
|
1490
1478
|
queryClient: {
|
|
1491
|
-
invalidateQueries: (filters: {
|
|
1479
|
+
invalidateQueries: (filters: {
|
|
1480
|
+
queryKey?: readonly unknown[];
|
|
1481
|
+
predicate?: (query: { queryKey: readonly unknown[] }) => boolean;
|
|
1482
|
+
}) => unknown;
|
|
1492
1483
|
},
|
|
1493
1484
|
documentId: string,
|
|
1494
1485
|
variables?: Pick<ProcessBuilderBodyHydrationRequest, "documentId"> | null,
|
|
@@ -1501,9 +1492,7 @@ export function invalidateBuilderBodyHydrationQueries(
|
|
|
1501
1492
|
queryKey: ["action", "get-content-database-source", { documentId }],
|
|
1502
1493
|
});
|
|
1503
1494
|
if (variables?.documentId) {
|
|
1504
|
-
queryClient.invalidateQueries(
|
|
1505
|
-
queryKey: ["action", "get-document", { id: variables.documentId }],
|
|
1506
|
-
});
|
|
1495
|
+
queryClient.invalidateQueries(documentQueryFilter(variables.documentId));
|
|
1507
1496
|
}
|
|
1508
1497
|
}
|
|
1509
1498
|
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { useContentSpaces } from "@/hooks/use-content-spaces";
|
|
13
13
|
import { useCreateDocument } from "@/hooks/use-documents";
|
|
14
14
|
import { useLocalStorage } from "@/hooks/use-local-storage";
|
|
15
|
+
import { documentQueryFilter } from "@/lib/document-query";
|
|
15
16
|
import { markDocumentCreationPending } from "@/lib/optimistic-document";
|
|
16
17
|
|
|
17
18
|
const LIST_DOCUMENTS_QUERY_KEY = [
|
|
@@ -103,9 +104,7 @@ export function useCreatePage(opts?: {
|
|
|
103
104
|
);
|
|
104
105
|
// Replace optimistic doc with real server doc + clear any 404 error
|
|
105
106
|
// state from the in-flight fetch that ran before create completed.
|
|
106
|
-
queryClient.invalidateQueries(
|
|
107
|
-
queryKey: ["action", "get-document", { id }],
|
|
108
|
-
});
|
|
107
|
+
queryClient.invalidateQueries(documentQueryFilter(id));
|
|
109
108
|
queryClient.invalidateQueries({
|
|
110
109
|
queryKey: ["action", "list-documents"],
|
|
111
110
|
});
|
|
@@ -115,9 +114,7 @@ export function useCreatePage(opts?: {
|
|
|
115
114
|
queryClient.invalidateQueries({
|
|
116
115
|
queryKey: ["action", "list-documents"],
|
|
117
116
|
});
|
|
118
|
-
queryClient.removeQueries(
|
|
119
|
-
queryKey: ["action", "get-document", { id }],
|
|
120
|
-
});
|
|
117
|
+
queryClient.removeQueries(documentQueryFilter(id));
|
|
121
118
|
if (shouldNavigate) navigate("/");
|
|
122
119
|
toast.error("Failed to create page", {
|
|
123
120
|
description:
|
|
@@ -22,7 +22,10 @@ import {
|
|
|
22
22
|
contentDatabaseQueryKey,
|
|
23
23
|
removeDocumentPropertyFromDatabaseResponse,
|
|
24
24
|
} from "./use-content-database";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
documentPropertiesQueryKey,
|
|
27
|
+
documentQueryFilter,
|
|
28
|
+
} from "./use-documents";
|
|
26
29
|
|
|
27
30
|
type DatabaseScopedRequest = { databaseId: string };
|
|
28
31
|
|
|
@@ -91,9 +94,7 @@ export function useConfigureDocumentProperty(
|
|
|
91
94
|
queryClient.invalidateQueries({
|
|
92
95
|
queryKey: documentPropertiesQueryKey(documentId, databaseId),
|
|
93
96
|
});
|
|
94
|
-
queryClient.invalidateQueries(
|
|
95
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
96
|
-
});
|
|
97
|
+
queryClient.invalidateQueries(documentQueryFilter(documentId));
|
|
97
98
|
queryClient.invalidateQueries(
|
|
98
99
|
contentDatabaseConstrainedQueryFilter(databaseDocumentId),
|
|
99
100
|
);
|
|
@@ -158,9 +159,7 @@ export function useSetDocumentProperty(
|
|
|
158
159
|
queryClient.invalidateQueries({
|
|
159
160
|
queryKey: documentPropertiesQueryKey(variables.documentId, databaseId),
|
|
160
161
|
});
|
|
161
|
-
queryClient.invalidateQueries(
|
|
162
|
-
queryKey: ["action", "get-document", { id: variables.documentId }],
|
|
163
|
-
});
|
|
162
|
+
queryClient.invalidateQueries(documentQueryFilter(variables.documentId));
|
|
164
163
|
queryClient.invalidateQueries(
|
|
165
164
|
contentDatabaseConstrainedQueryFilter(databaseDocumentId),
|
|
166
165
|
);
|
|
@@ -195,9 +194,7 @@ export function useDuplicateDocumentProperty(
|
|
|
195
194
|
queryClient.invalidateQueries({
|
|
196
195
|
queryKey: documentPropertiesQueryKey(documentId, databaseId),
|
|
197
196
|
});
|
|
198
|
-
queryClient.invalidateQueries(
|
|
199
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
200
|
-
});
|
|
197
|
+
queryClient.invalidateQueries(documentQueryFilter(documentId));
|
|
201
198
|
queryClient.invalidateQueries({
|
|
202
199
|
...contentDatabaseQueryFilter(databaseDocumentId),
|
|
203
200
|
});
|
|
@@ -220,9 +217,7 @@ export function useReorderDocumentProperty(
|
|
|
220
217
|
queryClient.invalidateQueries({
|
|
221
218
|
queryKey: documentPropertiesQueryKey(documentId, databaseId),
|
|
222
219
|
});
|
|
223
|
-
queryClient.invalidateQueries(
|
|
224
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
225
|
-
});
|
|
220
|
+
queryClient.invalidateQueries(documentQueryFilter(documentId));
|
|
226
221
|
queryClient.invalidateQueries({
|
|
227
222
|
queryKey: contentDatabaseQueryKey(databaseDocumentId),
|
|
228
223
|
});
|
|
@@ -277,9 +272,7 @@ export function useDeleteDocumentProperty(
|
|
|
277
272
|
queryClient.invalidateQueries({
|
|
278
273
|
queryKey: documentPropertiesQueryKey(documentId, databaseId),
|
|
279
274
|
});
|
|
280
|
-
queryClient.invalidateQueries(
|
|
281
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
282
|
-
});
|
|
275
|
+
queryClient.invalidateQueries(documentQueryFilter(documentId));
|
|
283
276
|
queryClient.invalidateQueries({
|
|
284
277
|
...contentDatabaseQueryFilter(databaseDocumentId),
|
|
285
278
|
});
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
import type { Document, DocumentVersion } from "@shared/api";
|
|
6
6
|
import { useQueryClient } from "@tanstack/react-query";
|
|
7
7
|
|
|
8
|
+
import { documentQueryFilter } from "./use-documents";
|
|
9
|
+
|
|
8
10
|
export function useDocumentVersions(documentId: string | null) {
|
|
9
11
|
return useActionQuery<DocumentVersion[]>(
|
|
10
12
|
"list-document-versions",
|
|
@@ -29,9 +31,7 @@ export function useRestoreDocumentVersion(documentId: string) {
|
|
|
29
31
|
queryClient.invalidateQueries({
|
|
30
32
|
queryKey: ["action", "list-document-versions", { documentId }],
|
|
31
33
|
});
|
|
32
|
-
queryClient.invalidateQueries(
|
|
33
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
34
|
-
});
|
|
34
|
+
queryClient.invalidateQueries(documentQueryFilter(documentId));
|
|
35
35
|
},
|
|
36
36
|
},
|
|
37
37
|
);
|
|
@@ -19,23 +19,50 @@ import { useQueryClient } from "@tanstack/react-query";
|
|
|
19
19
|
import { toast } from "sonner";
|
|
20
20
|
|
|
21
21
|
import type { DocumentUpdateConflictResponse } from "../../actions/update-document";
|
|
22
|
+
import {
|
|
23
|
+
documentQueryFilter,
|
|
24
|
+
documentQueryKey,
|
|
25
|
+
type DocumentQueryContext,
|
|
26
|
+
} from "../lib/document-query";
|
|
22
27
|
import {
|
|
23
28
|
removeOptimisticItemFromContentDatabase,
|
|
24
29
|
useRestoreContentDatabase,
|
|
25
30
|
} from "./use-content-database";
|
|
26
31
|
|
|
32
|
+
export {
|
|
33
|
+
documentQueryFilter,
|
|
34
|
+
documentQueryKey,
|
|
35
|
+
type DocumentQueryContext,
|
|
36
|
+
} from "../lib/document-query";
|
|
37
|
+
|
|
27
38
|
export type { DocumentUpdateConflictResponse };
|
|
28
39
|
|
|
40
|
+
export type PageOwnedDocumentCachePatch = Pick<
|
|
41
|
+
Partial<Document>,
|
|
42
|
+
| "id"
|
|
43
|
+
| "parentId"
|
|
44
|
+
| "title"
|
|
45
|
+
| "content"
|
|
46
|
+
| "description"
|
|
47
|
+
| "icon"
|
|
48
|
+
| "position"
|
|
49
|
+
| "isFavorite"
|
|
50
|
+
| "hideFromSearch"
|
|
51
|
+
| "visibility"
|
|
52
|
+
| "accessRole"
|
|
53
|
+
| "canEdit"
|
|
54
|
+
| "canManage"
|
|
55
|
+
| "source"
|
|
56
|
+
| "createdAt"
|
|
57
|
+
| "updatedAt"
|
|
58
|
+
>;
|
|
59
|
+
|
|
29
60
|
export const LIST_DOCUMENTS_QUERY_KEY = [
|
|
30
61
|
"action",
|
|
31
62
|
"list-documents",
|
|
32
63
|
undefined,
|
|
33
64
|
] as const;
|
|
34
65
|
|
|
35
|
-
export function documentQueryKey(documentId: string) {
|
|
36
|
-
return ["action", "get-document", { id: documentId }] as const;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
66
|
export function documentPropertiesQueryKey(
|
|
40
67
|
documentId: string,
|
|
41
68
|
databaseId: string,
|
|
@@ -74,7 +101,27 @@ export function mergeDocumentIntoDocumentCache(
|
|
|
74
101
|
old: unknown,
|
|
75
102
|
document: Document,
|
|
76
103
|
) {
|
|
77
|
-
|
|
104
|
+
const pageOwnedPatch: PageOwnedDocumentCachePatch = {
|
|
105
|
+
id: document.id,
|
|
106
|
+
parentId: document.parentId,
|
|
107
|
+
title: document.title,
|
|
108
|
+
content: document.content,
|
|
109
|
+
description: document.description,
|
|
110
|
+
icon: document.icon,
|
|
111
|
+
position: document.position,
|
|
112
|
+
isFavorite: document.isFavorite,
|
|
113
|
+
hideFromSearch: document.hideFromSearch,
|
|
114
|
+
visibility: document.visibility,
|
|
115
|
+
accessRole: document.accessRole,
|
|
116
|
+
canEdit: document.canEdit,
|
|
117
|
+
canManage: document.canManage,
|
|
118
|
+
source: document.source,
|
|
119
|
+
createdAt: document.createdAt,
|
|
120
|
+
updatedAt: document.updatedAt,
|
|
121
|
+
};
|
|
122
|
+
return old && typeof old === "object"
|
|
123
|
+
? { ...old, ...pageOwnedPatch }
|
|
124
|
+
: pageOwnedPatch;
|
|
78
125
|
}
|
|
79
126
|
|
|
80
127
|
export function mergeDocumentIntoListDocumentsCache(
|
|
@@ -157,9 +204,9 @@ function patchDocumentWithFavoriteMembershipInDatabaseCache(
|
|
|
157
204
|
export function patchDocumentCaches(
|
|
158
205
|
queryClient: Pick<QueryClient, "setQueryData" | "setQueriesData">,
|
|
159
206
|
documentId: string,
|
|
160
|
-
patch:
|
|
207
|
+
patch: PageOwnedDocumentCachePatch,
|
|
161
208
|
) {
|
|
162
|
-
queryClient.
|
|
209
|
+
queryClient.setQueriesData(documentQueryFilter(documentId), (old: unknown) =>
|
|
163
210
|
old && typeof old === "object" ? { ...old, ...patch } : old,
|
|
164
211
|
);
|
|
165
212
|
queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, (old: unknown) =>
|
|
@@ -219,7 +266,7 @@ export function patchContentSpaceNameCaches(
|
|
|
219
266
|
export function documentUpdateSuccessPatch(
|
|
220
267
|
data: DocumentUpdateResponse,
|
|
221
268
|
variables: DocumentUpdateRequestWithCas,
|
|
222
|
-
):
|
|
269
|
+
): PageOwnedDocumentCachePatch {
|
|
223
270
|
return {
|
|
224
271
|
updatedAt: data.updatedAt,
|
|
225
272
|
...(variables.title !== undefined ? { title: data.title } : {}),
|
|
@@ -289,13 +336,28 @@ export const DOCUMENT_QUERY_FRESHNESS_OPTIONS = {
|
|
|
289
336
|
retry: false,
|
|
290
337
|
};
|
|
291
338
|
|
|
292
|
-
export function useDocument(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
339
|
+
export function useDocument(
|
|
340
|
+
id: string | null,
|
|
341
|
+
context: DocumentQueryContext = {},
|
|
342
|
+
) {
|
|
343
|
+
return useActionQuery<Document>(
|
|
344
|
+
"get-document",
|
|
345
|
+
id
|
|
346
|
+
? {
|
|
347
|
+
id,
|
|
348
|
+
...(context.databaseId ? { databaseId: context.databaseId } : {}),
|
|
349
|
+
...(context.databaseDocumentId
|
|
350
|
+
? { databaseDocumentId: context.databaseDocumentId }
|
|
351
|
+
: {}),
|
|
352
|
+
}
|
|
353
|
+
: undefined,
|
|
354
|
+
{
|
|
355
|
+
enabled: !!id,
|
|
356
|
+
// Doc-not-found / no-access errors are deterministic — retrying just keeps
|
|
357
|
+
// the spinner up for ~7s before the UI can render "Not found".
|
|
358
|
+
...DOCUMENT_QUERY_FRESHNESS_OPTIONS,
|
|
359
|
+
},
|
|
360
|
+
);
|
|
299
361
|
}
|
|
300
362
|
|
|
301
363
|
export interface PreviewDocumentDraftRecord {
|
|
@@ -368,7 +430,7 @@ export function useUpdateDocument() {
|
|
|
368
430
|
};
|
|
369
431
|
if (Object.keys(optimisticPatch).length === 0) return undefined;
|
|
370
432
|
|
|
371
|
-
const
|
|
433
|
+
const documentFilter = documentQueryFilter(variables.id);
|
|
372
434
|
const databaseFilter = {
|
|
373
435
|
queryKey: ["action", "get-content-database"],
|
|
374
436
|
} as const;
|
|
@@ -376,14 +438,14 @@ export function useUpdateDocument() {
|
|
|
376
438
|
queryKey: ["action", "list-content-spaces"],
|
|
377
439
|
} as const;
|
|
378
440
|
await Promise.all([
|
|
379
|
-
queryClient.cancelQueries(
|
|
441
|
+
queryClient.cancelQueries(documentFilter),
|
|
380
442
|
queryClient.cancelQueries({ queryKey: LIST_DOCUMENTS_QUERY_KEY }),
|
|
381
443
|
queryClient.cancelQueries(databaseFilter),
|
|
382
444
|
queryClient.cancelQueries(contentSpacesFilter),
|
|
383
445
|
]);
|
|
384
446
|
|
|
385
447
|
const previous: Array<[readonly unknown[], unknown]> = [
|
|
386
|
-
|
|
448
|
+
...queryClient.getQueriesData(documentFilter),
|
|
387
449
|
[
|
|
388
450
|
LIST_DOCUMENTS_QUERY_KEY,
|
|
389
451
|
queryClient.getQueryData(LIST_DOCUMENTS_QUERY_KEY),
|
|
@@ -423,8 +485,8 @@ export function useUpdateDocument() {
|
|
|
423
485
|
// just-applied write.
|
|
424
486
|
if (isDocumentUpdateConflict(data)) {
|
|
425
487
|
const serverDocument = data.document;
|
|
426
|
-
queryClient.
|
|
427
|
-
|
|
488
|
+
queryClient.setQueriesData(
|
|
489
|
+
documentQueryFilter(variables.id),
|
|
428
490
|
(old: unknown) =>
|
|
429
491
|
mergeDocumentIntoDocumentCache(old, serverDocument),
|
|
430
492
|
);
|
|
@@ -453,9 +515,7 @@ export function useUpdateDocument() {
|
|
|
453
515
|
queryKey: ["action", "get-content-database"],
|
|
454
516
|
});
|
|
455
517
|
}
|
|
456
|
-
queryClient.invalidateQueries(
|
|
457
|
-
queryKey: ["action", "get-document", { id: variables.id }],
|
|
458
|
-
});
|
|
518
|
+
queryClient.invalidateQueries(documentQueryFilter(variables.id));
|
|
459
519
|
queryClient.invalidateQueries({
|
|
460
520
|
queryKey: ["action", "list-documents"],
|
|
461
521
|
});
|
|
@@ -525,9 +585,7 @@ export function useDeleteDocument() {
|
|
|
525
585
|
queryClient.invalidateQueries({
|
|
526
586
|
queryKey: ["action", "list-documents"],
|
|
527
587
|
});
|
|
528
|
-
queryClient.invalidateQueries(
|
|
529
|
-
queryKey: ["action", "get-document", { id: variables.id }],
|
|
530
|
-
});
|
|
588
|
+
queryClient.invalidateQueries(documentQueryFilter(variables.id));
|
|
531
589
|
queryClient.invalidateQueries({
|
|
532
590
|
queryKey: ["action", "get-content-database"],
|
|
533
591
|
});
|
|
@@ -606,9 +664,7 @@ export function useMoveDocument() {
|
|
|
606
664
|
queryClient.invalidateQueries({
|
|
607
665
|
queryKey: ["action", "list-documents"],
|
|
608
666
|
});
|
|
609
|
-
queryClient.invalidateQueries(
|
|
610
|
-
queryKey: ["action", "get-document", { id: variables.id }],
|
|
611
|
-
});
|
|
667
|
+
queryClient.invalidateQueries(documentQueryFilter(variables.id));
|
|
612
668
|
},
|
|
613
669
|
},
|
|
614
670
|
);
|
|
@@ -16,6 +16,8 @@ import type {
|
|
|
16
16
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
17
17
|
import { useEffect, useRef } from "react";
|
|
18
18
|
|
|
19
|
+
import { documentQueryFilter } from "./use-documents";
|
|
20
|
+
|
|
19
21
|
// The server signs a `redirect` query param into the OAuth `state` and the
|
|
20
22
|
// callback route sends the user back there once the connection completes. If
|
|
21
23
|
// we never send it, `state.redirectPath` defaults to "/" server-side and
|
|
@@ -55,9 +57,7 @@ export function invalidateDocumentQueries(
|
|
|
55
57
|
// path after every debounced editor save). Invalidating the bare ["action"]
|
|
56
58
|
// key would refetch every mounted query app-wide (sidebar tree, comments,
|
|
57
59
|
// database views, search, connection status, ...) on each cycle.
|
|
58
|
-
queryClient.invalidateQueries(
|
|
59
|
-
queryKey: ["action", "get-document", { id: documentId }],
|
|
60
|
-
});
|
|
60
|
+
queryClient.invalidateQueries(documentQueryFilter(documentId));
|
|
61
61
|
queryClient.invalidateQueries({
|
|
62
62
|
queryKey: ["action", "list-documents"],
|
|
63
63
|
});
|
|
@@ -155,21 +155,23 @@ export function useDocumentSyncStatus(
|
|
|
155
155
|
|
|
156
156
|
lastObservedSyncedAtRef.current = query.data.lastSyncedAt;
|
|
157
157
|
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
]);
|
|
158
|
+
const cachedDocuments = queryClient
|
|
159
|
+
.getQueriesData<Document>(documentQueryFilter(normalizedDocumentId))
|
|
160
|
+
.map(([, document]) => document)
|
|
161
|
+
.filter((document): document is Document => !!document);
|
|
163
162
|
const syncedLocalUpdatedAt = query.data.lastPushedLocalUpdatedAt;
|
|
164
163
|
|
|
165
164
|
if (
|
|
166
|
-
|
|
165
|
+
cachedDocuments.some(
|
|
166
|
+
(cachedDocument) =>
|
|
167
|
+
!!cachedDocument.updatedAt &&
|
|
168
|
+
!!syncedLocalUpdatedAt &&
|
|
169
|
+
syncedLocalUpdatedAt > cachedDocument.updatedAt,
|
|
170
|
+
) &&
|
|
167
171
|
syncedLocalUpdatedAt &&
|
|
168
|
-
|
|
172
|
+
cachedDocuments.length > 0
|
|
169
173
|
) {
|
|
170
|
-
queryClient.invalidateQueries(
|
|
171
|
-
queryKey: ["action", "get-document", { id: normalizedDocumentId }],
|
|
172
|
-
});
|
|
174
|
+
queryClient.invalidateQueries(documentQueryFilter(normalizedDocumentId));
|
|
173
175
|
queryClient.invalidateQueries({ queryKey: ["action", "list-documents"] });
|
|
174
176
|
}
|
|
175
177
|
}, [
|
|
@@ -3120,9 +3120,12 @@ const enUS = {
|
|
|
3120
3120
|
liveDocumentSaveBeforeSyncFailed:
|
|
3121
3121
|
"The live document could not be saved before syncing.",
|
|
3122
3122
|
documentTitle: "Document title",
|
|
3123
|
-
builderBodySyncing: "
|
|
3123
|
+
builderBodySyncing: "This page's content is still syncing from Builder",
|
|
3124
3124
|
builderBodySyncingDescription:
|
|
3125
3125
|
"Editing is paused until the Builder body finishes syncing, so the existing article content is not overwritten.",
|
|
3126
|
+
pageBodySyncing: "This page's content is still syncing",
|
|
3127
|
+
pageBodySyncingDescription:
|
|
3128
|
+
"Editing is paused until the page body finishes syncing, so existing content is not overwritten.",
|
|
3126
3129
|
localFileSavedHistoryNotUpdated:
|
|
3127
3130
|
"Local file saved, but history was not updated",
|
|
3128
3131
|
reorderField: "Reorder {{name}}",
|
|
@@ -5615,6 +5618,9 @@ const editorMessagesByLocale = {
|
|
|
5615
5618
|
builderBodySyncing: "内容仍在从 Builder 同步",
|
|
5616
5619
|
builderBodySyncingDescription:
|
|
5617
5620
|
"同步 Builder 正文完成前会暂停编辑,避免覆盖现有文章内容。",
|
|
5621
|
+
pageBodySyncing: "此页面的内容仍在同步",
|
|
5622
|
+
pageBodySyncingDescription:
|
|
5623
|
+
"在页面正文完成同步之前,编辑会暂停,以免覆盖现有内容。",
|
|
5618
5624
|
creatingDatabase: "正在创建内联数据库...",
|
|
5619
5625
|
databaseCreated: "内联数据库已创建",
|
|
5620
5626
|
emptyBlockPlaceholder: "按“/”使用命令",
|
|
@@ -5960,6 +5966,9 @@ const editorMessagesByLocale = {
|
|
|
5960
5966
|
builderBodySyncing: "El contenido aún se está sincronizando desde Builder",
|
|
5961
5967
|
builderBodySyncingDescription:
|
|
5962
5968
|
"La edición está en pausa hasta que el cuerpo de Builder termine de sincronizarse, para no sobrescribir el contenido existente del artículo.",
|
|
5969
|
+
pageBodySyncing: "El contenido de esta página aún se está sincronizando",
|
|
5970
|
+
pageBodySyncingDescription:
|
|
5971
|
+
"La edición está en pausa hasta que el contenido de la página termine de sincronizarse, para no sobrescribir el contenido existente.",
|
|
5963
5972
|
creatingDatabase: "Creando base de datos integrada...",
|
|
5964
5973
|
databaseCreated: "Base de datos integrada creada",
|
|
5965
5974
|
emptyBlockPlaceholder: "Pulsa «/» para ver los comandos",
|
|
@@ -6317,6 +6326,10 @@ const editorMessagesByLocale = {
|
|
|
6317
6326
|
"Le contenu est encore en cours de synchronisation depuis Builder",
|
|
6318
6327
|
builderBodySyncingDescription:
|
|
6319
6328
|
"La modification est suspendue jusqu'à la fin de la synchronisation du corps Builder, afin de ne pas écraser le contenu existant de l'article.",
|
|
6329
|
+
pageBodySyncing:
|
|
6330
|
+
"Le contenu de cette page est encore en cours de synchronisation",
|
|
6331
|
+
pageBodySyncingDescription:
|
|
6332
|
+
"La modification est suspendue jusqu'à la fin de la synchronisation du contenu de la page, afin de ne pas écraser le contenu existant.",
|
|
6320
6333
|
creatingDatabase: "Création d'une base de données intégrée...",
|
|
6321
6334
|
databaseCreated: "Base de données intégrée créée",
|
|
6322
6335
|
emptyBlockPlaceholder: "Appuyez sur « / » pour afficher les commandes",
|
|
@@ -6676,6 +6689,9 @@ const editorMessagesByLocale = {
|
|
|
6676
6689
|
builderBodySyncing: "Inhalte werden noch von Builder synchronisiert",
|
|
6677
6690
|
builderBodySyncingDescription:
|
|
6678
6691
|
"Die Bearbeitung ist pausiert, bis der Builder-Textkörper fertig synchronisiert ist, damit der bestehende Artikelinhalt nicht überschrieben wird.",
|
|
6692
|
+
pageBodySyncing: "Der Inhalt dieser Seite wird noch synchronisiert",
|
|
6693
|
+
pageBodySyncingDescription:
|
|
6694
|
+
"Die Bearbeitung ist pausiert, bis der Seiteninhalt fertig synchronisiert ist, damit bestehende Inhalte nicht überschrieben werden.",
|
|
6679
6695
|
creatingDatabase: "Inline-Datenbank wird erstellt...",
|
|
6680
6696
|
databaseCreated: "Inline-Datenbank erstellt",
|
|
6681
6697
|
emptyBlockPlaceholder: "Drücke „/“ für Befehle",
|
|
@@ -7040,6 +7056,9 @@ const editorMessagesByLocale = {
|
|
|
7040
7056
|
builderBodySyncing: "コンテンツはまだ Builder から同期中です",
|
|
7041
7057
|
builderBodySyncingDescription:
|
|
7042
7058
|
"既存の記事内容を上書きしないよう、Builder 本文の同期が完了するまで編集は一時停止されます。",
|
|
7059
|
+
pageBodySyncing: "このページのコンテンツはまだ同期中です",
|
|
7060
|
+
pageBodySyncingDescription:
|
|
7061
|
+
"既存のコンテンツを上書きしないよう、ページ本文の同期が完了するまで編集は一時停止されます。",
|
|
7043
7062
|
creatingDatabase: "インラインデータベースを作成しています...",
|
|
7044
7063
|
databaseCreated: "インラインデータベースが作成されました",
|
|
7045
7064
|
emptyBlockPlaceholder: "「/」でコマンドを表示",
|
|
@@ -7393,6 +7412,9 @@ const editorMessagesByLocale = {
|
|
|
7393
7412
|
builderBodySyncing: "콘텐츠가 아직 Builder에서 동기화되는 중입니다",
|
|
7394
7413
|
builderBodySyncingDescription:
|
|
7395
7414
|
"기존 문서 내용을 덮어쓰지 않도록 Builder 본문 동기화가 완료될 때까지 편집이 일시 중지됩니다.",
|
|
7415
|
+
pageBodySyncing: "이 페이지의 콘텐츠가 아직 동기화 중입니다",
|
|
7416
|
+
pageBodySyncingDescription:
|
|
7417
|
+
"기존 콘텐츠를 덮어쓰지 않도록 페이지 본문 동기화가 완료될 때까지 편집이 일시 중지됩니다.",
|
|
7396
7418
|
creatingDatabase: "인라인 데이터베이스 생성 중...",
|
|
7397
7419
|
databaseCreated: "인라인 데이터베이스가 생성되었습니다.",
|
|
7398
7420
|
emptyBlockPlaceholder: "‘/’를 눌러 명령 사용",
|
|
@@ -7745,6 +7767,9 @@ const editorMessagesByLocale = {
|
|
|
7745
7767
|
builderBodySyncing: "O conteúdo ainda está sincronizando do Builder",
|
|
7746
7768
|
builderBodySyncingDescription:
|
|
7747
7769
|
"A edição fica pausada até o corpo do Builder terminar de sincronizar, para não sobrescrever o conteúdo existente do artigo.",
|
|
7770
|
+
pageBodySyncing: "O conteúdo desta página ainda está sincronizando",
|
|
7771
|
+
pageBodySyncingDescription:
|
|
7772
|
+
"A edição fica pausada até o conteúdo da página terminar de sincronizar, para não sobrescrever o conteúdo existente.",
|
|
7748
7773
|
creatingDatabase: "Criando banco de dados embutido...",
|
|
7749
7774
|
databaseCreated: "Banco de dados embutido criado",
|
|
7750
7775
|
emptyBlockPlaceholder: "Pressione “/” para comandos",
|
|
@@ -8103,6 +8128,9 @@ const editorMessagesByLocale = {
|
|
|
8103
8128
|
builderBodySyncing: "सामग्री अभी भी Builder से सिंक हो रही है",
|
|
8104
8129
|
builderBodySyncingDescription:
|
|
8105
8130
|
"Builder का मुख्य भाग सिंक पूरा होने तक संपादन रोका गया है, ताकि मौजूदा लेख सामग्री अधिलेखित न हो।",
|
|
8131
|
+
pageBodySyncing: "इस पेज की सामग्री अभी भी सिंक हो रही है",
|
|
8132
|
+
pageBodySyncingDescription:
|
|
8133
|
+
"पेज का मुख्य भाग सिंक पूरा होने तक संपादन रोका गया है, ताकि मौजूदा सामग्री अधिलेखित न हो।",
|
|
8106
8134
|
creatingDatabase: "इनलाइन डेटाबेस बनाया जा रहा है...",
|
|
8107
8135
|
databaseCreated: "इनलाइन डेटाबेस बनाया गया",
|
|
8108
8136
|
emptyBlockPlaceholder: "कमांड के लिए '/' दबाएं",
|
|
@@ -8450,6 +8478,9 @@ const editorMessagesByLocale = {
|
|
|
8450
8478
|
builderBodySyncing: "لا يزال المحتوى قيد المزامنة من Builder",
|
|
8451
8479
|
builderBodySyncingDescription:
|
|
8452
8480
|
"يتم إيقاف التحرير مؤقتًا حتى تكتمل مزامنة نص Builder، حتى لا يتم استبدال محتوى المقالة الحالي.",
|
|
8481
|
+
pageBodySyncing: "لا يزال محتوى هذه الصفحة قيد المزامنة",
|
|
8482
|
+
pageBodySyncingDescription:
|
|
8483
|
+
"يتم إيقاف التحرير مؤقتًا حتى تكتمل مزامنة محتوى الصفحة، حتى لا تتم الكتابة فوق المحتوى الحالي.",
|
|
8453
8484
|
creatingDatabase: "جارٍ إنشاء قاعدة بيانات مضمنة...",
|
|
8454
8485
|
databaseCreated: "تم إنشاء قاعدة البيانات المضمنة",
|
|
8455
8486
|
emptyBlockPlaceholder: 'اضغط على "/" للأوامر',
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface DocumentQueryContext {
|
|
2
|
+
databaseId?: string | null;
|
|
3
|
+
databaseDocumentId?: string | null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function documentQueryKey(
|
|
7
|
+
documentId: string,
|
|
8
|
+
context: DocumentQueryContext = {},
|
|
9
|
+
) {
|
|
10
|
+
return [
|
|
11
|
+
"action",
|
|
12
|
+
"get-document",
|
|
13
|
+
{
|
|
14
|
+
id: documentId,
|
|
15
|
+
...(context.databaseId ? { databaseId: context.databaseId } : {}),
|
|
16
|
+
...(context.databaseDocumentId
|
|
17
|
+
? { databaseDocumentId: context.databaseDocumentId }
|
|
18
|
+
: {}),
|
|
19
|
+
},
|
|
20
|
+
] as const;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function documentQueryFilter(documentId: string) {
|
|
24
|
+
return {
|
|
25
|
+
queryKey: ["action", "get-document"] as const,
|
|
26
|
+
predicate: (query: { queryKey: readonly unknown[] }) => {
|
|
27
|
+
const args = query.queryKey[2];
|
|
28
|
+
return (
|
|
29
|
+
!!args &&
|
|
30
|
+
typeof args === "object" &&
|
|
31
|
+
"id" in args &&
|
|
32
|
+
args.id === documentId
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -14,6 +14,7 @@ export type DocumentContextPathEntry = {
|
|
|
14
14
|
* assembles the live path without copying ancestor prose into descendants. */
|
|
15
15
|
export async function getDocumentContextPath(
|
|
16
16
|
document: Pick<typeof schema.documents.$inferSelect, "id" | "parentId">,
|
|
17
|
+
options: { databaseId?: string } = {},
|
|
17
18
|
): Promise<DocumentContextPathEntry[]> {
|
|
18
19
|
const db = getDb();
|
|
19
20
|
const path: DocumentContextPathEntry[] = [];
|
|
@@ -43,6 +44,15 @@ export async function getDocumentContextPath(
|
|
|
43
44
|
parentId = parent.parentId;
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
const membershipClauses = [
|
|
48
|
+
eq(schema.contentDatabaseItems.documentId, document.id),
|
|
49
|
+
isNull(schema.contentDatabases.deletedAt),
|
|
50
|
+
];
|
|
51
|
+
if (options.databaseId) {
|
|
52
|
+
membershipClauses.push(
|
|
53
|
+
eq(schema.contentDatabaseItems.databaseId, options.databaseId),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
46
56
|
const [membership] = await db
|
|
47
57
|
.select({ database: schema.contentDatabases })
|
|
48
58
|
.from(schema.contentDatabaseItems)
|
|
@@ -50,12 +60,7 @@ export async function getDocumentContextPath(
|
|
|
50
60
|
schema.contentDatabases,
|
|
51
61
|
eq(schema.contentDatabases.id, schema.contentDatabaseItems.databaseId),
|
|
52
62
|
)
|
|
53
|
-
.where(
|
|
54
|
-
and(
|
|
55
|
-
eq(schema.contentDatabaseItems.documentId, document.id),
|
|
56
|
-
isNull(schema.contentDatabases.deletedAt),
|
|
57
|
-
),
|
|
58
|
-
)
|
|
63
|
+
.where(and(...membershipClauses))
|
|
59
64
|
.orderBy(
|
|
60
65
|
sql`CASE WHEN ${schema.contentDatabases.systemRole} IS NULL THEN 0 ELSE 1 END`,
|
|
61
66
|
asc(schema.contentDatabases.id),
|