@itwin/saved-views-react 0.5.0 → 0.7.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/README.md +38 -4
- package/lib/SavedView.d.ts +41 -11
- package/lib/SavedViewTile/SavedViewOptions.js +10 -10
- package/lib/SavedViewTile/SavedViewTile.css +8 -1
- package/lib/SavedViewTile/SavedViewTile.d.ts +4 -3
- package/lib/SavedViewTile/SavedViewTile.js +5 -9
- package/lib/SavedViewTile/SavedViewTileContext.d.ts +0 -1
- package/lib/SavedViewTile/SavedViewTileContext.js +1 -1
- package/lib/SavedViewsClient/ITwinSavedViewsClient.d.ts +21 -23
- package/lib/SavedViewsClient/ITwinSavedViewsClient.js +120 -57
- package/lib/SavedViewsClient/SavedViewsClient.d.ts +68 -45
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTile.js +2 -3
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTileContext.d.ts +0 -1
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTileContext.js +1 -1
- package/lib/SavedViewsWidget/SavedViewsExpandableBlockWidget.d.ts +5 -3
- package/lib/SavedViewsWidget/SavedViewsExpandableBlockWidget.js +3 -3
- package/lib/SavedViewsWidget/SavedViewsFolderWidget.d.ts +5 -4
- package/lib/SavedViewsWidget/SavedViewsFolderWidget.js +7 -7
- package/lib/applySavedView.d.ts +36 -31
- package/lib/applySavedView.js +61 -26
- package/lib/captureSavedViewData.d.ts +31 -5
- package/lib/captureSavedViewData.js +103 -63
- package/lib/captureSavedViewThumbnail.d.ts +8 -1
- package/lib/captureSavedViewThumbnail.js +15 -7
- package/lib/createViewState.d.ts +22 -5
- package/lib/createViewState.js +28 -25
- package/lib/index.d.ts +10 -10
- package/lib/index.js +7 -7
- package/lib/translation/SavedViewTypes.d.ts +1 -1
- package/lib/translation/SavedViewsExtensionHandlers.d.ts +3 -0
- package/lib/translation/SavedViewsExtensionHandlers.js +14 -0
- package/lib/translation/clipVectorsLegacyExtractor.js +4 -0
- package/lib/translation/displayStyleExtractor.js +80 -3
- package/lib/translation/extractionUtilities.d.ts +9 -1
- package/lib/translation/extractionUtilities.js +16 -0
- package/lib/useSavedViews.d.ts +172 -34
- package/lib/useSavedViews.js +475 -503
- package/lib/utils.d.ts +8 -0
- package/lib/utils.js +13 -0
- package/package.json +9 -9
package/lib/useSavedViews.js
CHANGED
|
@@ -1,554 +1,526 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
import { useEffect, useMemo, useRef, useState, } from "react";
|
|
7
|
+
import { useControlledState } from "./utils.js";
|
|
8
|
+
// #endregion
|
|
9
|
+
// #region useSavedViews
|
|
10
|
+
const emptyState = Object.freeze({
|
|
11
|
+
savedViews: new Map(),
|
|
12
|
+
savedViewData: new Map(),
|
|
13
|
+
groups: new Map(),
|
|
14
|
+
tags: new Map(),
|
|
15
|
+
thumbnails: new Map(),
|
|
16
|
+
});
|
|
3
17
|
/**
|
|
4
|
-
*
|
|
5
|
-
* the store is performed via {@linkcode SavedViewsClient} interface which could communicate, for instance, with
|
|
6
|
-
* [iTwin Saved Views API](https://developer.bentley.com/apis/savedviews/overview/) using `ITwinSavedViewsClient`.
|
|
18
|
+
* Provides basic functionality to help get started with Saved Views.
|
|
7
19
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* When hook arguments change, it does not automatically clear the {@linkcode UseSavedViewsResult.store}.
|
|
22
|
+
* If you want more control over the state, provide an external store via {@linkcode UseSavedViewsArgs.state}
|
|
23
|
+
* and {@linkcode UseSavedViewsArgs.setState} arguments.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const { iTwinId, iModelId, client, viewport } = props;
|
|
27
|
+
* const savedViews = useSavedViews({ iTwinId, iModelId, client });
|
|
28
|
+
* const [isLoading, setIsLoading] = useState(true);
|
|
29
|
+
* useEffect(
|
|
30
|
+
* () => {
|
|
31
|
+
* return savedViews.startLoadingData(() => { setIsLoading(false); });
|
|
32
|
+
* },
|
|
33
|
+
* [],
|
|
34
|
+
* );
|
|
35
|
+
*
|
|
36
|
+
* if (isLoading) {
|
|
37
|
+
* return <MyLoadingState />;
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* const handleOpenSavedView = async (savedViewId) => {
|
|
41
|
+
* const savedViewData = await savedViews.lookupSavedViewData(savedViewId);
|
|
42
|
+
* await applySavedView(iModel, viewport, savedViewData);
|
|
43
|
+
* };
|
|
44
|
+
*
|
|
45
|
+
* return <MySavedViewsWidget savedViews={savedViews} onOpenSavedView={handleOpenSavedView} />;
|
|
11
46
|
*/
|
|
12
|
-
export
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
export const useSavedViews = Object.assign((args) => {
|
|
48
|
+
const { iTwinId, iModelId, client } = args;
|
|
49
|
+
const [state, setState] = useControlledState(args.state ?? emptyState, args.state, args.setState);
|
|
50
|
+
const stateRef = useRef({ iTwinId, iModelId, client, state, setState });
|
|
51
|
+
stateRef.current = { iTwinId, iModelId, client, state, setState };
|
|
52
|
+
const [events] = useState({
|
|
53
|
+
...createActions(stateRef),
|
|
54
|
+
startLoadingData: (callback) => {
|
|
55
|
+
const { iTwinId, iModelId, client, setState } = stateRef.current;
|
|
56
|
+
const abortController = new AbortController();
|
|
57
|
+
const signal = abortController.signal;
|
|
58
|
+
const observer = new CustomObserver((savedViewId) => {
|
|
59
|
+
void (async () => {
|
|
60
|
+
const loadSavedViewData = async () => {
|
|
61
|
+
let savedViewData;
|
|
62
|
+
try {
|
|
63
|
+
savedViewData = await client.getSavedViewDataById({ savedViewId, signal });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
savedViewData = undefined;
|
|
67
|
+
}
|
|
68
|
+
if (savedViewData && !signal.aborted) {
|
|
69
|
+
setState((prev) => {
|
|
70
|
+
const newState = { ...prev };
|
|
71
|
+
newState.savedViewData = new Map(prev.savedViewData);
|
|
72
|
+
newState.savedViewData.set(savedViewId, savedViewData);
|
|
73
|
+
return newState;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const loadThumbnail = async () => {
|
|
78
|
+
let thumbnailUrl;
|
|
79
|
+
try {
|
|
80
|
+
thumbnailUrl = await client.getThumbnailUrl({ savedViewId, signal });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
thumbnailUrl = undefined;
|
|
84
|
+
}
|
|
85
|
+
if (!signal.aborted) {
|
|
86
|
+
setState((prev) => {
|
|
87
|
+
const newState = { ...prev };
|
|
88
|
+
newState.thumbnails = new Map(prev.thumbnails);
|
|
89
|
+
newState.thumbnails.set(savedViewId, thumbnailUrl && _jsx("img", { src: thumbnailUrl }));
|
|
90
|
+
return newState;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
await Promise.all([loadSavedViewData(), loadThumbnail()]);
|
|
95
|
+
})();
|
|
96
|
+
});
|
|
97
|
+
void (async () => {
|
|
98
|
+
try {
|
|
99
|
+
const result = await getSavedViewInfo(client, iTwinId, iModelId, signal);
|
|
100
|
+
signal.throwIfAborted();
|
|
101
|
+
setState((prev) => {
|
|
102
|
+
const newState = { ...prev };
|
|
103
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
104
|
+
result.savedViews.forEach((savedView) => newState.savedViews.set(savedView.savedViewId, savedView));
|
|
105
|
+
newState.groups = new Map(prev.groups);
|
|
106
|
+
result.groups.forEach((group) => newState.groups.set(group.groupId, group));
|
|
107
|
+
newState.tags = new Map(prev.tags);
|
|
108
|
+
result.tags.forEach((tag) => newState.tags.set(tag.tagId, tag));
|
|
109
|
+
newState.thumbnails = new Map(prev.thumbnails);
|
|
110
|
+
result.savedViews
|
|
111
|
+
.filter(({ savedViewId }) => !prev.thumbnails.has(savedViewId))
|
|
112
|
+
.forEach(({ savedViewId }) => {
|
|
113
|
+
newState.thumbnails.set(savedViewId, _jsx(ThumbnailPlaceholder, { savedViewId: savedViewId, observer: observer }));
|
|
114
|
+
});
|
|
115
|
+
return newState;
|
|
116
|
+
});
|
|
117
|
+
callback?.();
|
|
30
118
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (callback) {
|
|
121
|
+
callback(error);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
})();
|
|
128
|
+
return () => {
|
|
129
|
+
abortController.abort();
|
|
130
|
+
observer.disconnect();
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return useMemo(() => ({ ...events, store: state }), [events, state]);
|
|
135
|
+
}, (args) => {
|
|
136
|
+
const { iTwinId, iModelId, client } = args;
|
|
137
|
+
const [state, setState] = useControlledState(args.state ?? emptyState, args.state, args.setState);
|
|
138
|
+
const stateRef = useRef({ iTwinId, iModelId, client, state, setState });
|
|
139
|
+
stateRef.current = { iTwinId, iModelId, client, state, setState };
|
|
140
|
+
const [events] = useState({
|
|
141
|
+
...createActions(stateRef),
|
|
142
|
+
startLoadingData: (callback) => {
|
|
143
|
+
const { iTwinId, iModelId, client, setState } = stateRef.current;
|
|
144
|
+
const abortController = new AbortController();
|
|
145
|
+
const signal = abortController.signal;
|
|
146
|
+
const observer = new CustomObserver((savedViewId) => {
|
|
147
|
+
void (async () => {
|
|
148
|
+
const loadSavedViewData = async () => {
|
|
149
|
+
let savedViewData;
|
|
150
|
+
try {
|
|
151
|
+
savedViewData = await client.getSavedViewDataById({ savedViewId, signal });
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
savedViewData = undefined;
|
|
155
|
+
}
|
|
156
|
+
if (savedViewData && !signal.aborted) {
|
|
157
|
+
setState((prev) => {
|
|
158
|
+
const newState = { ...prev };
|
|
159
|
+
newState.savedViewData = new Map(prev.savedViewData);
|
|
160
|
+
newState.savedViewData.set(savedViewId, savedViewData);
|
|
161
|
+
return newState;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const loadThumbnail = async () => {
|
|
34
166
|
let thumbnailUrl;
|
|
35
167
|
try {
|
|
36
|
-
thumbnailUrl = await
|
|
168
|
+
thumbnailUrl = await client.getThumbnailUrl({ savedViewId, signal });
|
|
37
169
|
}
|
|
38
170
|
catch {
|
|
39
171
|
thumbnailUrl = undefined;
|
|
40
172
|
}
|
|
41
|
-
if (signal.aborted) {
|
|
42
|
-
|
|
173
|
+
if (!signal.aborted) {
|
|
174
|
+
setState((prev) => {
|
|
175
|
+
const newState = { ...prev };
|
|
176
|
+
newState.thumbnails = new Map(prev.thumbnails);
|
|
177
|
+
newState.thumbnails.set(savedViewId, thumbnailUrl && _jsx("img", { src: thumbnailUrl }));
|
|
178
|
+
return newState;
|
|
179
|
+
});
|
|
43
180
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
181
|
+
};
|
|
182
|
+
await Promise.all([loadSavedViewData(), loadThumbnail()]);
|
|
183
|
+
})();
|
|
184
|
+
});
|
|
185
|
+
void (async () => {
|
|
186
|
+
try {
|
|
187
|
+
const result = await getSavedViewInfo(client, iTwinId, iModelId, signal);
|
|
188
|
+
signal.throwIfAborted();
|
|
189
|
+
setState((prev) => {
|
|
190
|
+
const newState = { ...prev };
|
|
191
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
192
|
+
result.savedViews.forEach((savedView) => newState.savedViews.set(savedView.savedViewId, savedView));
|
|
193
|
+
newState.groups = new Map(prev.groups);
|
|
194
|
+
result.groups.forEach((group) => newState.groups.set(group.groupId, group));
|
|
195
|
+
newState.tags = new Map(prev.tags);
|
|
196
|
+
result.tags.forEach((tag) => newState.tags.set(tag.tagId, tag));
|
|
197
|
+
newState.thumbnails = new Map(prev.thumbnails);
|
|
198
|
+
result.savedViews
|
|
199
|
+
.filter(({ savedViewId }) => !prev.thumbnails.has(savedViewId))
|
|
200
|
+
.forEach(({ savedViewId }) => {
|
|
201
|
+
newState.thumbnails.set(savedViewId, _jsx(ThumbnailPlaceholder, { savedViewId: savedViewId, observer: observer }));
|
|
58
202
|
});
|
|
59
|
-
|
|
203
|
+
return newState;
|
|
204
|
+
});
|
|
205
|
+
callback?.();
|
|
60
206
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const result = await args.client.getSavedViewInfo({
|
|
70
|
-
iTwinId: args.iTwinId,
|
|
71
|
-
iModelId: args.iModelId,
|
|
72
|
-
signal: signal,
|
|
73
|
-
});
|
|
74
|
-
if (signal.aborted) {
|
|
75
|
-
return;
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (callback) {
|
|
209
|
+
callback(error);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
76
214
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
215
|
+
})();
|
|
216
|
+
return () => {
|
|
217
|
+
abortController.abort();
|
|
218
|
+
observer.disconnect();
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
return useMemo(() => ({ ...events, store: state }), [events, state]);
|
|
223
|
+
}, {
|
|
224
|
+
/** Suggested initial state of custom Saved View stores. Immutable. */
|
|
225
|
+
emptyState,
|
|
226
|
+
});
|
|
227
|
+
class CustomObserver {
|
|
228
|
+
#observer;
|
|
229
|
+
#observedElements = new Map();
|
|
230
|
+
constructor(onObserved) {
|
|
231
|
+
this.#observer = new IntersectionObserver((entries) => {
|
|
232
|
+
for (const entry of entries) {
|
|
233
|
+
if (!entry.isIntersecting) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const savedViewId = this.#observedElements.get(entry.target);
|
|
237
|
+
if (savedViewId) {
|
|
238
|
+
onObserved(savedViewId);
|
|
95
239
|
}
|
|
96
240
|
}
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
observe(savedViewId, element) {
|
|
244
|
+
this.#observedElements.set(element, savedViewId);
|
|
245
|
+
this.#observer.observe(element);
|
|
246
|
+
}
|
|
247
|
+
unobserve(element) {
|
|
248
|
+
this.#observer.unobserve(element);
|
|
249
|
+
this.#observedElements.delete(element);
|
|
250
|
+
}
|
|
251
|
+
disconnect() {
|
|
252
|
+
this.#observer.disconnect();
|
|
107
253
|
}
|
|
254
|
+
}
|
|
255
|
+
async function getSavedViewInfo(client, iTwinId, iModelId, signal) {
|
|
256
|
+
const args = { iTwinId, iModelId, signal };
|
|
257
|
+
const collectSavedViews = async () => {
|
|
258
|
+
let savedViews = [];
|
|
259
|
+
const iterable = client.getSavedViews(args);
|
|
260
|
+
for await (const page of iterable) {
|
|
261
|
+
savedViews = savedViews.concat(page);
|
|
262
|
+
}
|
|
263
|
+
return savedViews;
|
|
264
|
+
};
|
|
265
|
+
const [savedViews, groups, tags] = await Promise.all([
|
|
266
|
+
collectSavedViews(),
|
|
267
|
+
client.getGroups(args),
|
|
268
|
+
client.getTags(args),
|
|
269
|
+
]);
|
|
270
|
+
const comparator = (a, b) => a.displayName.localeCompare(b.displayName);
|
|
108
271
|
return {
|
|
109
|
-
savedViews:
|
|
110
|
-
groups:
|
|
111
|
-
tags:
|
|
112
|
-
actions: provider,
|
|
272
|
+
savedViews: savedViews.sort(comparator),
|
|
273
|
+
groups: groups.sort(comparator),
|
|
274
|
+
tags: tags.sort(comparator),
|
|
113
275
|
};
|
|
114
276
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
277
|
+
function ThumbnailPlaceholder(props) {
|
|
278
|
+
const { savedViewId, observer } = props;
|
|
279
|
+
const divRef = useRef(null);
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
const div = divRef.current;
|
|
282
|
+
if (!div) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
observer.observe(savedViewId, div);
|
|
286
|
+
return () => observer.unobserve(div);
|
|
287
|
+
}, [savedViewId, observer]);
|
|
288
|
+
return _jsx("div", { ref: divRef });
|
|
120
289
|
}
|
|
121
|
-
|
|
122
|
-
|
|
290
|
+
// #endregion
|
|
291
|
+
// #region createActions
|
|
292
|
+
function createActions(stateRef) {
|
|
293
|
+
// When an action is invoked, stateRef contains the most recent parameters that were passed to
|
|
294
|
+
// useSavedViews hook. We need to ensure that the same parameters are used throughout the
|
|
295
|
+
// operation, so we capture their current values at the beginning of each action.
|
|
123
296
|
return {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
else {
|
|
135
|
-
newSavedView = await client.createSavedView({
|
|
136
|
-
iTwinId: iTwinId,
|
|
137
|
-
iModelId: iModelId,
|
|
138
|
-
savedView: typeof savedView === "string" ? { displayName: savedView } : savedView,
|
|
139
|
-
savedViewData,
|
|
140
|
-
signal,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
updateSavedViews((savedViews) => {
|
|
144
|
-
const entries = Array.from(savedViews.values());
|
|
145
|
-
entries.push(newSavedView);
|
|
146
|
-
entries.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
147
|
-
return new Map(entries.map((savedView) => [savedView.id, savedView]));
|
|
297
|
+
createSavedView: async (savedView, savedViewData) => {
|
|
298
|
+
const { iTwinId, iModelId, client, setState } = stateRef.current;
|
|
299
|
+
const newSavedView = await client.createSavedView({ ...savedView, iTwinId, iModelId, savedViewData });
|
|
300
|
+
setState((prev) => {
|
|
301
|
+
const savedViews = Array.from(prev.savedViews.values());
|
|
302
|
+
savedViews.splice(savedViews.findIndex(({ savedViewId }) => savedViewId === newSavedView.savedViewId), 1, newSavedView);
|
|
303
|
+
savedViews.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
304
|
+
const newState = { ...prev };
|
|
305
|
+
newState.savedViews = new Map(savedViews.map((savedView) => [savedView.savedViewId, savedView]));
|
|
306
|
+
return newState;
|
|
148
307
|
});
|
|
149
|
-
return newSavedView.
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
let prevName;
|
|
160
|
-
updateSavedView(savedViewId, (savedView) => {
|
|
161
|
-
prevName = savedView.displayName;
|
|
162
|
-
savedView.displayName = newName;
|
|
308
|
+
return newSavedView.savedViewId;
|
|
309
|
+
},
|
|
310
|
+
lookupSavedViewData: async (savedViewId) => {
|
|
311
|
+
const { client, setState } = stateRef.current;
|
|
312
|
+
const savedViewData = await client.getSavedViewDataById({ savedViewId });
|
|
313
|
+
setState((prev) => {
|
|
314
|
+
const newState = { ...prev };
|
|
315
|
+
newState.savedViewData = new Map(prev.savedViewData);
|
|
316
|
+
newState.savedViewData.set(savedViewId, savedViewData);
|
|
317
|
+
return newState;
|
|
163
318
|
});
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
throw error;
|
|
178
|
-
}
|
|
179
|
-
}),
|
|
180
|
-
shareSavedView: actionWrapper(async (savedViewId, share) => {
|
|
181
|
-
const savedView = ref.current.mostRecentState.savedViews.get(savedViewId);
|
|
182
|
-
if (!savedView || savedView.shared === share) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
updateSavedView(savedViewId, (savedView) => {
|
|
186
|
-
savedView.shared = share;
|
|
319
|
+
return savedViewData;
|
|
320
|
+
},
|
|
321
|
+
updateSavedView: async (savedViewId, savedView, savedViewData) => {
|
|
322
|
+
const { client, setState } = stateRef.current;
|
|
323
|
+
const updatedSavedView = await client.updateSavedView({ ...savedView, savedViewId, savedViewData });
|
|
324
|
+
setState((prev) => {
|
|
325
|
+
const savedViews = Array.from(prev.savedViews.values());
|
|
326
|
+
savedViews.splice(savedViews.findIndex(({ savedViewId }) => savedViewId === updatedSavedView.savedViewId), 1, updatedSavedView);
|
|
327
|
+
savedViews.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
328
|
+
const newState = { ...prev };
|
|
329
|
+
newState.savedViews = new Map(savedViews.map((savedView) => [savedView.savedViewId, savedView]));
|
|
330
|
+
return newState;
|
|
187
331
|
});
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
332
|
+
},
|
|
333
|
+
renameSavedView: async (savedViewId, newName) => {
|
|
334
|
+
const { client, setState } = stateRef.current;
|
|
335
|
+
await client.updateSavedView({ savedViewId, displayName: newName });
|
|
336
|
+
setState((prev) => {
|
|
337
|
+
const savedView = prev.savedViews.get(savedViewId);
|
|
338
|
+
if (!savedView) {
|
|
339
|
+
return prev;
|
|
340
|
+
}
|
|
341
|
+
const newSavedView = { ...savedView };
|
|
342
|
+
newSavedView.displayName = newName;
|
|
343
|
+
const newState = { ...prev };
|
|
344
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
345
|
+
newState.savedViews.set(savedViewId, newSavedView);
|
|
346
|
+
savedView.displayName = newName;
|
|
347
|
+
return newState;
|
|
203
348
|
});
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
349
|
+
},
|
|
350
|
+
shareSavedView: async (savedViewId, shared) => {
|
|
351
|
+
const { client, setState } = stateRef.current;
|
|
352
|
+
await client.updateSavedView({ savedViewId, shared });
|
|
353
|
+
setState((prev) => {
|
|
354
|
+
const savedView = prev.savedViews.get(savedViewId);
|
|
355
|
+
if (!savedView) {
|
|
356
|
+
return prev;
|
|
210
357
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
iModelId: iModelId,
|
|
218
|
-
group: { displayName: groupName },
|
|
219
|
-
signal,
|
|
358
|
+
const newSavedView = { ...savedView };
|
|
359
|
+
newSavedView.shared = shared;
|
|
360
|
+
const newState = { ...prev };
|
|
361
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
362
|
+
newState.savedViews.set(savedViewId, newSavedView);
|
|
363
|
+
return newState;
|
|
220
364
|
});
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
365
|
+
},
|
|
366
|
+
deleteSavedView: async (savedViewId) => {
|
|
367
|
+
const { client, setState } = stateRef.current;
|
|
368
|
+
await client.deleteSavedView({ savedViewId });
|
|
369
|
+
setState((prev) => {
|
|
370
|
+
const newState = { ...prev };
|
|
371
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
372
|
+
newState.savedViews.delete(savedViewId);
|
|
373
|
+
return newState;
|
|
226
374
|
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
375
|
+
},
|
|
376
|
+
createGroup: async (groupName) => {
|
|
377
|
+
const { iTwinId, iModelId, client, setState } = stateRef.current;
|
|
378
|
+
const newGroup = await client.createGroup({ iTwinId, iModelId, displayName: groupName });
|
|
379
|
+
setState((prev) => {
|
|
380
|
+
const groups = Array.from(prev.groups.values());
|
|
381
|
+
groups.push(newGroup);
|
|
382
|
+
groups.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
383
|
+
const newState = { ...prev };
|
|
384
|
+
newState.groups = new Map(groups.map((group) => [group.groupId, group]));
|
|
385
|
+
return newState;
|
|
233
386
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
});
|
|
387
|
+
return newGroup.groupId;
|
|
388
|
+
},
|
|
389
|
+
renameGroup: async (groupId, newName) => {
|
|
390
|
+
const { client, setState } = stateRef.current;
|
|
391
|
+
await client.updateGroup({ groupId, displayName: newName });
|
|
392
|
+
setState((prev) => {
|
|
393
|
+
const group = prev.groups.get(groupId);
|
|
394
|
+
if (!group) {
|
|
395
|
+
return prev;
|
|
244
396
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
updateGroup(groupId, (group) => {
|
|
254
|
-
group.shared = share;
|
|
397
|
+
const newGroup = { ...group };
|
|
398
|
+
newGroup.displayName = newName;
|
|
399
|
+
const newState = { ...prev };
|
|
400
|
+
newState.groups = new Map(prev.groups);
|
|
401
|
+
newState.groups.set(groupId, newGroup);
|
|
402
|
+
return newState;
|
|
255
403
|
});
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
savedView.groupId = groupId;
|
|
404
|
+
},
|
|
405
|
+
shareGroup: async (groupId, shared) => {
|
|
406
|
+
const { client, setState } = stateRef.current;
|
|
407
|
+
await client.updateGroup({ groupId, shared });
|
|
408
|
+
setState((prev) => {
|
|
409
|
+
const group = prev.groups.get(groupId);
|
|
410
|
+
if (!group) {
|
|
411
|
+
return prev;
|
|
412
|
+
}
|
|
413
|
+
const newGroup = { ...group };
|
|
414
|
+
newGroup.shared = shared;
|
|
415
|
+
const newState = { ...prev };
|
|
416
|
+
newState.groups = new Map(prev.groups);
|
|
417
|
+
newState.groups.set(groupId, newGroup);
|
|
418
|
+
return newState;
|
|
272
419
|
});
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
group: { displayName: groupName },
|
|
289
|
-
signal,
|
|
420
|
+
},
|
|
421
|
+
moveToGroup: async (savedViewId, groupId) => {
|
|
422
|
+
const { client, setState } = stateRef.current;
|
|
423
|
+
await client.updateSavedView({ savedViewId, groupId });
|
|
424
|
+
setState((prev) => {
|
|
425
|
+
const savedView = prev.savedViews.get(savedViewId);
|
|
426
|
+
if (!savedView) {
|
|
427
|
+
return prev;
|
|
428
|
+
}
|
|
429
|
+
const newSavedView = { ...savedView };
|
|
430
|
+
newSavedView.groupId = groupId;
|
|
431
|
+
const newState = { ...prev };
|
|
432
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
433
|
+
newState.savedViews.set(savedViewId, newSavedView);
|
|
434
|
+
return newState;
|
|
290
435
|
});
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
436
|
+
},
|
|
437
|
+
deleteGroup: async (groupId) => {
|
|
438
|
+
const { client, setState } = stateRef.current;
|
|
439
|
+
await client.deleteGroup({ groupId });
|
|
440
|
+
setState((prev) => {
|
|
441
|
+
const newState = { ...prev };
|
|
442
|
+
newState.groups = new Map(prev.groups);
|
|
443
|
+
newState.groups.delete(groupId);
|
|
444
|
+
return newState;
|
|
299
445
|
});
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}),
|
|
310
|
-
deleteGroup: actionWrapper(async (groupId) => {
|
|
311
|
-
let prevGroups;
|
|
312
|
-
updateGroups((groups) => {
|
|
313
|
-
prevGroups = new Map(groups);
|
|
314
|
-
groups.delete(groupId);
|
|
446
|
+
},
|
|
447
|
+
createTag: async (tagName) => {
|
|
448
|
+
const { iTwinId, iModelId, client, setState } = stateRef.current;
|
|
449
|
+
const newTag = await client.createTag({ iTwinId, iModelId, displayName: tagName });
|
|
450
|
+
setState((prev) => {
|
|
451
|
+
const newState = { ...prev };
|
|
452
|
+
newState.tags = new Map(prev.tags);
|
|
453
|
+
newState.tags.set(newTag.tagId, newTag);
|
|
454
|
+
return newState;
|
|
315
455
|
});
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
updateGroups(() => prevGroups);
|
|
322
|
-
}
|
|
323
|
-
throw error;
|
|
324
|
-
}
|
|
325
|
-
}),
|
|
326
|
-
addTag: actionWrapper(async (savedViewId, tagId) => {
|
|
327
|
-
const savedView = ref.current.mostRecentState.savedViews.get(savedViewId);
|
|
456
|
+
return newTag.tagId;
|
|
457
|
+
},
|
|
458
|
+
addTag: async (savedViewId, tagId) => {
|
|
459
|
+
const { client, state, setState } = stateRef.current;
|
|
460
|
+
const savedView = state.savedViews.get(savedViewId);
|
|
328
461
|
if (!savedView) {
|
|
329
462
|
return;
|
|
330
463
|
}
|
|
331
464
|
const tagIds = savedView.tagIds?.slice() ?? [];
|
|
332
465
|
tagIds.push(tagId);
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
catch (error) {
|
|
347
|
-
updateSavedView(savedViewId, (savedView) => {
|
|
348
|
-
savedView.tagIds = prevTagIds;
|
|
349
|
-
});
|
|
350
|
-
throw error;
|
|
351
|
-
}
|
|
352
|
-
}),
|
|
353
|
-
addNewTag: actionWrapper(async (savedViewId, tagName) => {
|
|
354
|
-
const savedView = ref.current.mostRecentState.savedViews.get(savedViewId);
|
|
355
|
-
if (!savedView) {
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
const tag = await client.createTag({
|
|
359
|
-
iTwinId: iTwinId,
|
|
360
|
-
iModelId: iModelId,
|
|
361
|
-
displayName: tagName,
|
|
362
|
-
signal,
|
|
363
|
-
});
|
|
364
|
-
updateTags((tags) => {
|
|
365
|
-
tags.set(tag.id, tag);
|
|
366
|
-
});
|
|
367
|
-
const tagIds = savedView.tagIds?.slice() ?? [];
|
|
368
|
-
tagIds.push(tag.id);
|
|
369
|
-
tagIds.sort((a, b) => {
|
|
370
|
-
const aDisplayName = ref.current.mostRecentState.tags.get(a)?.displayName;
|
|
371
|
-
const bDisplayName = ref.current.mostRecentState.tags.get(b)?.displayName;
|
|
372
|
-
return aDisplayName?.toLowerCase().localeCompare(bDisplayName?.toLowerCase() ?? "") ?? -1;
|
|
373
|
-
});
|
|
374
|
-
let prevTagIds;
|
|
375
|
-
updateSavedView(savedViewId, (savedView) => {
|
|
376
|
-
prevTagIds = savedView.tagIds;
|
|
377
|
-
savedView.tagIds = tagIds;
|
|
466
|
+
await client.updateSavedView({ savedViewId, tagIds });
|
|
467
|
+
setState((prev) => {
|
|
468
|
+
const savedView = prev.savedViews.get(savedViewId);
|
|
469
|
+
if (!savedView) {
|
|
470
|
+
return prev;
|
|
471
|
+
}
|
|
472
|
+
const newSavedView = { ...savedView };
|
|
473
|
+
newSavedView.tagIds = savedView.tagIds?.slice() ?? [];
|
|
474
|
+
newSavedView.tagIds.push(tagId);
|
|
475
|
+
const newState = { ...prev };
|
|
476
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
477
|
+
newState.savedViews.set(savedViewId, newSavedView);
|
|
478
|
+
return newState;
|
|
378
479
|
});
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
savedView.tagIds = prevTagIds;
|
|
385
|
-
});
|
|
386
|
-
throw error;
|
|
387
|
-
}
|
|
388
|
-
}),
|
|
389
|
-
removeTag: actionWrapper(async (savedViewId, tagId) => {
|
|
390
|
-
const savedView = ref.current.mostRecentState.savedViews.get(savedViewId);
|
|
391
|
-
if (!savedView) {
|
|
480
|
+
},
|
|
481
|
+
removeTag: async (savedViewId, tagId) => {
|
|
482
|
+
const { client, state, setState } = stateRef.current;
|
|
483
|
+
const savedView = state.savedViews.get(savedViewId);
|
|
484
|
+
if (!savedView || !savedView.tagIds) {
|
|
392
485
|
return;
|
|
393
486
|
}
|
|
394
|
-
const
|
|
395
|
-
if (
|
|
487
|
+
const newTagIds = savedView.tagIds.splice(savedView.tagIds.indexOf(tagId), 1) ?? [];
|
|
488
|
+
if (newTagIds.length === savedView.tagIds.length) {
|
|
396
489
|
return;
|
|
397
490
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
savedView
|
|
491
|
+
await client.updateSavedView({ savedViewId, tagIds: newTagIds });
|
|
492
|
+
setState((prev) => {
|
|
493
|
+
const savedView = prev.savedViews.get(savedViewId);
|
|
494
|
+
if (!savedView) {
|
|
495
|
+
return prev;
|
|
496
|
+
}
|
|
497
|
+
const newSavedView = { ...savedView };
|
|
498
|
+
newSavedView.tagIds = newTagIds;
|
|
499
|
+
const newState = { ...prev };
|
|
500
|
+
newState.savedViews = new Map(prev.savedViews);
|
|
501
|
+
newState.savedViews.set(savedViewId, newSavedView);
|
|
502
|
+
return newState;
|
|
402
503
|
});
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}),
|
|
413
|
-
uploadThumbnail: actionWrapper(async (savedViewId, imageDataUrl) => {
|
|
414
|
-
let prevThumnbnail;
|
|
415
|
-
updateSavedView(savedViewId, (savedView) => {
|
|
416
|
-
prevThumnbnail = savedView.thumbnail;
|
|
417
|
-
savedView.thumbnail = imageDataUrl;
|
|
504
|
+
},
|
|
505
|
+
deleteTag: async (tagId) => {
|
|
506
|
+
const { client, setState } = stateRef.current;
|
|
507
|
+
await client.deleteTag({ tagId });
|
|
508
|
+
setState((prev) => {
|
|
509
|
+
const newState = { ...prev };
|
|
510
|
+
newState.tags = new Map(prev.tags);
|
|
511
|
+
newState.tags.delete(tagId);
|
|
512
|
+
return newState;
|
|
418
513
|
});
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
throw error;
|
|
429
|
-
}
|
|
430
|
-
}),
|
|
431
|
-
};
|
|
432
|
-
/**
|
|
433
|
-
* Serializes action execution and notifies when action processing begins and ends. Continues to execute actions
|
|
434
|
-
* after cancellation.
|
|
435
|
-
*/
|
|
436
|
-
function actionWrapper(callback) {
|
|
437
|
-
return async (...args) => {
|
|
438
|
-
// eslint-disable-next-line no-async-promise-executor
|
|
439
|
-
return new Promise(async (resolve, reject) => {
|
|
440
|
-
ref.current.actionQueue.push(async () => {
|
|
441
|
-
if (signal.aborted) {
|
|
442
|
-
reject(signal.reason);
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
try {
|
|
446
|
-
resolve(await callback(...args));
|
|
447
|
-
}
|
|
448
|
-
catch (error) {
|
|
449
|
-
reject(error);
|
|
450
|
-
if (isAbortError(error)) {
|
|
451
|
-
// It's a cancellation error, no need to report it
|
|
452
|
-
}
|
|
453
|
-
else {
|
|
454
|
-
try {
|
|
455
|
-
onUpdateError(error);
|
|
456
|
-
}
|
|
457
|
-
catch { }
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
// If there are no other queued actions, start executing the queue
|
|
462
|
-
if (ref.current.actionQueue.length === 1) {
|
|
463
|
-
onUpdateInProgress();
|
|
464
|
-
// By the time the first action completes, other actions may have been queued
|
|
465
|
-
while (ref.current.actionQueue.length > 0) {
|
|
466
|
-
await ref.current.actionQueue[0]();
|
|
467
|
-
ref.current.actionQueue.shift();
|
|
468
|
-
}
|
|
469
|
-
onUpdateComplete();
|
|
470
|
-
}
|
|
514
|
+
},
|
|
515
|
+
uploadThumbnail: async (savedViewId, imageDataUrl) => {
|
|
516
|
+
const { client, setState } = stateRef.current;
|
|
517
|
+
await client.uploadThumbnail({ savedViewId, image: imageDataUrl });
|
|
518
|
+
setState((prev) => {
|
|
519
|
+
const newState = { ...prev };
|
|
520
|
+
newState.thumbnails = new Map(prev.thumbnails);
|
|
521
|
+
newState.thumbnails.set(savedViewId, _jsx("img", { src: imageDataUrl }));
|
|
522
|
+
return newState;
|
|
471
523
|
});
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
function updateSavedViews(callback) {
|
|
475
|
-
if (signal.aborted) {
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
setState((prev) => {
|
|
479
|
-
const store = { ...prev };
|
|
480
|
-
const savedViews = new Map(prev.savedViews);
|
|
481
|
-
store.savedViews = callback(savedViews) ?? savedViews;
|
|
482
|
-
return store;
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
function updateSavedView(savedViewId, callback) {
|
|
486
|
-
if (signal.aborted) {
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
setState((prev) => {
|
|
490
|
-
const store = { ...prev };
|
|
491
|
-
store.savedViews = new Map(prev.savedViews);
|
|
492
|
-
const prevSavedView = store.savedViews.get(savedViewId);
|
|
493
|
-
if (!prevSavedView) {
|
|
494
|
-
return prev;
|
|
495
|
-
}
|
|
496
|
-
const savedView = { ...prevSavedView };
|
|
497
|
-
store.savedViews.set(savedViewId, callback(savedView) ?? savedView);
|
|
498
|
-
return store;
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
function updateGroups(callback) {
|
|
502
|
-
if (signal.aborted) {
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
setState((prev) => {
|
|
506
|
-
const store = { ...prev };
|
|
507
|
-
const groups = new Map(prev.groups);
|
|
508
|
-
store.groups = callback(groups) ?? groups;
|
|
509
|
-
return store;
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
function updateGroup(groupId, callback) {
|
|
513
|
-
if (signal.aborted) {
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
setState((prev) => {
|
|
517
|
-
const store = { ...prev };
|
|
518
|
-
store.groups = new Map(prev.groups);
|
|
519
|
-
const prevGroup = store.groups.get(groupId);
|
|
520
|
-
if (!prevGroup) {
|
|
521
|
-
return prev;
|
|
522
|
-
}
|
|
523
|
-
const group = { ...prevGroup };
|
|
524
|
-
store.groups.set(groupId, callback(group) ?? group);
|
|
525
|
-
return store;
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
function updateTags(callback) {
|
|
529
|
-
if (signal.aborted) {
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
setState((prev) => {
|
|
533
|
-
const store = { ...prev };
|
|
534
|
-
store.tags = new Map(prev.tags);
|
|
535
|
-
callback(store.tags);
|
|
536
|
-
return store;
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
function isAbortError(error) {
|
|
541
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
542
|
-
}
|
|
543
|
-
function ThumbnailPlaceholder(props) {
|
|
544
|
-
const divRef = useRef(null);
|
|
545
|
-
useEffect(() => {
|
|
546
|
-
const div = divRef.current;
|
|
547
|
-
if (!div) {
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
props.observer.observe(div);
|
|
551
|
-
return () => props.observer.unobserve(div);
|
|
552
|
-
}, [props.observer]);
|
|
553
|
-
return _jsx("div", { ref: divRef, "data-saved-view-id": props.savedViewId });
|
|
524
|
+
},
|
|
525
|
+
};
|
|
554
526
|
}
|