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.
 
 

142 lines
4.2 KiB

part of '../neon_files.dart';
class FilesSyncSources implements SyncSources<WebDavFile, FileSystemEntity> {
FilesSyncSources(
final NextcloudClient client,
final PathUri webdavBaseDir,
final Directory ioBaseDir,
) : sourceA = FilesSyncSourceWebDavFile(client, webdavBaseDir),
sourceB = FilesSyncSourceFileSystemEntity(client, ioBaseDir);
@override
final SyncSource<WebDavFile, FileSystemEntity> sourceA;
@override
final SyncSource<FileSystemEntity, WebDavFile> sourceB;
@override
SyncConflictSolution? findSolution(final SyncObject<WebDavFile> objectA, final SyncObject<FileSystemEntity> objectB) {
if (objectA.data.isDirectory && objectB.data is Directory) {
return SyncConflictSolution.overwriteA;
}
return null;
}
}
class FilesSyncSourceWebDavFile implements SyncSource<WebDavFile, FileSystemEntity> {
FilesSyncSourceWebDavFile(
this.client,
this.baseDir,
);
/// [NextcloudClient] used by the WebDAV part.
final NextcloudClient client;
/// Base directory on the WebDAV server.
final PathUri baseDir;
final props = WebDavPropWithoutValues.fromBools(
davgetetag: true,
davgetlastmodified: true,
nchaspreview: true,
ocsize: true,
ocfavorite: true,
);
PathUri _uri(final SyncObject<dynamic> object) => baseDir.join(PathUri.parse(object.id));
@override
Future<List<SyncObject<WebDavFile>>> listObjects() async => (await client.webdav.propfind(
baseDir,
prop: props,
depth: WebDavDepth.infinity,
))
.toWebDavFiles()
.sublist(1)
.map(
(final file) => (
id: file.path.pathSegments.sublist(baseDir.pathSegments.length).join('/'),
data: file,
),
)
.toList();
@override
Future<String> getObjectETag(final SyncObject<WebDavFile> object) async =>
object.data.isDirectory ? '' : object.data.etag!;
@override
Future<SyncObject<WebDavFile>> writeObject(final SyncObject<FileSystemEntity> object) async {
if (object.data is File) {
final stat = await object.data.stat();
await client.webdav.putFile(
object.data as File,
stat,
_uri(object),
lastModified: stat.modified,
);
} else if (object.data is Directory) {
await client.webdav.mkcol(_uri(object));
} else {
throw Exception('Unable to sync FileSystemEntity of type ${object.data.runtimeType}');
}
return (
id: object.id,
data: (await client.webdav.propfind(
_uri(object),
prop: props,
depth: WebDavDepth.zero,
))
.toWebDavFiles()
.single,
);
}
@override
Future<void> deleteObject(final SyncObject<WebDavFile> object) async => client.webdav.delete(_uri(object));
}
class FilesSyncSourceFileSystemEntity implements SyncSource<FileSystemEntity, WebDavFile> {
FilesSyncSourceFileSystemEntity(
this.client,
this.baseDir,
);
/// [NextcloudClient] used by the WebDAV part.
final NextcloudClient client;
/// Base directory on the local filesystem.
final Directory baseDir;
@override
Future<List<SyncObject<FileSystemEntity>>> listObjects() async => baseDir.listSync(recursive: true).map(
(final e) {
var path = p.relative(e.path, from: baseDir.path);
if (path.endsWith('/')) {
path = path.substring(0, path.length - 1);
}
return (id: path, data: e);
},
).toList();
@override
Future<String> getObjectETag(final SyncObject<FileSystemEntity> object) async =>
object.data is Directory ? '' : object.data.statSync().modified.millisecondsSinceEpoch.toString();
@override
Future<SyncObject<FileSystemEntity>> writeObject(final SyncObject<WebDavFile> object) async {
if (object.data.isDirectory) {
final dir = Directory(p.join(baseDir.path, object.id))..createSync();
return (id: object.id, data: dir);
} else {
final file = File(p.join(baseDir.path, object.id));
await client.webdav.getFile(object.data.path, file);
await file.setLastModified(object.data.lastModified!);
return (id: object.id, data: file);
}
}
@override
Future<void> deleteObject(final SyncObject<FileSystemEntity> object) async => object.data.delete();
}