OpenContacts/lib/clients/inventory_client.dart

181 lines
5.4 KiB
Dart
Raw Normal View History

2023-06-23 16:23:46 -04:00
import 'dart:async';
import 'package:recon/apis/record_api.dart';
import 'package:recon/clients/api_client.dart';
import 'package:recon/models/inventory/resonite_directory.dart';
import 'package:recon/models/records/record.dart';
2023-06-17 10:58:32 -04:00
import 'package:flutter/material.dart';
class InventoryClient extends ChangeNotifier {
final ApiClient apiClient;
2023-09-30 06:22:32 -04:00
Future<ResoniteDirectory>? _currentDirectory;
2023-06-17 10:58:32 -04:00
2023-09-30 06:22:32 -04:00
Future<ResoniteDirectory>? get directoryFuture => _currentDirectory;
2023-06-17 10:58:32 -04:00
InventoryClient({required this.apiClient});
2023-06-23 16:23:46 -04:00
final Map<String, Record> _selectedRecords = {};
List<Record> get selectedRecords => _selectedRecords.values.toList();
bool get isAnyRecordSelected => _selectedRecords.isNotEmpty;
bool isRecordSelected(Record record) => _selectedRecords.containsKey(record.id);
int get selectedRecordCount => _selectedRecords.length;
bool get onlyFilesSelected => _selectedRecords.values
.every((element) => element.recordType != RecordType.link && element.recordType != RecordType.directory);
void clearSelectedRecords() {
_selectedRecords.clear();
notifyListeners();
}
Future<void> deleteSelectedRecords() async {
for (final recordId in _selectedRecords.keys) {
await RecordApi.deleteRecord(apiClient, recordId: recordId);
}
_selectedRecords.clear();
reloadCurrentDirectory();
}
void toggleRecordSelected(Record record) {
if (_selectedRecords.containsKey(record.id)) {
_selectedRecords.remove(record.id);
} else {
_selectedRecords[record.id] = record;
}
notifyListeners();
}
2023-06-17 10:58:32 -04:00
Future<List<Record>> _getDirectory(Record record) async {
2023-09-30 06:22:32 -04:00
ResoniteDirectory? dir;
2023-06-17 11:36:52 -04:00
try {
dir = await _currentDirectory;
2023-06-23 16:23:46 -04:00
} catch (_) {}
2023-06-17 10:58:32 -04:00
final List<Record> records;
if (dir == null || record.isRoot) {
records = await RecordApi.getUserRecordsAt(
apiClient,
2023-09-30 06:22:32 -04:00
path: ResoniteDirectory.rootName,
2023-06-17 10:58:32 -04:00
);
} else {
if (record.recordType == RecordType.link) {
2023-06-23 16:23:46 -04:00
final linkRecord =
await RecordApi.getUserRecord(apiClient, recordId: record.linkRecordId, user: record.linkOwnerId);
records = await RecordApi.getUserRecordsAt(apiClient,
path: "${linkRecord.path}\\${record.name}", user: linkRecord.ownerId);
2023-06-17 10:58:32 -04:00
} else {
2023-06-23 16:23:46 -04:00
records =
await RecordApi.getUserRecordsAt(apiClient, path: "${record.path}\\${record.name}", user: record.ownerId);
2023-06-17 10:58:32 -04:00
}
}
return records;
}
void loadInventoryRoot() {
final rootRecord = Record.inventoryRoot();
final rootFuture = _getDirectory(rootRecord).then(
(records) {
2023-09-30 06:22:32 -04:00
final rootDir = ResoniteDirectory(
2023-06-17 10:58:32 -04:00
record: rootRecord,
children: [],
);
rootDir.children.addAll(
2023-09-30 06:22:32 -04:00
records.map((e) => ResoniteDirectory.fromRecord(record: e, parent: rootDir)).toList(),
2023-06-17 10:58:32 -04:00
);
return rootDir;
},
);
_currentDirectory = rootFuture;
2023-06-23 16:23:46 -04:00
}
void forceNotify() => notifyListeners();
Future<void> reloadCurrentDirectory() async {
final dir = await _currentDirectory;
if (dir == null) {
throw "Failed to reload: No directory loaded.";
}
_currentDirectory = _getDirectory(dir.record).then(
(records) {
2023-09-30 06:22:32 -04:00
final children = records.map((record) => ResoniteDirectory.fromRecord(record: record, parent: dir)).toList();
final newDir = ResoniteDirectory(record: dir.record, children: children, parent: dir.parent);
2023-06-23 16:23:46 -04:00
final parentIdx = dir.parent?.children.indexOf(dir) ?? -1;
if (parentIdx != -1) {
dir.parent?.children[parentIdx] = newDir;
}
return newDir;
},
).onError((error, stackTrace) {
return dir;
});
2023-06-17 10:58:32 -04:00
notifyListeners();
}
Future<void> navigateTo(Record record) async {
final dir = await _currentDirectory;
if (dir == null) {
throw "Failed to open: No directory loaded.";
}
if (record.recordType != RecordType.directory && record.recordType != RecordType.link) {
throw "Failed to open: Record is not a directory.";
}
final childDir = dir.findChildByRecord(record);
if (childDir == null) {
throw "Failed to open: Record is not a child of current directory.";
}
2023-06-23 16:23:46 -04:00
Object? caughtError;
2023-06-17 10:58:32 -04:00
if (childDir.isLoaded) {
_currentDirectory = Future.value(childDir);
} else {
_currentDirectory = _getDirectory(record).then(
(records) {
childDir.children.clear();
2023-09-30 06:22:32 -04:00
childDir.children.addAll(records.map((record) => ResoniteDirectory.fromRecord(record: record, parent: childDir)));
2023-06-17 10:58:32 -04:00
return childDir;
},
2023-06-17 13:28:23 -04:00
).onError((error, stackTrace) {
2023-06-23 16:23:46 -04:00
caughtError = error;
2023-06-17 13:28:23 -04:00
return dir;
});
2023-06-17 10:58:32 -04:00
}
notifyListeners();
2023-06-23 16:23:46 -04:00
await _currentDirectory;
// Dirty hack to throw the error here instead of letting the FutureBuilder handle it. This means we can keep showing
// the previous directory while also being able to display the error as a snackbar.
if (caughtError != null) {
throw caughtError!;
}
2023-06-17 10:58:32 -04:00
}
2023-06-17 11:36:52 -04:00
Future<void> navigateUp({int times = 1}) async {
2023-06-23 16:23:46 -04:00
if (times == 0) return;
2023-06-17 11:36:52 -04:00
var dir = await _currentDirectory;
2023-06-17 10:58:32 -04:00
if (dir == null) {
throw "Failed to navigate up: No directory loaded.";
}
if (dir.record.isRoot) {
throw "Failed navigate up: Already at root";
}
2023-06-17 11:36:52 -04:00
for (int i = 0; i < times; i++) {
dir = dir?.parent;
}
_currentDirectory = Future.value(dir);
2023-06-17 10:58:32 -04:00
notifyListeners();
}
}