added support for simultan downloads in files and talk, support for background downloads, enhanced loading in files with retry

This commit is contained in:
2026-07-06 15:43:17 +02:00
parent 614ab159af
commit 124b1a5177
34 changed files with 1866 additions and 409 deletions
@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:nextcloud/nextcloud.dart';
import '../../../../retry.dart';
import '../../webdav_api.dart';
import 'cacheable_file.dart';
import 'list_files_params.dart';
@@ -10,7 +11,11 @@ import 'list_files_response.dart';
class ListFiles extends WebdavApi<ListFilesParams> {
ListFilesParams params;
ListFiles(this.params) : super(params);
/// Forwarded to [retryOnTransientError] so the UI can surface "Erneuter
/// Versuch (2 von 3)" while a 5xx-plagued listing self-heals.
final void Function(int nextAttempt, int maxAttempts)? onRetry;
ListFiles(this.params, {this.onRetry}) : super(params);
// The Nextcloud root listing is significantly slower than subdirectories on
// our instance, so it gets a much longer ceiling. Subfolders fall back to a
@@ -44,15 +49,25 @@ class ListFiles extends WebdavApi<ListFilesParams> {
ncmounttype: true,
);
var files = await _fetch(webdav, prop, timeout);
// Our instance intermittently answers the (slow) root PROPFIND with a
// quick empty 500 — retry those transparently before surfacing an error.
var files = await retryOnTransientError(
() => _fetch(webdav, prop, timeout),
onRetry: onRetry,
);
// A freshly-entered incoming share sometimes answers its first PROPFIND
// without the OC/NC props (no fileid / has-preview / mount-type) while the
// share mount warms up server-side — which drops thumbnails AND share
// badges together. Retry a couple of times so the folder self-heals
// instead of needing manual re-entry.
// instead of needing manual re-entry. Best-effort: a failure here must
// not discard the listing that was already fetched successfully.
for (var attempt = 0; attempt < 2 && _looksIncomplete(files); attempt++) {
await Future<void>.delayed(const Duration(milliseconds: 700));
files = await _fetch(webdav, prop, timeout);
try {
files = await _fetch(webdav, prop, timeout);
} on Exception {
break;
}
}
return ListFilesResponse(files);
@@ -65,7 +80,11 @@ class ListFiles extends WebdavApi<ListFilesParams> {
) async {
final davFiles =
(await webdav
.propfind(PathUri.parse(params.path), prop: prop)
.propfind(
PathUri.parse(params.path),
prop: prop,
depth: WebDavDepth.one,
)
.timeout(timeout))
.toWebDavFiles();
final files = davFiles.map(CacheableFile.fromDavFile).toSet();
@@ -17,9 +17,10 @@ class ListFilesCache extends SimpleCache<ListFilesResponse> {
super.onError,
required String path,
super.renew = false,
void Function(int nextAttempt, int maxAttempts)? onRetry,
}) : super(
cacheTime: _cacheTimeFor(path),
loader: () => ListFiles(ListFilesParams(path)).run(),
loader: () => ListFiles(ListFilesParams(path), onRetry: onRetry).run(),
fromJson: ListFilesResponse.fromJson,
onUpdate: onUpdate,
) {