[client] ResourceRepository

This commit is contained in:
Abhishek Shroff
2024-09-09 17:10:33 +05:30
parent 61f4bc65c1
commit 0daeafc340
11 changed files with 94 additions and 50 deletions
@@ -47,12 +47,12 @@ class ResourceDeleteAction extends ApiAction<PhylumAccount> with JsonApiAction {
@override
FutureOr<void> applyOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).update((o) => o(deleted: const Value(true)));
account.resourceRepository.updateResource(id, (o) => o(deleted: const Value(true)));
}
@override
FutureOr<void> revertOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).update((o) => o(deleted: const Value(false)));
account.resourceRepository.updateResource(id, (o) => o(deleted: const Value(false)));
}
@override
@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/phylum_datastore.dart';
@@ -52,20 +51,12 @@ class ResourceMkdirAction extends ApiAction<PhylumAccount> with JsonApiAction {
@override
FutureOr<void> applyOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.create((o) => o(
dir: true,
etag: "",
size: 0,
id: id,
parent: Value(parent),
modified: DateTime.now(),
name: resourceName,
));
account.resourceRepository.createResource(id, true, parent, resourceName);
}
@override
FutureOr<void> revertOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).delete();
account.resourceRepository.deleteResource(id);
}
@override
@@ -50,12 +50,12 @@ class ResourceMoveAction extends ApiAction<PhylumAccount> with JsonApiAction {
@override
FutureOr<void> applyOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).update((o) => o(parent: Value(parent)));
account.resourceRepository.updateResource(id, (o) => o(parent: Value(parent)));
}
@override
FutureOr<void> revertOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).update((o) => o(parent: Value(oldParent)));
account.resourceRepository.updateResource(id, (o) => o(parent: Value(oldParent)));
}
@override
@@ -57,12 +57,12 @@ class ResourceRenameAction extends ApiAction<PhylumAccount> with JsonApiAction {
@override
FutureOr<void> applyOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).update((o) => o(name: Value(newName)));
account.resourceRepository.updateResource(id, (o) => o(name: Value(newName)));
}
@override
FutureOr<void> revertOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).update((o) => o(name: Value(oldName)));
account.resourceRepository.updateResource(id, (o) => o(name: Value(oldName)));
}
@override
@@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:http/http.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/phylum_account.dart';
@@ -72,20 +71,12 @@ class ResourceUploadAction extends ApiAction<PhylumAccount> with FileUploadApiAc
@override
FutureOr<void> applyOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.create((o) => o(
dir: false,
etag: "",
size: size,
id: id,
parent: Value(parent),
modified: DateTime.now(),
name: resourceName,
));
account.resourceRepository.createResource(id, true, parent, resourceName);
}
@override
FutureOr<void> revertOptimisticUpdate(PhylumAccount account) {
account.datastore.db.managers.resources.filter((f) => f.id.equals(id)).delete();
account.resourceRepository.deleteResource(id);
}
@override
+4
View File
@@ -1,7 +1,9 @@
import 'dart:convert';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_datastore.dart';
import 'package:phylum/libphylum/repositories/resource_repository.dart';
const _persistKeyAccessToken = 'accessToken';
const _persistKeyUserEmail = 'userEmail';
@@ -25,6 +27,8 @@ class PhylumApiErrorResponse {
Map<String, dynamic> _tansformResponse(String response) => (jsonDecode(response) as Map<String, dynamic>?) ?? const {};
class PhylumAccount extends Account<PhylumApiResponse, PhylumApiErrorResponse, PhylumAccount> {
late final db = AppDatabase(id);
late final resourceRepository = ResourceRepository(account: this);
final datastore = PhylumDatastore();
final actionQueue = ApiActionQueue<PhylumApiResponse, PhylumApiErrorResponse, PhylumAccount>();
+9 -11
View File
@@ -7,44 +7,42 @@ const resourceDetailsResponse = 'resourceDetails';
const resourceSummaryResponse = 'resourceSummary';
class PhylumDatastore with AccountListener<PhylumApiResponse, PhylumApiErrorResponse, PhylumAccount> {
late final AppDatabase db;
@override
Future<void> initialize(PhylumAccount account) async {
super.initialize(account);
db = AppDatabase(account.id);
account.api.addResponseListener(processResponse);
}
Future<void> processResponse(Map<String, dynamic> data, dynamic tag) {
return db.transaction(() => _processResponse(data, tag));
final db = account.db;
return db.transaction(() => _processResponse(data, tag, db));
}
Future<void> _processResponse(Map<String, dynamic> data, dynamic tag) async {
Future<void> _processResponse(Map<String, dynamic> data, dynamic tag, AppDatabase db) async {
switch (tag) {
case resourceDetailsResponse:
parseResourceDetails(data);
parseResourceDetails(data, db);
break;
case resourceSummaryResponse:
parseResourceSummary(data);
parseResourceSummary(data, db);
break;
}
}
Future parseResourceDetails(Map<String, dynamic> data) async {
final details = parseResourceSummary(data['metadata']).copyWith(lastFetch: Value(DateTime.now()));
Future parseResourceDetails(Map<String, dynamic> data, AppDatabase db) async {
final details = parseResourceSummary(data['metadata'], db).copyWith(lastFetch: Value(DateTime.now()));
final existing = Set.from(await db.managers.resources.filter((f) => f.parent.id.equals(data['metadata']['id'])).map((r) => r.id).get());
final children = data.containsKey('children')
? (data['children'] as List).cast<Map>().map((c) {
existing.remove(c['id']);
return parseResourceSummary(c.cast<String, dynamic>());
return parseResourceSummary(c.cast<String, dynamic>(), db);
})
: <ResourcesCompanion>[];
await db.resources.deleteWhere((o) => o.id.isIn(List.from(existing)));
return db.insertResources([details, ...children]);
}
ResourcesCompanion parseResourceSummary(Map<String, dynamic> data) {
ResourcesCompanion parseResourceSummary(Map<String, dynamic> data, AppDatabase db) {
return ResourcesCompanion.insert(
id: data['id'],
parent: Value(data['parent']),
@@ -0,0 +1,63 @@
import 'package:drift/drift.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/db/db.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/requests/resource_detail_request.dart';
class ResourceRepository {
final PhylumAccount account;
ResourceRepository({required this.account});
Future<ApiError<PhylumApiErrorResponse>?> requestResource(String id) {
return account.api.sendRequest(ResourceDetailRequest(id));
}
Future<Resource?> getResource(String id) {
return _selectResource(id).getSingleOrNull();
}
Stream<Resource?> watchResource(String id) {
return _selectResource(id).watchSingleOrNull();
}
Future<List<Resource>> getResources(Iterable<String> ids) {
return _selectResources(ids).get();
}
Stream<List<Resource>> watchChildren(String id) {
return (_selectChildren(id)..orderBy([(u) => OrderingTerm.desc(u.dir), (u) => OrderingTerm.asc(u.name.collate(Collate.noCase))])).watch();
}
SimpleSelectStatement<$ResourcesTable, Resource> _selectResource(String id) {
return account.db.resources.select()..where((f) => f.id.equals(id) & f.deleted.equals(false));
}
SimpleSelectStatement<$ResourcesTable, Resource> _selectChildren(String id) {
return account.db.resources.select()..where((f) => f.parent.equals(id) & f.deleted.equals(false));
}
SimpleSelectStatement<$ResourcesTable, Resource> _selectResources(Iterable<String> ids) {
return account.db.resources.select()..where((f) => f.id.isIn(ids));
}
Future<int> createResource(String id, bool dir, String parent, String name) {
return account.db.resources.insertOne(ResourcesCompanion.insert(
id: id,
name: name,
parent: Value(parent),
dir: dir,
modified: DateTime.now(),
size: 0,
etag: "",
));
}
Future<int> updateResource(String id, Insertable<Resource> Function($$ResourcesTableUpdateCompanionBuilder o) fn) {
return account.db.managers.resources.filter((f) => f.id.equals(id)).update(fn);
}
Future<int> deleteResource(String id) {
return account.db.resources.deleteWhere((f) => f.id.equals(id));
}
}
@@ -155,7 +155,7 @@ class _FolderContentsViewState extends State<FolderContentsView> {
if (uri.authority == openUri.authority && uri.path == openUri.path && uri.queryParameters.containsKey('id')) {
final resourceId = uri.queryParameters['id']!;
final cut = uri.queryParameters.containsKey('cut');
final resource = await account.datastore.db.managers.resources.filter((f) => f.id.equals(resourceId)).getSingleOrNull();
final resource = await account.resourceRepository.getResource(resourceId);
if (resource != null) {
(cut ? cutResources : copyResources).add(resource);
}
@@ -165,7 +165,7 @@ class _FolderContentsViewState extends State<FolderContentsView> {
}
if (!context.mounted) return;
uploadRecursive(context, widget.folderId, paths);
final parent = await account.datastore.db.managers.resources.filter((f) => f.id.equals(widget.folderId)).getSingleOrNull();
final parent = await account.resourceRepository.getResource(widget.folderId);
if (parent != null) {
for (final r in cutResources) {
account.addAction(ResourceMoveAction(r: r, parent: parent));
+5 -8
View File
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_state_notifier/flutter_state_notifier.dart';
import 'package:phylum/app_shortcuts.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/requests/resource_detail_request.dart';
import 'package:phylum/ui/folder/folder_contents_view.dart';
import 'package:phylum/ui/folder/folder_navigator_stack.dart';
import 'package:phylum/ui/folder/folder_selection_manager.dart';
@@ -24,8 +23,9 @@ class _FolderViewState extends State<FolderView> {
_refresh();
}
Future<void> _refresh() async {
await context.read<PhylumAccount>().api.sendRequest(ResourceDetailRequest(widget.id));
Future _refresh() {
debugPrint('Requesting ${widget.id}');
return context.read<PhylumAccount>().resourceRepository.requestResource(widget.id);
}
@override
@@ -50,17 +50,14 @@ class _FolderViewState extends State<FolderView> {
child: StateNotifierProvider<FolderSelectionManager, FolderSelectionState>(
create: (context) => FolderSelectionManager(),
child: StreamBuilder(
stream: account.datastore.db.managers.resources.filter((f) => f.id.equals(widget.id)).watchSingleOrNull(),
stream: account.resourceRepository.watchResource(widget.id),
builder: (context, snapshot) {
final folder = snapshot.data;
if (folder?.lastFetch == null) {
return buildEmptyView('Loading');
}
return StreamBuilder(
stream: account.datastore.db.managers.resources
.filter((f) => f.parent.id.equals(widget.id) & f.deleted.equals(false))
.orderBy((o) => o.dir.desc() & o.name.asc())
.watch(),
stream: account.resourceRepository.watchChildren(widget.id),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container();
@@ -42,7 +42,7 @@ class _ResourceDetailsRowState extends State<ResourceDetailsRow> {
if (details.data == _draggableDataSelected) {
final account = context.read<PhylumAccount>();
final selectedIds = context.read<FolderSelectionState>().selected;
final selected = await account.datastore.db.managers.resources.filter((f) => f.id.isIn(selectedIds)).get();
final selected = await account.resourceRepository.getResources(selectedIds);
for (final res in selected) {
account.addAction(ResourceMoveAction(r: res, parent: widget.r));
}