2021-03-10 16:54:41 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:comunic/constants.dart';
|
2021-03-14 16:50:45 +00:00
|
|
|
import 'package:comunic/utils/flutter_utils.dart';
|
2021-04-13 17:01:26 +00:00
|
|
|
import 'package:comunic/utils/log_utils.dart';
|
2021-03-10 16:54:41 +00:00
|
|
|
import 'package:path/path.dart' as path;
|
|
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
|
|
|
|
/// Base serialization helper
|
|
|
|
///
|
|
|
|
/// @author Pierre Hubert
|
|
|
|
|
|
|
|
abstract class SerializableElement<T> extends Comparable<T> {
|
|
|
|
Map<String, dynamic> toJson();
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract class BaseSerializationHelper<T extends SerializableElement> {
|
|
|
|
/// List cache
|
|
|
|
List<T> _cache;
|
|
|
|
|
|
|
|
/// The name of the type of data to serialise
|
|
|
|
String get type;
|
|
|
|
|
|
|
|
/// Parse an json entry into a [T] object
|
|
|
|
T parse(Map<String, dynamic> m);
|
|
|
|
|
|
|
|
/// Get the file where data should be stored
|
|
|
|
Future<File> _getFilePath() async {
|
|
|
|
final dir = await getApplicationDocumentsDirectory();
|
|
|
|
final targetDir =
|
|
|
|
Directory(path.join(dir.absolute.path, SERIALIZATION_DIRECTORY));
|
|
|
|
|
|
|
|
targetDir.create(recursive: true);
|
|
|
|
|
|
|
|
return File(path.join(targetDir.absolute.path, type));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Load the cache
|
|
|
|
Future<void> _loadCache() async {
|
|
|
|
if (_cache != null) return;
|
|
|
|
|
2021-03-14 16:50:45 +00:00
|
|
|
if (isWeb) {
|
|
|
|
_cache = [];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-10 16:54:41 +00:00
|
|
|
try {
|
|
|
|
final file = await _getFilePath();
|
|
|
|
|
2021-03-13 14:14:54 +00:00
|
|
|
if (!await file.exists()) return _cache = [];
|
2021-03-10 16:54:41 +00:00
|
|
|
|
|
|
|
final List<dynamic> json = jsonDecode(await file.readAsString());
|
|
|
|
_cache = json.cast<Map<String, dynamic>>().map(parse).toList();
|
|
|
|
|
|
|
|
_cache.sort();
|
|
|
|
} catch (e, s) {
|
2021-04-13 17:01:26 +00:00
|
|
|
logError(e, s);
|
|
|
|
print("Failed to read serialized data!");
|
2021-03-13 14:14:54 +00:00
|
|
|
_cache = [];
|
2021-03-10 16:54:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Save the cache to the persistent memory
|
|
|
|
Future<void> _saveCache() async {
|
2021-03-14 16:50:45 +00:00
|
|
|
if (isWeb) return;
|
|
|
|
|
2021-04-13 17:01:26 +00:00
|
|
|
try {
|
|
|
|
final file = await _getFilePath();
|
|
|
|
await file.writeAsString(jsonEncode(
|
|
|
|
_cache.map((e) => e.toJson()).toList().cast<Map<String, dynamic>>()));
|
|
|
|
} catch (e, s) {
|
|
|
|
print("Failed to write file!");
|
|
|
|
logError(e, s);
|
|
|
|
}
|
2021-03-10 16:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the current list of elements
|
|
|
|
Future<List<T>> getList() async {
|
|
|
|
await _loadCache();
|
|
|
|
return List.from(_cache);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a new list of conversations
|
|
|
|
Future<void> setList(List<T> list) async {
|
|
|
|
_cache = List.from(list);
|
|
|
|
await _saveCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert new element
|
|
|
|
Future<void> insert(T el) async {
|
|
|
|
await _loadCache();
|
|
|
|
_cache.add(el);
|
|
|
|
_cache.sort();
|
|
|
|
await _saveCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert new element
|
|
|
|
Future<void> insertMany(List<T> els) async {
|
|
|
|
await _loadCache();
|
|
|
|
_cache.addAll(els);
|
|
|
|
_cache.sort();
|
|
|
|
await _saveCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if any entry in the last match the predicate
|
|
|
|
Future<bool> any(bool isContained(T t)) async {
|
|
|
|
await _loadCache();
|
|
|
|
return _cache.any((element) => isContained(element));
|
|
|
|
}
|
|
|
|
|
2021-03-14 17:15:38 +00:00
|
|
|
Future<bool> has(T el) => any((t) => t == el);
|
|
|
|
|
2021-03-10 16:54:41 +00:00
|
|
|
/// Check if any entry in the last match the predicate
|
|
|
|
Future<T> first(bool filter(T t)) async {
|
|
|
|
await _loadCache();
|
|
|
|
return _cache.firstWhere((element) => filter(element));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Replace an element with another one
|
|
|
|
Future<void> insertOrReplaceElement(bool isToReplace(T t), T newEl) async {
|
|
|
|
await _loadCache();
|
|
|
|
|
|
|
|
// Insert or replace the element
|
2021-03-14 14:27:54 +00:00
|
|
|
_cache = _cache.where((element) => !isToReplace(element)).toList();
|
2021-03-10 16:54:41 +00:00
|
|
|
_cache.add(newEl);
|
|
|
|
|
|
|
|
_cache.sort();
|
|
|
|
await _saveCache();
|
|
|
|
}
|
|
|
|
|
2021-03-14 17:15:38 +00:00
|
|
|
/// Insert or replace many elements
|
|
|
|
Future<void> insertOrReplaceElements(List<T> list) async {
|
|
|
|
await _loadCache();
|
|
|
|
|
|
|
|
_cache.removeWhere((element) => list.any((newEl) => element == newEl));
|
|
|
|
_cache.addAll(list);
|
|
|
|
|
|
|
|
await _saveCache();
|
|
|
|
}
|
|
|
|
|
2021-03-10 16:54:41 +00:00
|
|
|
/// Remove elements
|
|
|
|
Future<void> removeElement(bool isToRemove(T t)) async {
|
|
|
|
await _loadCache();
|
|
|
|
_cache.removeWhere((element) => isToRemove(element));
|
|
|
|
await _saveCache();
|
|
|
|
}
|
2021-03-14 17:15:38 +00:00
|
|
|
|
|
|
|
/// Remove all elements
|
|
|
|
Future<void> removeAll() async {
|
|
|
|
_cache = [];
|
|
|
|
await _saveCache();
|
|
|
|
}
|
2021-03-10 16:54:41 +00:00
|
|
|
}
|