diff --git a/client/lib/libphylum/actions/action_resource_move.dart b/client/lib/libphylum/actions/action_resource_move.dart index e7848623..ee91c5a8 100644 --- a/client/lib/libphylum/actions/action_resource_move.dart +++ b/client/lib/libphylum/actions/action_resource_move.dart @@ -20,19 +20,12 @@ class ResourceMoveAction extends PhylumAction with JsonApiAction { final String resourceId; final String? parent; - final String oldParent; final String? resourceName; - final String oldName; + final DateTime modified; final String description; - ResourceMoveAction._({ - required this.resourceId, - required this.resourceName, - required this.parent, - required this.oldName, - required this.oldParent, - required this.description, - }); + ResourceMoveAction._( + {required this.resourceId, required this.resourceName, required this.parent, required this.description, required this.modified}); ResourceMoveAction({ required Resource r, @@ -42,9 +35,8 @@ class ResourceMoveAction extends PhylumAction with JsonApiAction { resourceId: r.id, resourceName: name, parent: parent?.id, - oldName: r.name, - oldParent: r.parent!, description: name == null ? 'Moving ${r.name}' : 'Renaming ${r.name} to $name', + modified: DateTime.now(), ); static ResourceMoveAction fromMap(Map map) { @@ -52,9 +44,8 @@ class ResourceMoveAction extends PhylumAction with JsonApiAction { resourceId: map['resourceId'], resourceName: map['resourceName'], parent: map['parent'], - oldName: map['oldName'], - oldParent: map['oldParent'], description: map['description'], + modified: DateTime.fromMillisecondsSinceEpoch(map['modified']), ); } @@ -70,6 +61,16 @@ class ResourceMoveAction extends PhylumAction with JsonApiAction { }; } + @override + Future initialize(PhylumAccount account) { + return account.resourceRepository.retainServerData(resourceId); + } + + @override + Future dispose(PhylumAccount account) { + return account.resourceRepository.discardServerData(resourceId); + } + @override Future applyOptimisticUpdate(PhylumAccount account) { return account.resourceRepository.updateResource( @@ -77,17 +78,20 @@ class ResourceMoveAction extends PhylumAction with JsonApiAction { (o) => o( name: Value.absentIfNull(resourceName), parent: Value.absentIfNull(parent), + modified: Value(modified), ), ); } @override Future revertOptimisticUpdate(PhylumAccount account) { + final serverData = account.resourceRepository.getServerData(resourceId)!; return account.resourceRepository.updateResource( resourceId, (o) => o( - name: Value.absentIfNull(oldName), - parent: Value.absentIfNull(oldParent), + name: resourceName == null ? Value.absent() : Value(serverData.name), + parent: parent == null ? Value.absent() : Value(serverData.parent), + modified: Value(serverData.modified), ), ); } @@ -97,9 +101,8 @@ class ResourceMoveAction extends PhylumAction with JsonApiAction { 'resourceId': resourceId, 'resourceName': resourceName, 'parent': parent, - 'oldName': oldName, - 'oldParent': oldParent, 'description': description, + 'modified': modified.millisecondsSinceEpoch, }; @override diff --git a/client/lib/libphylum/phylum_account.dart b/client/lib/libphylum/phylum_account.dart index 65d57836..c59627bf 100644 --- a/client/lib/libphylum/phylum_account.dart +++ b/client/lib/libphylum/phylum_account.dart @@ -55,6 +55,8 @@ class PhylumAccount extends Account { // Set Authorization header _accessToken = accessToken; + await resourceRepository.initialize(); + void Function()? l; l = apiClient.addResponseListener((request, errorResponse) { if (errorResponse is PhylumApiErrorResponse) { @@ -73,8 +75,8 @@ class PhylumAccount extends Account { } @override - Future clearData() async { - await super.clearData(); + Future cleanup() async { + await super.cleanup(); await db.dropDatabase(); } diff --git a/client/lib/libphylum/repositories/resource_repository.dart b/client/lib/libphylum/repositories/resource_repository.dart index 91d5ac6c..ba82fcfa 100644 --- a/client/lib/libphylum/repositories/resource_repository.dart +++ b/client/lib/libphylum/repositories/resource_repository.dart @@ -1,15 +1,48 @@ +import 'dart:convert'; + import 'package:drift/drift.dart'; +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; import 'package:offtheline/offtheline.dart'; import 'package:phylum/libphylum/db/db.dart'; import 'package:phylum/libphylum/phylum_account.dart'; import 'package:phylum/libphylum/phylum_api_types.dart'; import 'package:phylum/libphylum/requests/resource_detail_request.dart'; +class ResourceAdapter implements TypeAdapter { + const ResourceAdapter(); + + @override + int get typeId => 1; + + @override + Resource read(BinaryReader reader) { + reader.readByte(); + return Resource.fromJson(json.decode(reader.readString())); + } + + @override + void write(BinaryWriter writer, Resource obj) { + writer.writeByte(0); // Version marker, in case we need to change something in the future + writer.writeString(obj.toJsonString()); + } +} + class ResourceRepository { final PhylumAccount account; + late final Box _serverDataBox; + late final Box _serverDataCountsBox; ResourceRepository({required this.account}); + Future initialize() async { + if (!Hive.isAdapterRegistered(const ResourceAdapter().typeId)) { + Hive.registerAdapter(const ResourceAdapter()); + } + _serverDataBox = await account.openBox('server_data'); + _serverDataCountsBox = await account.openBox('server_data_counts'); + } + Future requestResource(String id) async { return account.apiClient.sendRequest(ResourceLsRequest(id), callback: (request, response) async { if (response is PhylumApiSuccessResponse) { @@ -46,6 +79,38 @@ class ResourceRepository { return account.db.resources.select()..where((f) => f.id.isIn(ids)); } + Future retainServerData(String id) async { + final count = _serverDataCountsBox.get(id) ?? 0; + if (count == 0) { + final r = await _selectResource(id).getSingle(); + await Future.wait([ + _serverDataCountsBox.put(id, 1), + _serverDataBox.put(id, r), + ]); + } else { + return _serverDataCountsBox.put(id, count + 1); + } + } + + Future discardServerData(String id) async { + final count = _serverDataCountsBox.get(id) ?? 0; + if (count <= 0) { + throw Exception('Cannot discard server data without first retaining'); + } + if (count == 1) { + await Future.wait([ + _serverDataCountsBox.delete(id), + _serverDataBox.delete(id), + ]); + } else { + return _serverDataCountsBox.put(id, count - 1); + } + } + + Resource? getServerData(String id) { + return _serverDataBox.get(id); + } + Future createResource(String id, bool dir, String parent, String name, String contentType) { return account.db.resources.insertOne( ResourcesCompanion.insert( @@ -79,12 +144,19 @@ class ResourceRepository { batch.deleteWhere(db.resources, (o) => o.id.isIn(deleted)); batch.insertAllOnConflictUpdate(db.resources, resources); }); + for (final r in resources) { + if (_serverDataBox.containsKey(r.id.value)) { + _serverDataBox.put(r.id.value, _serverDataBox.get(r.id.value)!.copyWithCompanion(r)); + } + } } Future processResourceUpdateResponse(String id, Map data) async { - final resource = parsePartialResourceObject(data); + final r = parsePartialResourceObject(data); + debugPrint(r.toString()); final update = (account.db.resources.update()..where((o) => o.id.equals(id))); - return update.write(resource); + await update.write(r); + _serverDataBox.put(r.id.value, _serverDataBox.get(r.id.value)!.copyWithCompanion(r)); } Future<(Iterable, Iterable)> parseFullResourceObject(Map data) async { @@ -133,7 +205,7 @@ class ResourceRepository { ResourcesCompanion parsePartialResourceObject(Map data) { return ResourcesCompanion( - id: Value(data['id']), + id: Value.absentIfNull(data['id']), parent: Value.absentIfNull(data['parent']), name: Value.absentIfNull(data['name']), dir: Value.absentIfNull(data['dir']), diff --git a/client/pubspec.lock b/client/pubspec.lock index 7cf8302f..6fed667f 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -585,8 +585,8 @@ packages: dependency: "direct main" description: path: "." - ref: a0bf6ed1e69a73e130b36436ec23437af76cd3fb - resolved-ref: a0bf6ed1e69a73e130b36436ec23437af76cd3fb + ref: b7d3c5c043a71ac1cfb4ba2548848e25187d9385 + resolved-ref: b7d3c5c043a71ac1cfb4ba2548848e25187d9385 url: "https://codeberg.org/shroff/offtheline.git" source: git version: "0.16.0" diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 690c442e..4c286d05 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: offtheline: git: url: https://codeberg.org/shroff/offtheline.git - ref: a0bf6ed1e69a73e130b36436ec23437af76cd3fb + ref: b7d3c5c043a71ac1cfb4ba2548848e25187d9385 open_file: path: path_provider: