added support for simultan downloads in files and talk, support for background downloads, enhanced loading in files with retry
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../share_intent/remote_file_ref.dart';
|
||||
import '../../utils/downloads/download_job.dart';
|
||||
import '../../utils/downloads/download_manager.dart';
|
||||
import '../confirm_dialog.dart';
|
||||
|
||||
/// Shared download trigger logic for the Files list and Talk chat bubbles.
|
||||
///
|
||||
/// Owns attaching to / detaching from a [DownloadJob] and starting/cancelling a
|
||||
/// download. Rendering of inline progress stays with each screen (list subtitle
|
||||
/// vs. bubble overlay) via [downloadJob]. Opening the finished file, the
|
||||
/// completion/parallel behaviour and error surfacing are handled centrally by
|
||||
/// the downloads tray/coordinator — not here.
|
||||
mixin DownloadTrigger<T extends StatefulWidget> on State<T> {
|
||||
DownloadJob? _job;
|
||||
|
||||
/// Current job for inline rendering, or null when idle.
|
||||
DownloadJob? get downloadJob => _job;
|
||||
|
||||
/// Remote (WebDAV) path of the file this widget can download, or null when
|
||||
/// the widget currently has no downloadable file (e.g. a text message).
|
||||
String? get downloadRemotePath;
|
||||
|
||||
/// True while a download for this widget's file is running.
|
||||
bool get isDownloading => _job?.status.value is DownloadInProgress;
|
||||
|
||||
/// Reattach to an in-flight job on mount. Call from `initState`.
|
||||
void initDownloadTrigger() => _attach(_lookup());
|
||||
|
||||
/// Re-resolve the job after the widget's file changed. Call from
|
||||
/// `didUpdateWidget` when [downloadRemotePath] may have changed.
|
||||
void refreshDownloadTrigger() {
|
||||
_detach();
|
||||
_attach(_lookup());
|
||||
}
|
||||
|
||||
/// Detach listeners. Call from `dispose`.
|
||||
void disposeDownloadTrigger() => _detach();
|
||||
|
||||
DownloadJob? _lookup() {
|
||||
final path = downloadRemotePath;
|
||||
return path == null ? null : DownloadManager.instance.jobFor(path);
|
||||
}
|
||||
|
||||
void _attach(DownloadJob? job) {
|
||||
_job = job;
|
||||
job?.status.addListener(_onStatus);
|
||||
}
|
||||
|
||||
void _detach() {
|
||||
_job?.status.removeListener(_onStatus);
|
||||
_job = null;
|
||||
}
|
||||
|
||||
void _onStatus() {
|
||||
if (!mounted) return;
|
||||
final job = _job;
|
||||
setState(() {});
|
||||
// Once finished, the tray owns the job (open / retry / dismiss); drop our
|
||||
// reference so the inline progress clears and we never touch a job the
|
||||
// manager is about to dispose.
|
||||
if (job != null && job.isFinished) _detach();
|
||||
}
|
||||
|
||||
/// Starts (or reattaches to) the download for this widget's file.
|
||||
Future<void> startDownload({
|
||||
required String name,
|
||||
RemoteFileRef? remoteFile,
|
||||
}) async {
|
||||
final path = downloadRemotePath;
|
||||
if (path == null) return;
|
||||
final job = await DownloadManager.instance.start(
|
||||
remotePath: path,
|
||||
name: name,
|
||||
remoteFile: remoteFile,
|
||||
);
|
||||
if (!mounted) return;
|
||||
if (_job != job) {
|
||||
_detach();
|
||||
_attach(job);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
/// Confirms and cancels the in-flight download.
|
||||
void confirmCancelDownload() {
|
||||
final job = _job;
|
||||
if (job == null || job.isFinished) return;
|
||||
ConfirmDialog(
|
||||
title: 'Download abbrechen?',
|
||||
content: 'Möchtest du den Download abbrechen?',
|
||||
confirmButton: 'Ja, Abbrechen',
|
||||
cancelButton: 'Nein',
|
||||
onConfirm: () => unawaited(DownloadManager.instance.cancel(job)),
|
||||
).asDialog(context);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user