@svadmin/ui 0.78.1 → 0.78.2
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/components/AutoTable.svelte +183 -962
- package/dist/components/list-column-preferences.svelte.d.ts +29 -0
- package/dist/components/list-column-preferences.svelte.js +92 -0
- package/dist/components/list-deletion.svelte.d.ts +38 -0
- package/dist/components/list-deletion.svelte.js +111 -0
- package/dist/components/list-selection.svelte.d.ts +31 -0
- package/dist/components/list-selection.svelte.js +100 -0
- package/dist/components/list-state.svelte.d.ts +49 -0
- package/dist/components/list-state.svelte.js +225 -0
- package/dist/components/saved-list-views.svelte.d.ts +42 -0
- package/dist/components/saved-list-views.svelte.js +405 -0
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
2
|
+
import { type ListPreferenceScope, type SavedListView, type SavedListViewAccess, type SavedListViewProvider, type SavedListViewState, type SavedListViewSubject } from './saved-list-views.js';
|
|
3
|
+
interface SavedListViewsOptions {
|
|
4
|
+
scope: () => ListPreferenceScope;
|
|
5
|
+
columns: () => Set<string>;
|
|
6
|
+
provider: () => SavedListViewProvider | undefined;
|
|
7
|
+
isScopeLoaded: () => boolean;
|
|
8
|
+
readPreference: (scope: ListPreferenceScope, key: string, legacyKey: string) => string | null;
|
|
9
|
+
hasExplicitURLState: boolean;
|
|
10
|
+
canApplyRemoteDefault: () => boolean;
|
|
11
|
+
captureState: () => SavedListViewState;
|
|
12
|
+
applyState: (state: SavedListViewState) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function createSavedListViews(options: SavedListViewsOptions): {
|
|
15
|
+
initialSavedView: SavedListView | undefined;
|
|
16
|
+
readonly availableSavedViews: SavedListView[];
|
|
17
|
+
readonly activeSavedViewId: string | undefined;
|
|
18
|
+
readonly activeSavedViewName: string | undefined;
|
|
19
|
+
savedViewName: string;
|
|
20
|
+
savedViewsOpen: boolean;
|
|
21
|
+
savedViewSource: "local" | "team" | "system";
|
|
22
|
+
readonly remoteViewsLoading: boolean;
|
|
23
|
+
readonly remoteViewsFailed: boolean;
|
|
24
|
+
readonly savedViewMutationPending: boolean;
|
|
25
|
+
readonly savedViewMutationError: "failure" | "conflict" | undefined;
|
|
26
|
+
accessDrafts: SvelteMap<string, SavedListViewAccess>;
|
|
27
|
+
accessSubjects: SvelteMap<string, SavedListViewSubject[]>;
|
|
28
|
+
accessSubjectsLoading: SvelteMap<string, boolean>;
|
|
29
|
+
accessSubjectsFailed: SvelteMap<string, boolean>;
|
|
30
|
+
accessSubjectQueries: SvelteMap<string, string>;
|
|
31
|
+
restoreScope: (scope: ListPreferenceScope) => SavedListView | undefined;
|
|
32
|
+
markSavedViewDirty: () => void;
|
|
33
|
+
clearActiveSavedView: () => void;
|
|
34
|
+
applySavedView: (view: SavedListView) => void;
|
|
35
|
+
saveCurrentView: () => void;
|
|
36
|
+
deleteSavedView: (id: string) => void;
|
|
37
|
+
setRemoteDefault: (view: SavedListView) => void;
|
|
38
|
+
saveRemoteAccess: (view: SavedListView) => void;
|
|
39
|
+
loadAccessSubjects: (view: SavedListView, query?: string) => void;
|
|
40
|
+
reload(): void;
|
|
41
|
+
};
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
3
|
+
import { activeSavedListViewStorageKey, cloneSavedListViewState, decodeRemoteSavedListViews, decodeSavedListViewAccess, decodeSavedListViewMutationResult, decodeSavedListViewRemoveResult, decodeSavedListViewSubjects, legacyActiveSavedListViewStorageKey, legacySavedListViewsStorageKey, listPreferenceScopeId, readSavedListViews, savedListViewsStorageKey, serializeSavedListViews, } from './saved-list-views.js';
|
|
4
|
+
export function createSavedListViews(options) {
|
|
5
|
+
const listPreferenceScope = $derived(options.scope());
|
|
6
|
+
const savedViewColumnIds = $derived(options.columns());
|
|
7
|
+
const savedViewProvider = $derived(options.provider());
|
|
8
|
+
const preferenceScopeIsLoaded = options.isScopeLoaded;
|
|
9
|
+
const hasExplicitURLState = options.hasExplicitURLState;
|
|
10
|
+
function readPreferences(scope) {
|
|
11
|
+
const savedViews = readSavedListViews(options.readPreference(scope, savedListViewsStorageKey(scope), legacySavedListViewsStorageKey(scope.resourceName)), savedViewColumnIds);
|
|
12
|
+
const candidate = options.readPreference(scope, activeSavedListViewStorageKey(scope), legacyActiveSavedListViewStorageKey(scope.resourceName));
|
|
13
|
+
const activeSavedView = savedViews.find(view => view.id === candidate);
|
|
14
|
+
return { savedViews, activeSavedView };
|
|
15
|
+
}
|
|
16
|
+
const initial = untrack(() => readPreferences(listPreferenceScope));
|
|
17
|
+
const initialSavedView = hasExplicitURLState ? undefined : initial.activeSavedView;
|
|
18
|
+
let savedViews = $state(initial.savedViews);
|
|
19
|
+
let activeSavedViewId = $state(initialSavedView?.id);
|
|
20
|
+
let savedViewName = $state('');
|
|
21
|
+
let savedViewsOpen = $state(false);
|
|
22
|
+
let savedViewSource = $state('local');
|
|
23
|
+
let remoteSavedViews = $state([]);
|
|
24
|
+
let remoteViewsLoading = $state(false);
|
|
25
|
+
let remoteViewsFailed = $state(false);
|
|
26
|
+
let remoteViewsReload = $state(0);
|
|
27
|
+
let remoteDefaultEligible = true;
|
|
28
|
+
let savedViewMutationPending = $state(false);
|
|
29
|
+
let savedViewMutationError = $state();
|
|
30
|
+
let savedViewMutationEpoch = 0;
|
|
31
|
+
let remoteViewEpoch = 0;
|
|
32
|
+
const accessDrafts = new SvelteMap();
|
|
33
|
+
const accessSubjects = new SvelteMap();
|
|
34
|
+
const accessSubjectsLoading = new SvelteMap();
|
|
35
|
+
const accessSubjectsFailed = new SvelteMap();
|
|
36
|
+
const accessSubjectQueries = new SvelteMap();
|
|
37
|
+
const accessSubjectRequests = new Map();
|
|
38
|
+
const availableSavedViews = $derived.by(() => {
|
|
39
|
+
const ids = new Set();
|
|
40
|
+
return [...remoteSavedViews, ...savedViews].filter(view => {
|
|
41
|
+
if (ids.has(view.id))
|
|
42
|
+
return false;
|
|
43
|
+
ids.add(view.id);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
const activeSavedViewName = $derived(availableSavedViews.find(view => view.id === activeSavedViewId)?.name);
|
|
48
|
+
function persistSavedViews() {
|
|
49
|
+
if (typeof window === 'undefined' || !preferenceScopeIsLoaded())
|
|
50
|
+
return;
|
|
51
|
+
try {
|
|
52
|
+
localStorage.setItem(savedListViewsStorageKey(listPreferenceScope), serializeSavedListViews(savedViews));
|
|
53
|
+
}
|
|
54
|
+
catch { /* ignore quota errors */ }
|
|
55
|
+
}
|
|
56
|
+
function persistActiveSavedView() {
|
|
57
|
+
if (typeof window === 'undefined' || !preferenceScopeIsLoaded())
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
const key = activeSavedListViewStorageKey(listPreferenceScope);
|
|
61
|
+
if (activeSavedViewId)
|
|
62
|
+
localStorage.setItem(key, activeSavedViewId);
|
|
63
|
+
else
|
|
64
|
+
localStorage.removeItem(key);
|
|
65
|
+
}
|
|
66
|
+
catch { /* ignore quota errors */ }
|
|
67
|
+
}
|
|
68
|
+
function markSavedViewDirty() {
|
|
69
|
+
remoteDefaultEligible = false;
|
|
70
|
+
if (!activeSavedViewId)
|
|
71
|
+
return;
|
|
72
|
+
clearActiveSavedView();
|
|
73
|
+
}
|
|
74
|
+
function clearActiveSavedView() {
|
|
75
|
+
remoteDefaultEligible = false;
|
|
76
|
+
activeSavedViewId = undefined;
|
|
77
|
+
persistActiveSavedView();
|
|
78
|
+
}
|
|
79
|
+
function applySavedView(view) {
|
|
80
|
+
remoteDefaultEligible = false;
|
|
81
|
+
const state = cloneSavedListViewState(view.state);
|
|
82
|
+
activeSavedViewId = view.id;
|
|
83
|
+
persistActiveSavedView();
|
|
84
|
+
options.applyState(state);
|
|
85
|
+
}
|
|
86
|
+
// 表格完成列偏好和查询恢复后才打开持久化门,避免把旧租户状态写入新作用域。
|
|
87
|
+
function restoreScope(scope) {
|
|
88
|
+
remoteDefaultEligible = true;
|
|
89
|
+
const preferences = readPreferences(scope);
|
|
90
|
+
savedViewName = '';
|
|
91
|
+
savedViewsOpen = false;
|
|
92
|
+
savedViews = preferences.savedViews;
|
|
93
|
+
remoteSavedViews = [];
|
|
94
|
+
activeSavedViewId = preferences.activeSavedView?.id;
|
|
95
|
+
return preferences.activeSavedView;
|
|
96
|
+
}
|
|
97
|
+
$effect(() => {
|
|
98
|
+
const provider = savedViewProvider;
|
|
99
|
+
const scope = listPreferenceScope;
|
|
100
|
+
const columns = savedViewColumnIds;
|
|
101
|
+
void remoteViewsReload;
|
|
102
|
+
remoteSavedViews = [];
|
|
103
|
+
remoteViewsLoading = false;
|
|
104
|
+
remoteViewsFailed = false;
|
|
105
|
+
accessDrafts.clear();
|
|
106
|
+
accessSubjects.clear();
|
|
107
|
+
accessSubjectsLoading.clear();
|
|
108
|
+
accessSubjectsFailed.clear();
|
|
109
|
+
accessSubjectQueries.clear();
|
|
110
|
+
accessSubjectRequests.clear();
|
|
111
|
+
savedViewMutationEpoch += 1;
|
|
112
|
+
savedViewMutationPending = false;
|
|
113
|
+
savedViewMutationError = undefined;
|
|
114
|
+
savedViewSource = 'local';
|
|
115
|
+
if (!provider || !preferenceScopeIsLoaded())
|
|
116
|
+
return;
|
|
117
|
+
const epoch = ++remoteViewEpoch;
|
|
118
|
+
remoteViewsLoading = true;
|
|
119
|
+
void Promise.resolve().then(() => provider.list({ ...scope })).then(value => {
|
|
120
|
+
if (epoch !== remoteViewEpoch || !preferenceScopeIsLoaded())
|
|
121
|
+
return;
|
|
122
|
+
remoteSavedViews = decodeRemoteSavedListViews(value, columns, !!(provider.save || provider.remove || provider.setDefault || provider.updateAccess));
|
|
123
|
+
if (options.canApplyRemoteDefault() && remoteDefaultEligible) {
|
|
124
|
+
const defaults = remoteSavedViews.filter(view => view.default === true);
|
|
125
|
+
if (!hasExplicitURLState && !activeSavedViewId && defaults.length === 1 && defaults[0]) {
|
|
126
|
+
applySavedView(defaults[0]);
|
|
127
|
+
remoteDefaultEligible = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}).catch(() => {
|
|
131
|
+
if (epoch === remoteViewEpoch) {
|
|
132
|
+
remoteSavedViews = [];
|
|
133
|
+
remoteViewsFailed = true;
|
|
134
|
+
}
|
|
135
|
+
}).finally(() => {
|
|
136
|
+
if (epoch === remoteViewEpoch)
|
|
137
|
+
remoteViewsLoading = false;
|
|
138
|
+
});
|
|
139
|
+
return () => {
|
|
140
|
+
remoteViewEpoch += 1;
|
|
141
|
+
savedViewMutationEpoch += 1;
|
|
142
|
+
accessSubjectRequests.clear();
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
function saveCurrentView() {
|
|
146
|
+
if (!preferenceScopeIsLoaded() || savedViewMutationPending)
|
|
147
|
+
return;
|
|
148
|
+
remoteDefaultEligible = false;
|
|
149
|
+
const name = savedViewName.trim().slice(0, 60);
|
|
150
|
+
if (!name)
|
|
151
|
+
return;
|
|
152
|
+
const state = options.captureState();
|
|
153
|
+
if (savedViewSource !== 'local') {
|
|
154
|
+
const existing = remoteSavedViews.find(view => view.source === savedViewSource
|
|
155
|
+
&& view.name.toLocaleLowerCase() === name.toLocaleLowerCase());
|
|
156
|
+
if (!savedViewProvider?.save || (existing && (existing.readOnly || !existing.version)))
|
|
157
|
+
return;
|
|
158
|
+
void mutateRemoteView({
|
|
159
|
+
id: existing?.id ?? crypto.randomUUID(), name, state,
|
|
160
|
+
source: savedViewSource, expectedVersion: existing?.version ?? null,
|
|
161
|
+
});
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const existing = savedViews.find(view => view.name.toLocaleLowerCase() === name.toLocaleLowerCase());
|
|
165
|
+
const view = {
|
|
166
|
+
id: existing?.id ?? `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
167
|
+
name, state,
|
|
168
|
+
};
|
|
169
|
+
savedViews = existing
|
|
170
|
+
? savedViews.map(candidate => candidate.id === existing.id ? view : candidate)
|
|
171
|
+
: [view, ...savedViews].slice(0, 25);
|
|
172
|
+
activeSavedViewId = view.id;
|
|
173
|
+
savedViewName = '';
|
|
174
|
+
persistSavedViews();
|
|
175
|
+
persistActiveSavedView();
|
|
176
|
+
}
|
|
177
|
+
function deleteSavedView(id) {
|
|
178
|
+
if (!preferenceScopeIsLoaded() || savedViewMutationPending)
|
|
179
|
+
return;
|
|
180
|
+
const remote = remoteSavedViews.find(view => view.id === id);
|
|
181
|
+
if (remote) {
|
|
182
|
+
if (!savedViewProvider?.remove || remote.readOnly || !remote.version)
|
|
183
|
+
return;
|
|
184
|
+
void mutateRemoteView({
|
|
185
|
+
id, expectedVersion: remote.version, source: remote.source === 'system' ? 'system' : 'team',
|
|
186
|
+
name: remote.name, state: remote.state,
|
|
187
|
+
}, true);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
savedViews = savedViews.filter(view => view.id !== id);
|
|
191
|
+
if (activeSavedViewId === id)
|
|
192
|
+
activeSavedViewId = undefined;
|
|
193
|
+
persistSavedViews();
|
|
194
|
+
persistActiveSavedView();
|
|
195
|
+
}
|
|
196
|
+
async function mutateRemoteView(mutation, remove = false) {
|
|
197
|
+
const provider = savedViewProvider;
|
|
198
|
+
if (!provider || savedViewMutationPending || !preferenceScopeIsLoaded()
|
|
199
|
+
|| (remove && (mutation.expectedVersion === null || !provider.remove))
|
|
200
|
+
|| (!remove && !provider.save))
|
|
201
|
+
return;
|
|
202
|
+
const scope = { ...listPreferenceScope };
|
|
203
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
204
|
+
const columns = savedViewColumnIds;
|
|
205
|
+
const epoch = ++savedViewMutationEpoch;
|
|
206
|
+
const current = () => epoch === savedViewMutationEpoch && provider === savedViewProvider
|
|
207
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope) && preferenceScopeIsLoaded();
|
|
208
|
+
savedViewMutationPending = true;
|
|
209
|
+
savedViewMutationError = undefined;
|
|
210
|
+
try {
|
|
211
|
+
const result = remove
|
|
212
|
+
? await provider.remove?.(scope, { id: mutation.id, expectedVersion: mutation.expectedVersion ?? 0 })
|
|
213
|
+
: await provider.save?.(scope, { ...mutation, state: cloneSavedListViewState(mutation.state) });
|
|
214
|
+
if (!current())
|
|
215
|
+
return;
|
|
216
|
+
if (remove && mutation.expectedVersion !== null && decodeSavedListViewRemoveResult(result, {
|
|
217
|
+
id: mutation.id, expectedVersion: mutation.expectedVersion,
|
|
218
|
+
})) {
|
|
219
|
+
remoteSavedViews = remoteSavedViews.filter(view => view.id !== mutation.id);
|
|
220
|
+
if (activeSavedViewId === mutation.id)
|
|
221
|
+
activeSavedViewId = undefined;
|
|
222
|
+
persistActiveSavedView();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const decoded = decodeSavedListViewMutationResult(result, columns, mutation);
|
|
226
|
+
if (!decoded || (remove && decoded.ok)) {
|
|
227
|
+
savedViewMutationError = 'failure';
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (!decoded.ok) {
|
|
231
|
+
savedViewMutationError = 'conflict';
|
|
232
|
+
// 保留本地查询;用户必须重新读取后再决定如何修改。
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const view = { ...decoded.view, readOnly: decoded.view.readOnly !== false };
|
|
236
|
+
remoteSavedViews = [view, ...remoteSavedViews.filter(candidate => candidate.id !== view.id)];
|
|
237
|
+
savedViewName = '';
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
if (current())
|
|
241
|
+
savedViewMutationError = 'failure';
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
if (current())
|
|
245
|
+
savedViewMutationPending = false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function setRemoteDefault(view) {
|
|
249
|
+
const provider = savedViewProvider;
|
|
250
|
+
if (!provider?.setDefault || savedViewMutationPending || view.readOnly || !view.version
|
|
251
|
+
|| savedViewMutationError === 'conflict' || !preferenceScopeIsLoaded()
|
|
252
|
+
|| (view.source !== 'team' && view.source !== 'system'))
|
|
253
|
+
return;
|
|
254
|
+
const source = view.source;
|
|
255
|
+
const expectedVersion = view.version;
|
|
256
|
+
const defaultValue = view.default !== true;
|
|
257
|
+
remoteDefaultEligible = false;
|
|
258
|
+
const scope = { ...listPreferenceScope };
|
|
259
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
260
|
+
const epoch = ++savedViewMutationEpoch;
|
|
261
|
+
const current = () => epoch === savedViewMutationEpoch && provider === savedViewProvider
|
|
262
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope) && preferenceScopeIsLoaded();
|
|
263
|
+
savedViewMutationPending = true;
|
|
264
|
+
savedViewMutationError = undefined;
|
|
265
|
+
void Promise.resolve().then(() => provider.setDefault?.(scope, {
|
|
266
|
+
id: view.id, source, expectedVersion, default: defaultValue,
|
|
267
|
+
})).then(result => {
|
|
268
|
+
if (!current())
|
|
269
|
+
return;
|
|
270
|
+
const decoded = decodeSavedListViewMutationResult(result, savedViewColumnIds, {
|
|
271
|
+
id: view.id, source, expectedVersion,
|
|
272
|
+
});
|
|
273
|
+
if (!decoded) {
|
|
274
|
+
savedViewMutationError = 'failure';
|
|
275
|
+
}
|
|
276
|
+
else if (!decoded.ok) {
|
|
277
|
+
savedViewMutationError = 'conflict';
|
|
278
|
+
}
|
|
279
|
+
else if (decoded.view.default !== defaultValue) {
|
|
280
|
+
savedViewMutationError = 'failure';
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
// 默认标记会同时改变同一作用域的其他视图,成功后重新读取完整集合。
|
|
284
|
+
remoteViewsReload += 1;
|
|
285
|
+
}
|
|
286
|
+
}).catch(() => {
|
|
287
|
+
if (current())
|
|
288
|
+
savedViewMutationError = 'failure';
|
|
289
|
+
}).finally(() => {
|
|
290
|
+
if (current())
|
|
291
|
+
savedViewMutationPending = false;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function saveRemoteAccess(view) {
|
|
295
|
+
const provider = savedViewProvider;
|
|
296
|
+
const draft = accessDrafts.get(view.id);
|
|
297
|
+
if (!provider?.updateAccess || !draft || view.readOnly !== false || !view.version
|
|
298
|
+
|| savedViewMutationPending || savedViewMutationError === 'conflict' || !preferenceScopeIsLoaded()
|
|
299
|
+
|| (view.source !== 'team' && view.source !== 'system'))
|
|
300
|
+
return;
|
|
301
|
+
const access = decodeSavedListViewAccess({
|
|
302
|
+
mode: draft.mode,
|
|
303
|
+
subjectIds: draft.mode === 'restricted' ? draft.subjectIds : [],
|
|
304
|
+
});
|
|
305
|
+
if (!access) {
|
|
306
|
+
savedViewMutationError = 'failure';
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const source = view.source;
|
|
310
|
+
const expectedVersion = view.version;
|
|
311
|
+
const scope = { ...listPreferenceScope };
|
|
312
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
313
|
+
const epoch = ++savedViewMutationEpoch;
|
|
314
|
+
const current = () => epoch === savedViewMutationEpoch && provider === savedViewProvider
|
|
315
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope) && preferenceScopeIsLoaded();
|
|
316
|
+
remoteDefaultEligible = false;
|
|
317
|
+
savedViewMutationPending = true;
|
|
318
|
+
savedViewMutationError = undefined;
|
|
319
|
+
void Promise.resolve().then(() => {
|
|
320
|
+
if (!current())
|
|
321
|
+
return;
|
|
322
|
+
return provider.updateAccess?.(scope, {
|
|
323
|
+
id: view.id, source, expectedVersion, access: { ...access, subjectIds: [...access.subjectIds] },
|
|
324
|
+
});
|
|
325
|
+
}).then(result => {
|
|
326
|
+
if (!current())
|
|
327
|
+
return;
|
|
328
|
+
const decoded = decodeSavedListViewMutationResult(result, savedViewColumnIds, {
|
|
329
|
+
id: view.id, source, expectedVersion,
|
|
330
|
+
});
|
|
331
|
+
if (!decoded)
|
|
332
|
+
savedViewMutationError = 'failure';
|
|
333
|
+
else if (!decoded.ok)
|
|
334
|
+
savedViewMutationError = 'conflict';
|
|
335
|
+
else if (decoded.view.access?.mode !== access.mode
|
|
336
|
+
|| decoded.view.access.subjectIds.length !== access.subjectIds.length
|
|
337
|
+
|| !decoded.view.access.subjectIds.every(id => access.subjectIds.includes(id))) {
|
|
338
|
+
savedViewMutationError = 'failure';
|
|
339
|
+
}
|
|
340
|
+
else
|
|
341
|
+
remoteViewsReload += 1;
|
|
342
|
+
}).catch(() => {
|
|
343
|
+
if (current())
|
|
344
|
+
savedViewMutationError = 'failure';
|
|
345
|
+
}).finally(() => {
|
|
346
|
+
if (current())
|
|
347
|
+
savedViewMutationPending = false;
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function loadAccessSubjects(view, query = '') {
|
|
351
|
+
const provider = savedViewProvider;
|
|
352
|
+
if (!provider?.listAccessSubjects || view.readOnly !== false
|
|
353
|
+
|| view.source === 'local' || !preferenceScopeIsLoaded())
|
|
354
|
+
return;
|
|
355
|
+
const scope = { ...listPreferenceScope };
|
|
356
|
+
const scopeId = listPreferenceScopeId(scope);
|
|
357
|
+
const request = {};
|
|
358
|
+
accessSubjectRequests.set(view.id, request);
|
|
359
|
+
const current = () => accessSubjectRequests.get(view.id) === request
|
|
360
|
+
&& scopeId === listPreferenceScopeId(listPreferenceScope)
|
|
361
|
+
&& provider === savedViewProvider && preferenceScopeIsLoaded();
|
|
362
|
+
accessSubjectsLoading.set(view.id, true);
|
|
363
|
+
accessSubjectsFailed.delete(view.id);
|
|
364
|
+
accessSubjects.delete(view.id);
|
|
365
|
+
void Promise.resolve().then(() => {
|
|
366
|
+
if (current())
|
|
367
|
+
return provider.listAccessSubjects?.(scope, { query: query.trim(), limit: 50 });
|
|
368
|
+
})
|
|
369
|
+
.then(result => {
|
|
370
|
+
if (!current())
|
|
371
|
+
return;
|
|
372
|
+
const subjects = decodeSavedListViewSubjects(result);
|
|
373
|
+
if (!subjects)
|
|
374
|
+
accessSubjectsFailed.set(view.id, true);
|
|
375
|
+
else
|
|
376
|
+
accessSubjects.set(view.id, subjects);
|
|
377
|
+
})
|
|
378
|
+
.catch(() => {
|
|
379
|
+
if (current())
|
|
380
|
+
accessSubjectsFailed.set(view.id, true);
|
|
381
|
+
})
|
|
382
|
+
.finally(() => { if (current())
|
|
383
|
+
accessSubjectsLoading.delete(view.id); });
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
initialSavedView,
|
|
387
|
+
get availableSavedViews() { return availableSavedViews; },
|
|
388
|
+
get activeSavedViewId() { return activeSavedViewId; },
|
|
389
|
+
get activeSavedViewName() { return activeSavedViewName; },
|
|
390
|
+
get savedViewName() { return savedViewName; },
|
|
391
|
+
set savedViewName(value) { savedViewName = value; },
|
|
392
|
+
get savedViewsOpen() { return savedViewsOpen; },
|
|
393
|
+
set savedViewsOpen(value) { savedViewsOpen = value; },
|
|
394
|
+
get savedViewSource() { return savedViewSource; },
|
|
395
|
+
set savedViewSource(value) { savedViewSource = value; },
|
|
396
|
+
get remoteViewsLoading() { return remoteViewsLoading; },
|
|
397
|
+
get remoteViewsFailed() { return remoteViewsFailed; },
|
|
398
|
+
get savedViewMutationPending() { return savedViewMutationPending; },
|
|
399
|
+
get savedViewMutationError() { return savedViewMutationError; },
|
|
400
|
+
accessDrafts, accessSubjects, accessSubjectsLoading, accessSubjectsFailed, accessSubjectQueries,
|
|
401
|
+
restoreScope, markSavedViewDirty, clearActiveSavedView, applySavedView, saveCurrentView, deleteSavedView,
|
|
402
|
+
setRemoteDefault, saveRemoteAccess, loadAccessSubjects,
|
|
403
|
+
reload() { remoteViewsReload += 1; },
|
|
404
|
+
};
|
|
405
|
+
}
|