@conduction/nextcloud-vue 2.75.0 → 2.76.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +6 -0
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -2
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnCredentials/CnCredentials.vue2.js +11 -10
- package/dist/esm/components/CnCredentials/CnCredentials.vue2.js.map +1 -1
- package/dist/esm/components/CnCredentials/CnCredentials.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +8 -5
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +144 -6
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +12 -11
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/selfModeActions.js +90 -14
- package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +15 -3
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +26 -0
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +26 -9
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +19 -3
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +153 -38
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +74 -56
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useObjectLock.js +41 -10
- package/dist/esm/composables/useObjectLock.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +7 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +8 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +88 -1
- package/dist/esm/store/useObjectStore.js +37 -9
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/objectSignals.js +44 -0
- package/dist/esm/utils/objectSignals.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +871 -328
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +88 -1
- package/l10n/en.json +7 -1
- package/l10n/nl.json +8 -2
- package/package.json +1 -1
- package/src/components/CnCardGrid/CnCardGrid.md +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +7 -0
- package/src/components/CnCredentials/CnCredentials.vue +17 -16
- package/src/components/CnDetailPage/CnDetailPage.vue +8 -5
- package/src/components/CnIndexPage/CnIndexPage.md +15 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +155 -16
- package/src/components/CnIndexPage/selfModeActions.js +89 -13
- package/src/components/CnIndexPage/useSelfFetchList.js +15 -3
- package/src/components/CnObjectCard/CnObjectCard.md +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +89 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +19 -3
- package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +181 -38
- package/src/composables/__tests__/useObjectLock.endpoints.spec.js +83 -0
- package/src/composables/useObjectLock.js +41 -10
- package/src/css/look-board-index.css +35 -1
- package/src/index.js +1 -0
- package/src/store/useObjectStore.js +37 -9
- package/src/utils/index.js +1 -0
- package/src/utils/objectSignals.js +41 -0
|
@@ -431,6 +431,12 @@ var script = {
|
|
|
431
431
|
groups: [],
|
|
432
432
|
/** Active tab key. */
|
|
433
433
|
activeKey: '',
|
|
434
|
+
/** Keys of the tabbed sections whose request is still in flight (`objects`, `files`, `relations`). */
|
|
435
|
+
pendingSections: [],
|
|
436
|
+
/** Tabbed sections whose request failed: `{ key, label }`. */
|
|
437
|
+
failedSections: [],
|
|
438
|
+
/** Bumped per `loadTabs()`; an answer from an older load is dropped. */
|
|
439
|
+
loadToken: 0,
|
|
434
440
|
/** Key of the inline-expanded row (`{group}-{id}`) for leaves with no owning-app page (e.g. notes). */
|
|
435
441
|
expandedKey: '',
|
|
436
442
|
/** Whether a footer file upload is in flight. */
|
|
@@ -555,6 +561,22 @@ var script = {
|
|
|
555
561
|
)
|
|
556
562
|
},
|
|
557
563
|
|
|
564
|
+
/**
|
|
565
|
+
* The line under the panel naming the sections still loading, once
|
|
566
|
+
* another section is already on screen.
|
|
567
|
+
*
|
|
568
|
+
* @return {string}
|
|
569
|
+
*/
|
|
570
|
+
pendingLabel() {
|
|
571
|
+
const labels = {
|
|
572
|
+
objects: this.objectsLabel,
|
|
573
|
+
files: this.filesLabel,
|
|
574
|
+
relations: translate('nextcloud-vue', 'Linked items'),
|
|
575
|
+
};
|
|
576
|
+
const names = this.pendingSections.map((key) => labels[key] || key).join(', ');
|
|
577
|
+
return translate('nextcloud-vue', 'Still loading: {sections}', { sections: names })
|
|
578
|
+
},
|
|
579
|
+
|
|
558
580
|
/** Placeholder shown in the body while the first fetch is in flight. */
|
|
559
581
|
loadingLabel() {
|
|
560
582
|
return translate('nextcloud-vue', 'Loading …')
|
|
@@ -1051,16 +1073,27 @@ var script = {
|
|
|
1051
1073
|
* @return {Promise<object|null>}
|
|
1052
1074
|
*/
|
|
1053
1075
|
async fetchSubResource(suffix) {
|
|
1076
|
+
return (await this.fetchSection(suffix)).data
|
|
1077
|
+
},
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* GET an OpenRegister sub-resource and say whether it answered, so a
|
|
1081
|
+
* section can tell "nothing linked" from "could not load".
|
|
1082
|
+
*
|
|
1083
|
+
* @param {string} suffix - The sub-resource suffix.
|
|
1084
|
+
* @return {Promise<{ ok: boolean, data: object|null }>}
|
|
1085
|
+
*/
|
|
1086
|
+
async fetchSection(suffix) {
|
|
1054
1087
|
try {
|
|
1055
1088
|
// `no-store`: relations change as the user links content; a stale
|
|
1056
1089
|
// cached empty response would wrongly show the empty state on load.
|
|
1057
1090
|
const response = await fetch(this.relatedUrl(suffix), { method: 'GET', headers: buildHeaders(), cache: 'no-store' });
|
|
1058
1091
|
if (!response.ok) {
|
|
1059
|
-
return null
|
|
1092
|
+
return { ok: false, data: null }
|
|
1060
1093
|
}
|
|
1061
|
-
return await response.json()
|
|
1094
|
+
return { ok: true, data: await response.json() }
|
|
1062
1095
|
} catch {
|
|
1063
|
-
return null
|
|
1096
|
+
return { ok: false, data: null }
|
|
1064
1097
|
}
|
|
1065
1098
|
},
|
|
1066
1099
|
|
|
@@ -1264,56 +1297,138 @@ var script = {
|
|
|
1264
1297
|
},
|
|
1265
1298
|
|
|
1266
1299
|
/**
|
|
1267
|
-
* Fetch every group directly from OpenRegister and build the tabs
|
|
1300
|
+
* Fetch every group directly from OpenRegister and build the tabs,
|
|
1301
|
+
* one section at a time.
|
|
1302
|
+
*
|
|
1303
|
+
* 🔴 EACH SECTION SHOWS WHEN ITS OWN REQUEST RETURNS. This used to wait
|
|
1304
|
+
* for relations, uses, used, contracts and files together behind one
|
|
1305
|
+
* `Promise.all`, so the relations call (0.45 s on pipelinq's lead page)
|
|
1306
|
+
* sat behind uses/used (about 9 s) and the card said "Loading …" the
|
|
1307
|
+
* whole time. Now the groups are laid out up front in their fixed order
|
|
1308
|
+
* and filled in place as each answer arrives, so a slow section never
|
|
1309
|
+
* reorders or hides a fast one. A section still in flight is named in
|
|
1310
|
+
* `pendingSections`; one whose request failed in `failedSections`.
|
|
1268
1311
|
*
|
|
1312
|
+
* @spec openspec/changes/r4-object-lock-url-and-credentials-copy/specs/related-objects-progressive/spec.md
|
|
1269
1313
|
* @return {Promise<void>}
|
|
1270
1314
|
*/
|
|
1271
1315
|
async loadTabs() {
|
|
1316
|
+
const token = ++this.loadToken;
|
|
1317
|
+
const current = () => token === this.loadToken;
|
|
1272
1318
|
this.loading = true;
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
])
|
|
1319
|
+
this.failedSections = [];
|
|
1320
|
+
|
|
1321
|
+
const objectSuffixes = this.showObjects
|
|
1322
|
+
? ['uses', 'used', ...(this.showContracts ? ['contracts'] : [])]
|
|
1323
|
+
: [];
|
|
1324
|
+
const sections = [
|
|
1325
|
+
...(this.showObjects ? [{ key: 'objects', label: this.objectsLabel }] : []),
|
|
1326
|
+
...(this.showFiles ? [{ key: 'files', label: this.filesLabel }] : []),
|
|
1327
|
+
{ key: 'relations', label: translate('nextcloud-vue', 'Linked items') },
|
|
1328
|
+
];
|
|
1329
|
+
this.pendingSections = sections.map((section) => section.key);
|
|
1281
1330
|
|
|
1282
|
-
|
|
1331
|
+
// Lay out every group now, in its fixed order, empty. visibleGroups
|
|
1332
|
+
// hides an empty group, so nothing shows until a section fills it.
|
|
1333
|
+
const groups = [];
|
|
1334
|
+
if (this.showObjects) {
|
|
1335
|
+
groups.push({ key: 'objects', label: this.objectsLabel, icon: 'FileTreeOutline', integrationId: '', items: [], total: 0 });
|
|
1336
|
+
}
|
|
1337
|
+
if (this.showFiles) {
|
|
1338
|
+
groups.push({ key: 'files', label: this.filesLabel, icon: 'Paperclip', integrationId: 'files', items: [], total: 0 });
|
|
1339
|
+
}
|
|
1340
|
+
for (const def of LEAF_GROUPS) {
|
|
1341
|
+
groups.push({
|
|
1342
|
+
key: def.key,
|
|
1343
|
+
label: this.leafLabel(def.key),
|
|
1344
|
+
// Prefer the registered integration's icon so it matches the
|
|
1345
|
+
// sidebar/linked-apps; fall back to the known-good default.
|
|
1346
|
+
icon: this.integrationIcon(def.integrationId) || def.icon,
|
|
1347
|
+
integrationId: def.integrationId,
|
|
1348
|
+
requiredApp: def.requiredApp,
|
|
1349
|
+
items: [],
|
|
1350
|
+
total: 0,
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
this.groups = groups;
|
|
1283
1354
|
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1355
|
+
const setGroup = (key, items, total) => {
|
|
1356
|
+
const index = this.groups.findIndex((group) => group.key === key);
|
|
1357
|
+
if (index === -1) {
|
|
1358
|
+
return
|
|
1288
1359
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1360
|
+
const next = this.groups.slice();
|
|
1361
|
+
next[index] = { ...next[index], items, total };
|
|
1362
|
+
this.groups = next;
|
|
1363
|
+
};
|
|
1364
|
+
const settle = (key, ok) => {
|
|
1365
|
+
this.pendingSections = this.pendingSections.filter((k) => k !== key);
|
|
1366
|
+
if (!ok) {
|
|
1367
|
+
const section = sections.find((s) => s.key === key);
|
|
1368
|
+
this.failedSections = [...this.failedSections, { key, label: section ? section.label : key }];
|
|
1369
|
+
}
|
|
1370
|
+
// Keep the tab the reader saw first: pin it, so a slower section
|
|
1371
|
+
// that sorts before it does not take the panel away.
|
|
1372
|
+
if (!this.activeKey && this.visibleGroups.length) {
|
|
1373
|
+
this.activeKey = this.visibleGroups[0].key;
|
|
1293
1374
|
}
|
|
1375
|
+
};
|
|
1294
1376
|
|
|
1377
|
+
const loadRelations = this.fetchSection('relations').then(({ ok, data }) => {
|
|
1378
|
+
if (!current()) {
|
|
1379
|
+
return
|
|
1380
|
+
}
|
|
1295
1381
|
for (const def of LEAF_GROUPS) {
|
|
1296
|
-
const block =
|
|
1382
|
+
const block = data && data[def.responseKey];
|
|
1297
1383
|
const results = (block && block.results) || [];
|
|
1298
|
-
|
|
1299
|
-
key: def.key,
|
|
1300
|
-
label: this.leafLabel(def.key),
|
|
1301
|
-
// Prefer the registered integration's icon so it matches the
|
|
1302
|
-
// sidebar/linked-apps; fall back to the known-good default.
|
|
1303
|
-
icon: this.integrationIcon(def.integrationId) || def.icon,
|
|
1304
|
-
integrationId: def.integrationId,
|
|
1305
|
-
requiredApp: def.requiredApp,
|
|
1306
|
-
items: results.map((r, i) => this.toLeafRow(r, i)),
|
|
1307
|
-
total: block ? (block.total ?? results.length) : 0,
|
|
1308
|
-
});
|
|
1384
|
+
setGroup(def.key, results.map((r, i) => this.toLeafRow(r, i)), block ? (block.total ?? results.length) : 0);
|
|
1309
1385
|
}
|
|
1386
|
+
settle('relations', ok);
|
|
1387
|
+
});
|
|
1310
1388
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1389
|
+
const loadFiles = this.showFiles
|
|
1390
|
+
? this.fetchSection('files').then(({ ok, data }) => {
|
|
1391
|
+
if (!current()) {
|
|
1392
|
+
return
|
|
1393
|
+
}
|
|
1394
|
+
const fileResults = (data && data.results) || [];
|
|
1395
|
+
setGroup('files', fileResults.map((f) => this.toFileRow(f)), data ? (data.total ?? fileResults.length) : 0);
|
|
1396
|
+
settle('files', ok);
|
|
1397
|
+
})
|
|
1398
|
+
: null;
|
|
1399
|
+
|
|
1400
|
+
// uses, used and contracts merge into one Objects group: each answer
|
|
1401
|
+
// re-merges what has arrived so far, and the group stays pending
|
|
1402
|
+
// until the last of them is in.
|
|
1403
|
+
const envelopes = objectSuffixes.map(() => null);
|
|
1404
|
+
let objectsOk = true;
|
|
1405
|
+
const loadObjects = objectSuffixes.length
|
|
1406
|
+
? Promise.all(objectSuffixes.map((suffix, i) => this.fetchSection(suffix).then(async ({ ok, data }) => {
|
|
1407
|
+
if (!current()) {
|
|
1408
|
+
return
|
|
1409
|
+
}
|
|
1410
|
+
objectsOk = objectsOk && ok;
|
|
1411
|
+
envelopes[i] = data;
|
|
1412
|
+
await this.resolveSchemaTitles([data]);
|
|
1413
|
+
if (!current()) {
|
|
1414
|
+
return
|
|
1415
|
+
}
|
|
1416
|
+
const objectItems = this.mergeObjectResults(envelopes);
|
|
1417
|
+
setGroup('objects', objectItems, objectItems.length);
|
|
1418
|
+
}))).then(() => {
|
|
1419
|
+
if (current()) {
|
|
1420
|
+
settle('objects', objectsOk);
|
|
1421
|
+
}
|
|
1422
|
+
})
|
|
1423
|
+
: null;
|
|
1424
|
+
|
|
1425
|
+
try {
|
|
1426
|
+
await Promise.all([loadRelations, loadFiles, loadObjects]);
|
|
1315
1427
|
} finally {
|
|
1316
|
-
|
|
1428
|
+
if (current()) {
|
|
1429
|
+
this.loading = false;
|
|
1430
|
+
this.pendingSections = [];
|
|
1431
|
+
}
|
|
1317
1432
|
}
|
|
1318
1433
|
},
|
|
1319
1434
|
|