@comapeo/core-react 6.0.0 → 6.1.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/dist/commonjs/hooks/projects.js +18 -9
- package/dist/commonjs/lib/react-query/projects.d.ts +11 -63
- package/dist/commonjs/lib/react-query/projects.js +28 -29
- package/dist/commonjs/lib/urls.d.ts +15 -0
- package/dist/commonjs/lib/urls.js +56 -0
- package/dist/esm/hooks/projects.js +19 -10
- package/dist/esm/lib/react-query/projects.d.ts +11 -63
- package/dist/esm/lib/react-query/projects.js +26 -25
- package/dist/esm/lib/urls.d.ts +15 -0
- package/dist/esm/lib/urls.js +52 -0
- package/package.json +1 -1
|
@@ -29,6 +29,7 @@ const react_query_1 = require("@tanstack/react-query");
|
|
|
29
29
|
const react_1 = require("react");
|
|
30
30
|
const projects_js_1 = require("../lib/react-query/projects.js");
|
|
31
31
|
const sync_js_1 = require("../lib/sync.js");
|
|
32
|
+
const urls_js_1 = require("../lib/urls.js");
|
|
32
33
|
const client_js_1 = require("./client.js");
|
|
33
34
|
/**
|
|
34
35
|
* Retrieve the project settings for a project.
|
|
@@ -185,13 +186,14 @@ function useManyMembers({ projectId }) {
|
|
|
185
186
|
*/
|
|
186
187
|
function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
|
|
187
188
|
const { data: projectApi } = useSingleProject({ projectId });
|
|
188
|
-
const { data, error, isRefetching } = (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
projectId,
|
|
189
|
+
const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
|
|
190
|
+
const iconUrl = (0, urls_js_1.getIconUrl)({
|
|
191
|
+
serverOrigin,
|
|
192
192
|
iconId,
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
projectId,
|
|
194
|
+
mimeBasedOpts,
|
|
195
|
+
});
|
|
196
|
+
return { data: iconUrl, error, isRefetching };
|
|
195
197
|
}
|
|
196
198
|
/**
|
|
197
199
|
* Retrieve a URL that points to a desired blob resource.
|
|
@@ -246,10 +248,17 @@ function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
|
|
|
246
248
|
*/
|
|
247
249
|
function useAttachmentUrl({ projectId, blobId, }) {
|
|
248
250
|
const { data: projectApi } = useSingleProject({ projectId });
|
|
249
|
-
const { data, error, isRefetching } = (
|
|
251
|
+
const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
|
|
252
|
+
const blobUrl = (0, urls_js_1.getBlobUrl)({ serverOrigin, projectId, blobId });
|
|
253
|
+
return { data: blobUrl, error, isRefetching };
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* @internal
|
|
257
|
+
* Hack to retrieve the media server origin (protocol + host).
|
|
258
|
+
*/
|
|
259
|
+
function useMediaServerOrigin({ projectApi }) {
|
|
260
|
+
const { data, error, isRefetching } = (0, react_query_1.useSuspenseQuery)((0, projects_js_1.mediaServerOriginQueryOptions)({
|
|
250
261
|
projectApi,
|
|
251
|
-
projectId,
|
|
252
|
-
blobId,
|
|
253
262
|
}));
|
|
254
263
|
return { data, error, isRefetching };
|
|
255
264
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BlobApi
|
|
1
|
+
import type { BlobApi } from '@comapeo/core' with { 'resolution-mode': 'import' };
|
|
2
2
|
import type { MapeoClientApi, MapeoProjectApi } from '@comapeo/ipc' with { 'resolution-mode': 'import' };
|
|
3
3
|
import type { ProjectSettings } from '@comapeo/schema' with { 'resolution-mode': 'import' };
|
|
4
4
|
import { type QueryClient, type UnusedSkipTokenOptions } from '@tanstack/react-query';
|
|
@@ -19,27 +19,16 @@ export declare function getMemberByIdQueryKey({ projectId, deviceId, }: {
|
|
|
19
19
|
projectId: string;
|
|
20
20
|
deviceId: string;
|
|
21
21
|
}): readonly ["@comapeo/core-react", "projects", string, "members", string];
|
|
22
|
-
export declare function getIconUrlQueryKey({ projectId, iconId, ...mimeBasedOpts }: {
|
|
23
|
-
projectId: string;
|
|
24
|
-
iconId: string;
|
|
25
|
-
} & (IconApi.BitmapOpts | IconApi.SvgOpts)): readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
26
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/png">;
|
|
27
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant, {
|
|
28
|
-
mimeType: "image/png";
|
|
29
|
-
}>["pixelDensity"];
|
|
30
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
31
|
-
} | {
|
|
32
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/svg+xml">;
|
|
33
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
34
|
-
}];
|
|
35
22
|
export declare function getDocumentCreatedByQueryKey({ projectId, originalVersionId, }: {
|
|
36
23
|
projectId: string;
|
|
37
24
|
originalVersionId: string;
|
|
38
25
|
}): readonly ["@comapeo/core-react", "projects", string, "document_created_by", string];
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
/**
|
|
27
|
+
* We call this within a project hook, because that's the only place the API is
|
|
28
|
+
* exposed right now, but it is the same for all projects, so no need for
|
|
29
|
+
* scoping the query key to the project
|
|
30
|
+
*/
|
|
31
|
+
export declare function getMediaServerOriginQueryKey(): readonly ["@comapeo/core-react", "media_server_origin"];
|
|
43
32
|
export declare function projectsQueryOptions({ clientApi, }: {
|
|
44
33
|
clientApi: MapeoClientApi;
|
|
45
34
|
}): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<import("@comapeo/core/dist/mapeo-manager.js", { with: { "resolution-mode": "import" } }).ListedProject[], Error, import("@comapeo/core/dist/mapeo-manager.js", { with: { "resolution-mode": "import" } }).ListedProject[], readonly ["@comapeo/core-react", "projects"]>, "queryFn"> & {
|
|
@@ -99,45 +88,6 @@ export declare function projectOwnRoleQueryOptions({ projectApi, projectId, }: {
|
|
|
99
88
|
[dataTagErrorSymbol]: Error;
|
|
100
89
|
};
|
|
101
90
|
};
|
|
102
|
-
export declare function iconUrlQueryOptions({ projectApi, projectId, iconId, ...mimeBasedOpts }: {
|
|
103
|
-
projectApi: MapeoProjectApi;
|
|
104
|
-
projectId: string;
|
|
105
|
-
iconId: Parameters<MapeoProjectApi['$icons']['getIconUrl']>[0];
|
|
106
|
-
} & (IconApi.BitmapOpts | IconApi.SvgOpts)): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
107
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/png">;
|
|
108
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant, {
|
|
109
|
-
mimeType: "image/png";
|
|
110
|
-
}>["pixelDensity"];
|
|
111
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
112
|
-
} | {
|
|
113
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/svg+xml">;
|
|
114
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
115
|
-
}]>, "queryFn"> & {
|
|
116
|
-
queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
117
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/png">;
|
|
118
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant, {
|
|
119
|
-
mimeType: "image/png";
|
|
120
|
-
}>["pixelDensity"];
|
|
121
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
122
|
-
} | {
|
|
123
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/svg+xml">;
|
|
124
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
125
|
-
}], never> | undefined;
|
|
126
|
-
} & {
|
|
127
|
-
queryKey: readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
128
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/png">;
|
|
129
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant, {
|
|
130
|
-
mimeType: "image/png";
|
|
131
|
-
}>["pixelDensity"];
|
|
132
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
133
|
-
} | {
|
|
134
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).IconVariant["mimeType"], "image/svg+xml">;
|
|
135
|
-
size: import("@comapeo/core/dist/icon-api.js", { with: { "resolution-mode": "import" } }).ValidSizes;
|
|
136
|
-
}] & {
|
|
137
|
-
[dataTagSymbol]: string;
|
|
138
|
-
[dataTagErrorSymbol]: Error;
|
|
139
|
-
};
|
|
140
|
-
};
|
|
141
91
|
export declare function documentCreatedByQueryOptions({ projectApi, projectId, originalVersionId, }: {
|
|
142
92
|
projectApi: MapeoProjectApi;
|
|
143
93
|
projectId: string;
|
|
@@ -150,14 +100,12 @@ export declare function documentCreatedByQueryOptions({ projectApi, projectId, o
|
|
|
150
100
|
[dataTagErrorSymbol]: Error;
|
|
151
101
|
};
|
|
152
102
|
};
|
|
153
|
-
export declare function
|
|
103
|
+
export declare function mediaServerOriginQueryOptions({ projectApi, }: {
|
|
154
104
|
projectApi: MapeoProjectApi;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "projects", string, "attachments", import("@comapeo/core/dist/types.js", { with: { "resolution-mode": "import" } }).BlobId]>, "queryFn"> & {
|
|
158
|
-
queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "projects", string, "attachments", import("@comapeo/core/dist/types.js", { with: { "resolution-mode": "import" } }).BlobId], never> | undefined;
|
|
105
|
+
}): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "media_server_origin"]>, "queryFn"> & {
|
|
106
|
+
queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "media_server_origin"], never> | undefined;
|
|
159
107
|
} & {
|
|
160
|
-
queryKey: readonly ["@comapeo/core-react", "
|
|
108
|
+
queryKey: readonly ["@comapeo/core-react", "media_server_origin"] & {
|
|
161
109
|
[dataTagSymbol]: string;
|
|
162
110
|
[dataTagErrorSymbol]: Error;
|
|
163
111
|
};
|
|
@@ -6,18 +6,16 @@ exports.getProjectSettingsQueryKey = getProjectSettingsQueryKey;
|
|
|
6
6
|
exports.getProjectRoleQueryKey = getProjectRoleQueryKey;
|
|
7
7
|
exports.getMembersQueryKey = getMembersQueryKey;
|
|
8
8
|
exports.getMemberByIdQueryKey = getMemberByIdQueryKey;
|
|
9
|
-
exports.getIconUrlQueryKey = getIconUrlQueryKey;
|
|
10
9
|
exports.getDocumentCreatedByQueryKey = getDocumentCreatedByQueryKey;
|
|
11
|
-
exports.
|
|
10
|
+
exports.getMediaServerOriginQueryKey = getMediaServerOriginQueryKey;
|
|
12
11
|
exports.projectsQueryOptions = projectsQueryOptions;
|
|
13
12
|
exports.projectByIdQueryOptions = projectByIdQueryOptions;
|
|
14
13
|
exports.projectSettingsQueryOptions = projectSettingsQueryOptions;
|
|
15
14
|
exports.projectMembersQueryOptions = projectMembersQueryOptions;
|
|
16
15
|
exports.projectMemberByIdQueryOptions = projectMemberByIdQueryOptions;
|
|
17
16
|
exports.projectOwnRoleQueryOptions = projectOwnRoleQueryOptions;
|
|
18
|
-
exports.iconUrlQueryOptions = iconUrlQueryOptions;
|
|
19
17
|
exports.documentCreatedByQueryOptions = documentCreatedByQueryOptions;
|
|
20
|
-
exports.
|
|
18
|
+
exports.mediaServerOriginQueryOptions = mediaServerOriginQueryOptions;
|
|
21
19
|
exports.addServerPeerMutationOptions = addServerPeerMutationOptions;
|
|
22
20
|
exports.removeServerPeerMutationOptions = removeServerPeerMutationOptions;
|
|
23
21
|
exports.createProjectMutationOptions = createProjectMutationOptions;
|
|
@@ -52,16 +50,6 @@ function getMembersQueryKey({ projectId }) {
|
|
|
52
50
|
function getMemberByIdQueryKey({ projectId, deviceId, }) {
|
|
53
51
|
return [shared_js_1.ROOT_QUERY_KEY, 'projects', projectId, 'members', deviceId];
|
|
54
52
|
}
|
|
55
|
-
function getIconUrlQueryKey({ projectId, iconId, ...mimeBasedOpts }) {
|
|
56
|
-
return [
|
|
57
|
-
shared_js_1.ROOT_QUERY_KEY,
|
|
58
|
-
'projects',
|
|
59
|
-
projectId,
|
|
60
|
-
'icons',
|
|
61
|
-
iconId,
|
|
62
|
-
mimeBasedOpts,
|
|
63
|
-
];
|
|
64
|
-
}
|
|
65
53
|
function getDocumentCreatedByQueryKey({ projectId, originalVersionId, }) {
|
|
66
54
|
return [
|
|
67
55
|
shared_js_1.ROOT_QUERY_KEY,
|
|
@@ -71,8 +59,13 @@ function getDocumentCreatedByQueryKey({ projectId, originalVersionId, }) {
|
|
|
71
59
|
originalVersionId,
|
|
72
60
|
];
|
|
73
61
|
}
|
|
74
|
-
|
|
75
|
-
|
|
62
|
+
/**
|
|
63
|
+
* We call this within a project hook, because that's the only place the API is
|
|
64
|
+
* exposed right now, but it is the same for all projects, so no need for
|
|
65
|
+
* scoping the query key to the project
|
|
66
|
+
*/
|
|
67
|
+
function getMediaServerOriginQueryKey() {
|
|
68
|
+
return [shared_js_1.ROOT_QUERY_KEY, 'media_server_origin'];
|
|
76
69
|
}
|
|
77
70
|
function projectsQueryOptions({ clientApi, }) {
|
|
78
71
|
return (0, react_query_1.queryOptions)({
|
|
@@ -128,15 +121,6 @@ function projectOwnRoleQueryOptions({ projectApi, projectId, }) {
|
|
|
128
121
|
},
|
|
129
122
|
});
|
|
130
123
|
}
|
|
131
|
-
function iconUrlQueryOptions({ projectApi, projectId, iconId, ...mimeBasedOpts }) {
|
|
132
|
-
return (0, react_query_1.queryOptions)({
|
|
133
|
-
...(0, shared_js_1.baseQueryOptions)(),
|
|
134
|
-
queryKey: getIconUrlQueryKey({ ...mimeBasedOpts, projectId, iconId }),
|
|
135
|
-
queryFn: async () => {
|
|
136
|
-
return projectApi.$icons.getIconUrl(iconId, mimeBasedOpts);
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
124
|
function documentCreatedByQueryOptions({ projectApi, projectId, originalVersionId, }) {
|
|
141
125
|
return (0, react_query_1.queryOptions)({
|
|
142
126
|
...(0, shared_js_1.baseQueryOptions)(),
|
|
@@ -147,16 +131,31 @@ function documentCreatedByQueryOptions({ projectApi, projectId, originalVersionI
|
|
|
147
131
|
queryFn: async () => {
|
|
148
132
|
return projectApi.$originalVersionIdToDeviceId(originalVersionId);
|
|
149
133
|
},
|
|
134
|
+
staleTime: 'static',
|
|
135
|
+
gcTime: Infinity,
|
|
150
136
|
});
|
|
151
137
|
}
|
|
152
|
-
|
|
138
|
+
// Used as a placeholder so that we can read the server port from the $blobs.getUrl() method
|
|
139
|
+
const FAKE_BLOB_ID = {
|
|
140
|
+
type: 'photo',
|
|
141
|
+
variant: 'original',
|
|
142
|
+
name: 'name',
|
|
143
|
+
driveId: 'drive-id',
|
|
144
|
+
};
|
|
145
|
+
function mediaServerOriginQueryOptions({ projectApi, }) {
|
|
153
146
|
return (0, react_query_1.queryOptions)({
|
|
154
147
|
...(0, shared_js_1.baseQueryOptions)(),
|
|
155
|
-
|
|
148
|
+
// HACK: The server doesn't yet expose a method to get its origin, so we use
|
|
149
|
+
// the existing $blobs.getUrl() to get the origin with a fake BlobId. The origin
|
|
150
|
+
// is the same regardless of the blobId, so it's not necessary to include it
|
|
151
|
+
// as a dep for the query key.
|
|
152
|
+
queryKey: getMediaServerOriginQueryKey(),
|
|
156
153
|
queryFn: async () => {
|
|
157
|
-
|
|
158
|
-
return
|
|
154
|
+
const url = await projectApi.$blobs.getUrl(FAKE_BLOB_ID);
|
|
155
|
+
return new URL(url).origin;
|
|
159
156
|
},
|
|
157
|
+
staleTime: 'static',
|
|
158
|
+
gcTime: Infinity,
|
|
160
159
|
});
|
|
161
160
|
}
|
|
162
161
|
function addServerPeerMutationOptions({ projectApi, projectId, queryClient, }) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BlobApi, IconApi } from '@comapeo/core';
|
|
2
|
+
/**
|
|
3
|
+
* Get a url for a blob based on its BlobId
|
|
4
|
+
*/
|
|
5
|
+
export declare function getBlobUrl({ serverOrigin, projectId, blobId, }: {
|
|
6
|
+
serverOrigin: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
blobId: BlobApi.BlobId;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }: {
|
|
11
|
+
serverOrigin: string;
|
|
12
|
+
iconId: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
mimeBasedOpts: IconApi.BitmapOpts | IconApi.SvgOpts;
|
|
15
|
+
}): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// TODO: Move these into a separate "@comapeo/asset-server" module which can
|
|
3
|
+
// export them to be imported directly in a client.
|
|
4
|
+
//
|
|
5
|
+
// NB: The URL construction is fragile right now - it must match the
|
|
6
|
+
// implementation in the @comapeo/core fastify plugins
|
|
7
|
+
// [blobServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/blobs.js)
|
|
8
|
+
// and
|
|
9
|
+
// [iconServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/icons.js)
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.getBlobUrl = getBlobUrl;
|
|
12
|
+
exports.getIconUrl = getIconUrl;
|
|
13
|
+
const MIME_TO_EXTENSION = {
|
|
14
|
+
'image/png': '.png',
|
|
15
|
+
'image/svg+xml': '.svg',
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Get a url for a blob based on its BlobId
|
|
19
|
+
*/
|
|
20
|
+
function getBlobUrl({ serverOrigin, projectId, blobId, }) {
|
|
21
|
+
const { driveId, type, variant, name } = blobId;
|
|
22
|
+
return `${serverOrigin}/blobs/${projectId}/${driveId}/${type}/${variant}/${name}`;
|
|
23
|
+
}
|
|
24
|
+
function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }) {
|
|
25
|
+
const mimeExtension = MIME_TO_EXTENSION[mimeBasedOpts.mimeType];
|
|
26
|
+
const pixelDensity = mimeBasedOpts.mimeType === 'image/svg+xml' ||
|
|
27
|
+
// if the pixel density is 1, we can omit the density suffix in the resulting url
|
|
28
|
+
// and assume the pixel density is 1 for applicable mime types when using the url
|
|
29
|
+
mimeBasedOpts.pixelDensity === 1
|
|
30
|
+
? undefined
|
|
31
|
+
: mimeBasedOpts.pixelDensity;
|
|
32
|
+
return (`${serverOrigin}/icons/${projectId}/` +
|
|
33
|
+
constructIconPath({
|
|
34
|
+
pixelDensity,
|
|
35
|
+
size: mimeBasedOpts.size,
|
|
36
|
+
extension: mimeExtension,
|
|
37
|
+
iconId,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* General purpose path builder for an icon
|
|
42
|
+
*/
|
|
43
|
+
function constructIconPath({ size, pixelDensity, iconId, extension, }) {
|
|
44
|
+
if (iconId.length === 0 || size.length === 0 || extension.length === 0) {
|
|
45
|
+
throw new Error('iconId, size, and extension cannot be empty strings');
|
|
46
|
+
}
|
|
47
|
+
let result = `${iconId}/${size}`;
|
|
48
|
+
if (typeof pixelDensity === 'number') {
|
|
49
|
+
if (pixelDensity < 1) {
|
|
50
|
+
throw new Error('pixelDensity must be a positive number');
|
|
51
|
+
}
|
|
52
|
+
result += `@${pixelDensity}x`;
|
|
53
|
+
}
|
|
54
|
+
result += extension.startsWith('.') ? extension : '.' + extension;
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useMutation, useQueryClient, useSuspenseQuery, } from '@tanstack/react-query';
|
|
2
2
|
import { useSyncExternalStore } from 'react';
|
|
3
|
-
import { addServerPeerMutationOptions,
|
|
3
|
+
import { addServerPeerMutationOptions, connectSyncServersMutationOptions, createBlobMutationOptions, createProjectMutationOptions, disconnectSyncServersMutationOptions, documentCreatedByQueryOptions, exportGeoJSONMutationOptions, exportZipFileMutationOptions, importProjectConfigMutationOptions, leaveProjectMutationOptions, mediaServerOriginQueryOptions, projectByIdQueryOptions, projectMemberByIdQueryOptions, projectMembersQueryOptions, projectOwnRoleQueryOptions, projectSettingsQueryOptions, projectsQueryOptions, removeServerPeerMutationOptions, setAutostopDataSyncTimeoutMutationOptions, startSyncMutationOptions, stopSyncMutationOptions, updateProjectSettingsMutationOptions, } from '../lib/react-query/projects.js';
|
|
4
4
|
import { SyncStore } from '../lib/sync.js';
|
|
5
|
+
import { getBlobUrl, getIconUrl } from '../lib/urls.js';
|
|
5
6
|
import { useClientApi } from './client.js';
|
|
6
7
|
/**
|
|
7
8
|
* Retrieve the project settings for a project.
|
|
@@ -158,13 +159,14 @@ export function useManyMembers({ projectId }) {
|
|
|
158
159
|
*/
|
|
159
160
|
export function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
|
|
160
161
|
const { data: projectApi } = useSingleProject({ projectId });
|
|
161
|
-
const { data, error, isRefetching } =
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
projectId,
|
|
162
|
+
const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
|
|
163
|
+
const iconUrl = getIconUrl({
|
|
164
|
+
serverOrigin,
|
|
165
165
|
iconId,
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
projectId,
|
|
167
|
+
mimeBasedOpts,
|
|
168
|
+
});
|
|
169
|
+
return { data: iconUrl, error, isRefetching };
|
|
168
170
|
}
|
|
169
171
|
/**
|
|
170
172
|
* Retrieve a URL that points to a desired blob resource.
|
|
@@ -219,10 +221,17 @@ export function useIconUrl({ projectId, iconId, ...mimeBasedOpts }) {
|
|
|
219
221
|
*/
|
|
220
222
|
export function useAttachmentUrl({ projectId, blobId, }) {
|
|
221
223
|
const { data: projectApi } = useSingleProject({ projectId });
|
|
222
|
-
const { data, error, isRefetching } =
|
|
224
|
+
const { data: serverOrigin, error, isRefetching, } = useMediaServerOrigin({ projectApi });
|
|
225
|
+
const blobUrl = getBlobUrl({ serverOrigin, projectId, blobId });
|
|
226
|
+
return { data: blobUrl, error, isRefetching };
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* @internal
|
|
230
|
+
* Hack to retrieve the media server origin (protocol + host).
|
|
231
|
+
*/
|
|
232
|
+
function useMediaServerOrigin({ projectApi }) {
|
|
233
|
+
const { data, error, isRefetching } = useSuspenseQuery(mediaServerOriginQueryOptions({
|
|
223
234
|
projectApi,
|
|
224
|
-
projectId,
|
|
225
|
-
blobId,
|
|
226
235
|
}));
|
|
227
236
|
return { data, error, isRefetching };
|
|
228
237
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BlobApi
|
|
1
|
+
import type { BlobApi } from '@comapeo/core' with { 'resolution-mode': 'import' };
|
|
2
2
|
import type { MapeoClientApi, MapeoProjectApi } from '@comapeo/ipc' with { 'resolution-mode': 'import' };
|
|
3
3
|
import type { ProjectSettings } from '@comapeo/schema' with { 'resolution-mode': 'import' };
|
|
4
4
|
import { type QueryClient, type UnusedSkipTokenOptions } from '@tanstack/react-query';
|
|
@@ -19,27 +19,16 @@ export declare function getMemberByIdQueryKey({ projectId, deviceId, }: {
|
|
|
19
19
|
projectId: string;
|
|
20
20
|
deviceId: string;
|
|
21
21
|
}): readonly ["@comapeo/core-react", "projects", string, "members", string];
|
|
22
|
-
export declare function getIconUrlQueryKey({ projectId, iconId, ...mimeBasedOpts }: {
|
|
23
|
-
projectId: string;
|
|
24
|
-
iconId: string;
|
|
25
|
-
} & (IconApi.BitmapOpts | IconApi.SvgOpts)): readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
26
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/png">;
|
|
27
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant, {
|
|
28
|
-
mimeType: "image/png";
|
|
29
|
-
}>["pixelDensity"];
|
|
30
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
31
|
-
} | {
|
|
32
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/svg+xml">;
|
|
33
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
34
|
-
}];
|
|
35
22
|
export declare function getDocumentCreatedByQueryKey({ projectId, originalVersionId, }: {
|
|
36
23
|
projectId: string;
|
|
37
24
|
originalVersionId: string;
|
|
38
25
|
}): readonly ["@comapeo/core-react", "projects", string, "document_created_by", string];
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
/**
|
|
27
|
+
* We call this within a project hook, because that's the only place the API is
|
|
28
|
+
* exposed right now, but it is the same for all projects, so no need for
|
|
29
|
+
* scoping the query key to the project
|
|
30
|
+
*/
|
|
31
|
+
export declare function getMediaServerOriginQueryKey(): readonly ["@comapeo/core-react", "media_server_origin"];
|
|
43
32
|
export declare function projectsQueryOptions({ clientApi, }: {
|
|
44
33
|
clientApi: MapeoClientApi;
|
|
45
34
|
}): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<import("@comapeo/core/dist/mapeo-manager.js").ListedProject[], Error, import("@comapeo/core/dist/mapeo-manager.js").ListedProject[], readonly ["@comapeo/core-react", "projects"]>, "queryFn"> & {
|
|
@@ -99,45 +88,6 @@ export declare function projectOwnRoleQueryOptions({ projectApi, projectId, }: {
|
|
|
99
88
|
[dataTagErrorSymbol]: Error;
|
|
100
89
|
};
|
|
101
90
|
};
|
|
102
|
-
export declare function iconUrlQueryOptions({ projectApi, projectId, iconId, ...mimeBasedOpts }: {
|
|
103
|
-
projectApi: MapeoProjectApi;
|
|
104
|
-
projectId: string;
|
|
105
|
-
iconId: Parameters<MapeoProjectApi['$icons']['getIconUrl']>[0];
|
|
106
|
-
} & (IconApi.BitmapOpts | IconApi.SvgOpts)): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
107
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/png">;
|
|
108
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant, {
|
|
109
|
-
mimeType: "image/png";
|
|
110
|
-
}>["pixelDensity"];
|
|
111
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
112
|
-
} | {
|
|
113
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/svg+xml">;
|
|
114
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
115
|
-
}]>, "queryFn"> & {
|
|
116
|
-
queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
117
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/png">;
|
|
118
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant, {
|
|
119
|
-
mimeType: "image/png";
|
|
120
|
-
}>["pixelDensity"];
|
|
121
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
122
|
-
} | {
|
|
123
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/svg+xml">;
|
|
124
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
125
|
-
}], never> | undefined;
|
|
126
|
-
} & {
|
|
127
|
-
queryKey: readonly ["@comapeo/core-react", "projects", string, "icons", string, {
|
|
128
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/png">;
|
|
129
|
-
pixelDensity: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant, {
|
|
130
|
-
mimeType: "image/png";
|
|
131
|
-
}>["pixelDensity"];
|
|
132
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
133
|
-
} | {
|
|
134
|
-
mimeType: Extract<import("@comapeo/core/dist/icon-api.js").IconVariant["mimeType"], "image/svg+xml">;
|
|
135
|
-
size: import("@comapeo/core/dist/icon-api.js").ValidSizes;
|
|
136
|
-
}] & {
|
|
137
|
-
[dataTagSymbol]: string;
|
|
138
|
-
[dataTagErrorSymbol]: Error;
|
|
139
|
-
};
|
|
140
|
-
};
|
|
141
91
|
export declare function documentCreatedByQueryOptions({ projectApi, projectId, originalVersionId, }: {
|
|
142
92
|
projectApi: MapeoProjectApi;
|
|
143
93
|
projectId: string;
|
|
@@ -150,14 +100,12 @@ export declare function documentCreatedByQueryOptions({ projectApi, projectId, o
|
|
|
150
100
|
[dataTagErrorSymbol]: Error;
|
|
151
101
|
};
|
|
152
102
|
};
|
|
153
|
-
export declare function
|
|
103
|
+
export declare function mediaServerOriginQueryOptions({ projectApi, }: {
|
|
154
104
|
projectApi: MapeoProjectApi;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "projects", string, "attachments", import("@comapeo/core/dist/types.js").BlobId]>, "queryFn"> & {
|
|
158
|
-
queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "projects", string, "attachments", import("@comapeo/core/dist/types.js").BlobId], never> | undefined;
|
|
105
|
+
}): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<string, Error, string, readonly ["@comapeo/core-react", "media_server_origin"]>, "queryFn"> & {
|
|
106
|
+
queryFn?: import("@tanstack/react-query").QueryFunction<string, readonly ["@comapeo/core-react", "media_server_origin"], never> | undefined;
|
|
159
107
|
} & {
|
|
160
|
-
queryKey: readonly ["@comapeo/core-react", "
|
|
108
|
+
queryKey: readonly ["@comapeo/core-react", "media_server_origin"] & {
|
|
161
109
|
[dataTagSymbol]: string;
|
|
162
110
|
[dataTagErrorSymbol]: Error;
|
|
163
111
|
};
|
|
@@ -18,16 +18,6 @@ export function getMembersQueryKey({ projectId }) {
|
|
|
18
18
|
export function getMemberByIdQueryKey({ projectId, deviceId, }) {
|
|
19
19
|
return [ROOT_QUERY_KEY, 'projects', projectId, 'members', deviceId];
|
|
20
20
|
}
|
|
21
|
-
export function getIconUrlQueryKey({ projectId, iconId, ...mimeBasedOpts }) {
|
|
22
|
-
return [
|
|
23
|
-
ROOT_QUERY_KEY,
|
|
24
|
-
'projects',
|
|
25
|
-
projectId,
|
|
26
|
-
'icons',
|
|
27
|
-
iconId,
|
|
28
|
-
mimeBasedOpts,
|
|
29
|
-
];
|
|
30
|
-
}
|
|
31
21
|
export function getDocumentCreatedByQueryKey({ projectId, originalVersionId, }) {
|
|
32
22
|
return [
|
|
33
23
|
ROOT_QUERY_KEY,
|
|
@@ -37,8 +27,13 @@ export function getDocumentCreatedByQueryKey({ projectId, originalVersionId, })
|
|
|
37
27
|
originalVersionId,
|
|
38
28
|
];
|
|
39
29
|
}
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
/**
|
|
31
|
+
* We call this within a project hook, because that's the only place the API is
|
|
32
|
+
* exposed right now, but it is the same for all projects, so no need for
|
|
33
|
+
* scoping the query key to the project
|
|
34
|
+
*/
|
|
35
|
+
export function getMediaServerOriginQueryKey() {
|
|
36
|
+
return [ROOT_QUERY_KEY, 'media_server_origin'];
|
|
42
37
|
}
|
|
43
38
|
export function projectsQueryOptions({ clientApi, }) {
|
|
44
39
|
return queryOptions({
|
|
@@ -94,15 +89,6 @@ export function projectOwnRoleQueryOptions({ projectApi, projectId, }) {
|
|
|
94
89
|
},
|
|
95
90
|
});
|
|
96
91
|
}
|
|
97
|
-
export function iconUrlQueryOptions({ projectApi, projectId, iconId, ...mimeBasedOpts }) {
|
|
98
|
-
return queryOptions({
|
|
99
|
-
...baseQueryOptions(),
|
|
100
|
-
queryKey: getIconUrlQueryKey({ ...mimeBasedOpts, projectId, iconId }),
|
|
101
|
-
queryFn: async () => {
|
|
102
|
-
return projectApi.$icons.getIconUrl(iconId, mimeBasedOpts);
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
92
|
export function documentCreatedByQueryOptions({ projectApi, projectId, originalVersionId, }) {
|
|
107
93
|
return queryOptions({
|
|
108
94
|
...baseQueryOptions(),
|
|
@@ -113,16 +99,31 @@ export function documentCreatedByQueryOptions({ projectApi, projectId, originalV
|
|
|
113
99
|
queryFn: async () => {
|
|
114
100
|
return projectApi.$originalVersionIdToDeviceId(originalVersionId);
|
|
115
101
|
},
|
|
102
|
+
staleTime: 'static',
|
|
103
|
+
gcTime: Infinity,
|
|
116
104
|
});
|
|
117
105
|
}
|
|
118
|
-
|
|
106
|
+
// Used as a placeholder so that we can read the server port from the $blobs.getUrl() method
|
|
107
|
+
const FAKE_BLOB_ID = {
|
|
108
|
+
type: 'photo',
|
|
109
|
+
variant: 'original',
|
|
110
|
+
name: 'name',
|
|
111
|
+
driveId: 'drive-id',
|
|
112
|
+
};
|
|
113
|
+
export function mediaServerOriginQueryOptions({ projectApi, }) {
|
|
119
114
|
return queryOptions({
|
|
120
115
|
...baseQueryOptions(),
|
|
121
|
-
|
|
116
|
+
// HACK: The server doesn't yet expose a method to get its origin, so we use
|
|
117
|
+
// the existing $blobs.getUrl() to get the origin with a fake BlobId. The origin
|
|
118
|
+
// is the same regardless of the blobId, so it's not necessary to include it
|
|
119
|
+
// as a dep for the query key.
|
|
120
|
+
queryKey: getMediaServerOriginQueryKey(),
|
|
122
121
|
queryFn: async () => {
|
|
123
|
-
|
|
124
|
-
return
|
|
122
|
+
const url = await projectApi.$blobs.getUrl(FAKE_BLOB_ID);
|
|
123
|
+
return new URL(url).origin;
|
|
125
124
|
},
|
|
125
|
+
staleTime: 'static',
|
|
126
|
+
gcTime: Infinity,
|
|
126
127
|
});
|
|
127
128
|
}
|
|
128
129
|
export function addServerPeerMutationOptions({ projectApi, projectId, queryClient, }) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BlobApi, IconApi } from '@comapeo/core';
|
|
2
|
+
/**
|
|
3
|
+
* Get a url for a blob based on its BlobId
|
|
4
|
+
*/
|
|
5
|
+
export declare function getBlobUrl({ serverOrigin, projectId, blobId, }: {
|
|
6
|
+
serverOrigin: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
blobId: BlobApi.BlobId;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }: {
|
|
11
|
+
serverOrigin: string;
|
|
12
|
+
iconId: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
mimeBasedOpts: IconApi.BitmapOpts | IconApi.SvgOpts;
|
|
15
|
+
}): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// TODO: Move these into a separate "@comapeo/asset-server" module which can
|
|
2
|
+
// export them to be imported directly in a client.
|
|
3
|
+
//
|
|
4
|
+
// NB: The URL construction is fragile right now - it must match the
|
|
5
|
+
// implementation in the @comapeo/core fastify plugins
|
|
6
|
+
// [blobServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/blobs.js)
|
|
7
|
+
// and
|
|
8
|
+
// [iconServerPlugin](https://github.com/digidem/comapeo-core/blob/main/src/fastify-plugins/icons.js)
|
|
9
|
+
const MIME_TO_EXTENSION = {
|
|
10
|
+
'image/png': '.png',
|
|
11
|
+
'image/svg+xml': '.svg',
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Get a url for a blob based on its BlobId
|
|
15
|
+
*/
|
|
16
|
+
export function getBlobUrl({ serverOrigin, projectId, blobId, }) {
|
|
17
|
+
const { driveId, type, variant, name } = blobId;
|
|
18
|
+
return `${serverOrigin}/blobs/${projectId}/${driveId}/${type}/${variant}/${name}`;
|
|
19
|
+
}
|
|
20
|
+
export function getIconUrl({ serverOrigin, iconId, projectId, mimeBasedOpts, }) {
|
|
21
|
+
const mimeExtension = MIME_TO_EXTENSION[mimeBasedOpts.mimeType];
|
|
22
|
+
const pixelDensity = mimeBasedOpts.mimeType === 'image/svg+xml' ||
|
|
23
|
+
// if the pixel density is 1, we can omit the density suffix in the resulting url
|
|
24
|
+
// and assume the pixel density is 1 for applicable mime types when using the url
|
|
25
|
+
mimeBasedOpts.pixelDensity === 1
|
|
26
|
+
? undefined
|
|
27
|
+
: mimeBasedOpts.pixelDensity;
|
|
28
|
+
return (`${serverOrigin}/icons/${projectId}/` +
|
|
29
|
+
constructIconPath({
|
|
30
|
+
pixelDensity,
|
|
31
|
+
size: mimeBasedOpts.size,
|
|
32
|
+
extension: mimeExtension,
|
|
33
|
+
iconId,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* General purpose path builder for an icon
|
|
38
|
+
*/
|
|
39
|
+
function constructIconPath({ size, pixelDensity, iconId, extension, }) {
|
|
40
|
+
if (iconId.length === 0 || size.length === 0 || extension.length === 0) {
|
|
41
|
+
throw new Error('iconId, size, and extension cannot be empty strings');
|
|
42
|
+
}
|
|
43
|
+
let result = `${iconId}/${size}`;
|
|
44
|
+
if (typeof pixelDensity === 'number') {
|
|
45
|
+
if (pixelDensity < 1) {
|
|
46
|
+
throw new Error('pixelDensity must be a positive number');
|
|
47
|
+
}
|
|
48
|
+
result += `@${pixelDensity}x`;
|
|
49
|
+
}
|
|
50
|
+
result += extension.startsWith('.') ? extension : '.' + extension;
|
|
51
|
+
return result;
|
|
52
|
+
}
|