A framework for building convergent cross-platform Nextcloud clients using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

206 lines
5.7 KiB

part of '../neon_files.dart';
abstract class FilesBlocEvents {
void uploadFile(final List<String> path, final String localPath);
void syncFile(final List<String> path);
void openFile(final List<String> path, final String etag, final String? mimeType);
void delete(final List<String> path);
void rename(final List<String> path, final String name);
void move(final List<String> path, final List<String> destination);
void copy(final List<String> path, final List<String> destination);
void addFavorite(final List<String> path);
void removeFavorite(final List<String> path);
}
abstract class FilesBlocStates {
BehaviorSubject<List<UploadTask>> get uploadTasks;
BehaviorSubject<List<DownloadTask>> get downloadTasks;
}
class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocStates {
FilesBloc(
this.options,
this.client,
this._requestManager,
this._platform,
) {
options.uploadQueueParallelism.addListener(_uploadParalelismListener);
options.downloadQueueParallelism.addListener(_downloadParalelismListener);
}
final FilesAppSpecificOptions options;
final NextcloudClient client;
final RequestManager _requestManager;
final NeonPlatform _platform;
late final browser = getNewFilesBrowserBloc();
final _uploadQueue = Queue();
final _downloadQueue = Queue();
@override
void dispose() {
_uploadQueue.dispose();
_downloadQueue.dispose();
unawaited(uploadTasks.close());
unawaited(downloadTasks.close());
options.uploadQueueParallelism.removeListener(_uploadParalelismListener);
options.downloadQueueParallelism.removeListener(_downloadParalelismListener);
}
@override
BehaviorSubject<List<UploadTask>> uploadTasks = BehaviorSubject<List<UploadTask>>.seeded([]);
@override
BehaviorSubject<List<DownloadTask>> downloadTasks = BehaviorSubject<List<DownloadTask>>.seeded([]);
@override
void addFavorite(final List<String> path) {
wrapAction(() async => client.webdav.updateProps(path.join('/'), WebDavProp(ocfavorite: 1)));
}
@override
void copy(final List<String> path, final List<String> destination) {
wrapAction(() async => client.webdav.copy(path.join('/'), destination.join('/')));
}
@override
void delete(final List<String> path) {
wrapAction(() async => client.webdav.delete(path.join('/')));
}
@override
void move(final List<String> path, final List<String> destination) {
wrapAction(() async => client.webdav.move(path.join('/'), destination.join('/')));
}
@override
void openFile(final List<String> path, final String etag, final String? mimeType) {
wrapAction(
() async {
final file = File(
p.join(
await _platform.getApplicationCachePath(),
'files',
etag.replaceAll('"', ''),
path.last,
),
);
if (!file.existsSync()) {
debugPrint('Downloading ${path.join('/')} since it does not exist');
if (!file.parent.existsSync()) {
await file.parent.create(recursive: true);
}
await _downloadFile(path, file);
}
final result = await OpenFile.open(file.path, type: mimeType);
if (result.type != ResultType.done) {
throw UnableToOpenFileException();
}
},
disableTimeout: true,
);
}
@override
Future refresh() async {
await browser.refresh();
}
@override
void removeFavorite(final List<String> path) {
wrapAction(
() async => client.webdav.updateProps(
path.join('/'),
WebDavProp(ocfavorite: 0),
),
);
}
@override
void rename(final List<String> path, final String name) {
wrapAction(
() async => client.webdav.move(
path.join('/'),
(path.sublist(0, path.length - 1)..add(name)).join('/'),
),
);
}
@override
void syncFile(final List<String> path) {
wrapAction(
() async {
final file = File(
p.join(
await _platform.getUserAccessibleAppDataPath(),
client.humanReadableID,
'files',
path.join(Platform.pathSeparator),
),
);
if (!file.parent.existsSync()) {
file.parent.createSync(recursive: true);
}
await _downloadFile(path, file);
},
disableTimeout: true,
);
}
@override
void uploadFile(final List<String> path, final String localPath) {
wrapAction(
() async {
final file = File(localPath);
// ignore: avoid_slow_async_io
final stat = await file.stat();
final task = UploadTask(
path: path,
size: stat.size,
lastModified: stat.modified,
);
uploadTasks.add(uploadTasks.value..add(task));
await _uploadQueue.add(() => task.execute(client, file.openRead()));
uploadTasks.add(uploadTasks.value..removeWhere((final t) => t == task));
},
disableTimeout: true,
);
}
Future _downloadFile(
final List<String> path,
final File file,
) async {
final sink = file.openWrite();
try {
final task = DownloadTask(
path: path,
);
downloadTasks.add(downloadTasks.value..add(task));
await _downloadQueue.add(() => task.execute(client, sink));
downloadTasks.add(downloadTasks.value..removeWhere((final t) => t == task));
} finally {
await sink.close();
}
}
FilesBrowserBloc getNewFilesBrowserBloc() => FilesBrowserBloc(_requestManager, options, client);
void _downloadParalelismListener() {
_downloadQueue.parallel = options.downloadQueueParallelism.value;
}
void _uploadParalelismListener() {
_uploadQueue.parallel = options.uploadQueueParallelism.value;
}
}