new change to use intaleq_map sdk 04-16-4
This commit is contained in:
32
packages/get_storage/lib/src/read_write_value.dart
Normal file
32
packages/get_storage/lib/src/read_write_value.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:get_storage/src/storage_impl.dart';
|
||||
|
||||
typedef StorageFactory = GetStorage Function();
|
||||
|
||||
class ReadWriteValue<T> {
|
||||
final String key;
|
||||
final T defaultValue;
|
||||
final StorageFactory? getBox;
|
||||
// final EncodeObject encoder;
|
||||
|
||||
ReadWriteValue(
|
||||
this.key,
|
||||
this.defaultValue, [
|
||||
this.getBox,
|
||||
// this.encoder,
|
||||
]);
|
||||
|
||||
GetStorage _getRealBox() => getBox?.call() ?? GetStorage();
|
||||
|
||||
T get val => _getRealBox().read(key) ?? defaultValue;
|
||||
set val(T newVal) => _getRealBox().write(key, newVal);
|
||||
}
|
||||
|
||||
extension Data<T> on T {
|
||||
ReadWriteValue<T> val(
|
||||
String valueKey, {
|
||||
StorageFactory? getBox,
|
||||
T? defVal,
|
||||
}) {
|
||||
return ReadWriteValue(valueKey, defVal ?? this, getBox);
|
||||
}
|
||||
}
|
||||
100
packages/get_storage/lib/src/storage/html.dart
Normal file
100
packages/get_storage/lib/src/storage/html.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
// ignore: avoid_web_libraries_in_flutter
|
||||
import 'dart:html' as html;
|
||||
import '../value.dart';
|
||||
|
||||
class StorageImpl {
|
||||
StorageImpl(this.fileName, [this.path]);
|
||||
html.Storage get localStorage => html.window.localStorage;
|
||||
|
||||
final String? path;
|
||||
final String fileName;
|
||||
|
||||
ValueStorage<Map<String, dynamic>> subject =
|
||||
ValueStorage<Map<String, dynamic>>(<String, dynamic>{});
|
||||
|
||||
void clear() {
|
||||
localStorage.remove(fileName);
|
||||
subject.value.clear();
|
||||
|
||||
subject
|
||||
..value.clear()
|
||||
..changeValue("", null);
|
||||
}
|
||||
|
||||
Future<bool> _exists() async {
|
||||
return localStorage.containsKey(fileName);
|
||||
}
|
||||
|
||||
Future<void> flush() {
|
||||
return _writeToStorage(subject.value);
|
||||
}
|
||||
|
||||
T? read<T>(String key) {
|
||||
return subject.value[key] as T?;
|
||||
}
|
||||
|
||||
T getKeys<T>() {
|
||||
return subject.value.keys as T;
|
||||
}
|
||||
|
||||
T getValues<T>() {
|
||||
return subject.value.values as T;
|
||||
}
|
||||
|
||||
Future<void> init([Map<String, dynamic>? initialData]) async {
|
||||
subject.value = initialData ?? <String, dynamic>{};
|
||||
if (await _exists()) {
|
||||
await _readFromStorage();
|
||||
} else {
|
||||
await _writeToStorage(subject.value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void remove(String key) {
|
||||
subject
|
||||
..value.remove(key)
|
||||
..changeValue(key, null);
|
||||
// return _writeToStorage(subject.value);
|
||||
}
|
||||
|
||||
void write(String key, dynamic value) {
|
||||
subject
|
||||
..value[key] = value
|
||||
..changeValue(key, value);
|
||||
//return _writeToStorage(subject.value);
|
||||
}
|
||||
|
||||
// void writeInMemory(String key, dynamic value) {
|
||||
|
||||
// }
|
||||
|
||||
Future<void> _writeToStorage(Map<String, dynamic> data) async {
|
||||
localStorage.update(fileName, (val) => json.encode(subject.value),
|
||||
ifAbsent: () => json.encode(subject.value));
|
||||
}
|
||||
|
||||
Future<void> _readFromStorage() async {
|
||||
final dataFromLocal = localStorage.entries.firstWhereOrNull(
|
||||
(value) {
|
||||
return value.key == fileName;
|
||||
},
|
||||
);
|
||||
if (dataFromLocal != null) {
|
||||
subject.value = json.decode(dataFromLocal.value) as Map<String, dynamic>;
|
||||
} else {
|
||||
await _writeToStorage(<String, dynamic>{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FirstWhereExt<T> on Iterable<T> {
|
||||
T? firstWhereOrNull(bool Function(T element) test) {
|
||||
for (var element in this) {
|
||||
if (test(element)) return element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
153
packages/get_storage/lib/src/storage/io.dart
Normal file
153
packages/get_storage/lib/src/storage/io.dart
Normal file
@@ -0,0 +1,153 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../value.dart';
|
||||
|
||||
class StorageImpl {
|
||||
StorageImpl(this.fileName, [this.path]);
|
||||
|
||||
final String? path;
|
||||
final String fileName;
|
||||
|
||||
final ValueStorage<Map<String, dynamic>> subject =
|
||||
ValueStorage<Map<String, dynamic>>(<String, dynamic>{});
|
||||
|
||||
RandomAccessFile? _randomAccessfile;
|
||||
|
||||
void clear() async {
|
||||
subject
|
||||
..value.clear()
|
||||
..changeValue("", null);
|
||||
}
|
||||
|
||||
Future<void> deleteBox() async {
|
||||
final box = await _fileDb(isBackup: false);
|
||||
final backup = await _fileDb(isBackup: true);
|
||||
await Future.wait([box.delete(), backup.delete()]);
|
||||
}
|
||||
|
||||
Future<void> flush() async {
|
||||
final buffer = utf8.encode(json.encode(subject.value));
|
||||
final length = buffer.length;
|
||||
RandomAccessFile _file = await _getRandomFile();
|
||||
|
||||
_randomAccessfile = await _file.lock();
|
||||
_randomAccessfile = await _randomAccessfile!.setPosition(0);
|
||||
_randomAccessfile = await _randomAccessfile!.writeFrom(buffer);
|
||||
_randomAccessfile = await _randomAccessfile!.truncate(length);
|
||||
_randomAccessfile = await _file.unlock();
|
||||
_madeBackup();
|
||||
}
|
||||
|
||||
void _madeBackup() {
|
||||
_getFile(true).then(
|
||||
(value) => value.writeAsString(
|
||||
json.encode(subject.value),
|
||||
flush: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
T? read<T>(String key) {
|
||||
return subject.value[key] as T?;
|
||||
}
|
||||
|
||||
T getKeys<T>() {
|
||||
return subject.value.keys as T;
|
||||
}
|
||||
|
||||
T getValues<T>() {
|
||||
return subject.value.values as T;
|
||||
}
|
||||
|
||||
Future<void> init([Map<String, dynamic>? initialData]) async {
|
||||
subject.value = initialData ?? <String, dynamic>{};
|
||||
|
||||
RandomAccessFile _file = await _getRandomFile();
|
||||
return _file.lengthSync() == 0 ? flush() : _readFile();
|
||||
}
|
||||
|
||||
void remove(String key) {
|
||||
subject
|
||||
..value.remove(key)
|
||||
..changeValue(key, null);
|
||||
}
|
||||
|
||||
void write(String key, dynamic value) {
|
||||
subject
|
||||
..value[key] = value
|
||||
..changeValue(key, value);
|
||||
}
|
||||
|
||||
Future<void> _readFile() async {
|
||||
try {
|
||||
RandomAccessFile _file = await _getRandomFile();
|
||||
_file = await _file.setPosition(0);
|
||||
final buffer = new Uint8List(await _file.length());
|
||||
await _file.readInto(buffer);
|
||||
subject.value = json.decode(utf8.decode(buffer));
|
||||
} catch (e) {
|
||||
Get.log('Corrupted box, recovering backup file', isError: true);
|
||||
final _file = await _getFile(true);
|
||||
|
||||
final content = await _file.readAsString()
|
||||
..trim();
|
||||
|
||||
if (content.isEmpty) {
|
||||
subject.value = {};
|
||||
} else {
|
||||
try {
|
||||
subject.value = (json.decode(content) as Map<String, dynamic>?) ?? {};
|
||||
} catch (e) {
|
||||
Get.log('Can not recover Corrupted box', isError: true);
|
||||
subject.value = {};
|
||||
}
|
||||
}
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
Future<RandomAccessFile> _getRandomFile() async {
|
||||
if (_randomAccessfile != null) return _randomAccessfile!;
|
||||
final fileDb = await _getFile(false);
|
||||
_randomAccessfile = await fileDb.open(mode: FileMode.append);
|
||||
|
||||
return _randomAccessfile!;
|
||||
}
|
||||
|
||||
Future<File> _getFile(bool isBackup) async {
|
||||
final fileDb = await _fileDb(isBackup: isBackup);
|
||||
if (!fileDb.existsSync()) {
|
||||
fileDb.createSync(recursive: true);
|
||||
}
|
||||
return fileDb;
|
||||
}
|
||||
|
||||
Future<File> _fileDb({required bool isBackup}) async {
|
||||
final dir = await _getImplicitDir();
|
||||
final _path = await _getPath(isBackup, path ?? dir.path);
|
||||
final _file = File(_path);
|
||||
return _file;
|
||||
}
|
||||
|
||||
Future<Directory> _getImplicitDir() async {
|
||||
try {
|
||||
return getApplicationDocumentsDirectory();
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _getPath(bool isBackup, String? path) async {
|
||||
final _isWindows = GetPlatform.isWindows;
|
||||
final _separator = _isWindows ? '\\' : '/';
|
||||
return isBackup
|
||||
? '$path$_separator$fileName.bak'
|
||||
: '$path$_separator$fileName.gs';
|
||||
}
|
||||
}
|
||||
182
packages/get_storage/lib/src/storage_impl.dart
Normal file
182
packages/get_storage/lib/src/storage_impl.dart
Normal file
@@ -0,0 +1,182 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get/utils.dart';
|
||||
|
||||
import 'storage/html.dart' if (dart.library.io) 'storage/io.dart';
|
||||
import 'value.dart';
|
||||
|
||||
/// Instantiate GetStorage to access storage driver apis
|
||||
class GetStorage {
|
||||
factory GetStorage(
|
||||
[String container = 'GetStorage',
|
||||
String? path,
|
||||
Map<String, dynamic>? initialData]) {
|
||||
if (_sync.containsKey(container)) {
|
||||
return _sync[container]!;
|
||||
} else {
|
||||
final instance = GetStorage._internal(container, path, initialData);
|
||||
_sync[container] = instance;
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
GetStorage._internal(String key,
|
||||
[String? path, Map<String, dynamic>? initialData]) {
|
||||
_concrete = StorageImpl(key, path);
|
||||
_initialData = initialData;
|
||||
|
||||
initStorage = Future<bool>(() async {
|
||||
await _init();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static final Map<String, GetStorage> _sync = {};
|
||||
|
||||
final microtask = Microtask();
|
||||
|
||||
/// Start the storage drive. It's important to use await before calling this API, or side effects will occur.
|
||||
static Future<bool> init([String container = 'GetStorage']) {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
return GetStorage(container).initStorage;
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
try {
|
||||
await _concrete.init(_initialData);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a value in your container with the given key.
|
||||
T? read<T>(String key) {
|
||||
return _concrete.read(key);
|
||||
}
|
||||
|
||||
T getKeys<T>() {
|
||||
return _concrete.getKeys();
|
||||
}
|
||||
|
||||
T getValues<T>() {
|
||||
return _concrete.getValues();
|
||||
}
|
||||
|
||||
/// return data true if value is different of null;
|
||||
bool hasData(String key) {
|
||||
return (read(key) == null ? false : true);
|
||||
}
|
||||
|
||||
Map<String, dynamic> get changes => _concrete.subject.changes;
|
||||
|
||||
/// Listen changes in your container
|
||||
VoidCallback listen(VoidCallback value) {
|
||||
return _concrete.subject.addListener(value);
|
||||
}
|
||||
|
||||
Map<Function, Function> _keyListeners = <Function, Function>{};
|
||||
|
||||
VoidCallback listenKey(String key, ValueSetter callback) {
|
||||
final VoidCallback listen = () {
|
||||
if (changes.keys.first == key) {
|
||||
callback(changes[key]);
|
||||
}
|
||||
};
|
||||
|
||||
_keyListeners[callback] = listen;
|
||||
return _concrete.subject.addListener(listen);
|
||||
}
|
||||
|
||||
// /// Remove listen of your container
|
||||
// void removeKeyListen(Function(Map<String, dynamic>) callback) {
|
||||
// _concrete.subject.removeListener(_keyListeners[callback]);
|
||||
// }
|
||||
|
||||
// /// Remove listen of your container
|
||||
// void removeListen(void Function() listener) {
|
||||
// _concrete.subject.removeListener(listener);
|
||||
// }
|
||||
|
||||
/// Write data on your container
|
||||
Future<void> write(String key, dynamic value) async {
|
||||
writeInMemory(key, value);
|
||||
// final _encoded = json.encode(value);
|
||||
// await _concrete.write(key, json.decode(_encoded));
|
||||
|
||||
return _tryFlush();
|
||||
}
|
||||
|
||||
void writeInMemory(String key, dynamic value) {
|
||||
_concrete.write(key, value);
|
||||
}
|
||||
|
||||
/// Write data on your only if data is null
|
||||
Future<void> writeIfNull(String key, dynamic value) async {
|
||||
if (read(key) != null) return;
|
||||
return write(key, value);
|
||||
}
|
||||
|
||||
/// remove data from container by key
|
||||
Future<void> remove(String key) async {
|
||||
_concrete.remove(key);
|
||||
return _tryFlush();
|
||||
}
|
||||
|
||||
/// clear all data on your container
|
||||
Future<void> erase() async {
|
||||
_concrete.clear();
|
||||
return _tryFlush();
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
return _tryFlush();
|
||||
}
|
||||
|
||||
Future<void> _tryFlush() async {
|
||||
return microtask.exec(_addToQueue);
|
||||
}
|
||||
|
||||
Future _addToQueue() {
|
||||
return queue.add(_flush);
|
||||
}
|
||||
|
||||
Future<void> _flush() async {
|
||||
try {
|
||||
await _concrete.flush();
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
late StorageImpl _concrete;
|
||||
|
||||
GetQueue queue = GetQueue();
|
||||
|
||||
/// listenable of container
|
||||
ValueStorage<Map<String, dynamic>> get listenable => _concrete.subject;
|
||||
|
||||
/// Start the storage drive. Important: use await before calling this api, or side effects will happen.
|
||||
late Future<bool> initStorage;
|
||||
|
||||
Map<String, dynamic>? _initialData;
|
||||
}
|
||||
|
||||
class Microtask {
|
||||
int _version = 0;
|
||||
int _microtask = 0;
|
||||
|
||||
void exec(Function callback) {
|
||||
if (_microtask == _version) {
|
||||
_microtask++;
|
||||
scheduleMicrotask(() {
|
||||
_version++;
|
||||
_microtask = _version;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef KeyCallback = Function(String);
|
||||
28
packages/get_storage/lib/src/value.dart
Normal file
28
packages/get_storage/lib/src/value.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get/state_manager.dart';
|
||||
|
||||
class ValueStorage<T> {
|
||||
ValueStorage(T value) : this._value = Value(value);
|
||||
|
||||
final Value _value;
|
||||
|
||||
Map<String, dynamic> changes = <String, dynamic>{};
|
||||
|
||||
void changeValue(String key, dynamic value) {
|
||||
changes = {key: value};
|
||||
// ignore: invalid_use_of_protected_member
|
||||
_value.refresh();
|
||||
}
|
||||
|
||||
T get value => _value.value;
|
||||
|
||||
set value(T value) {
|
||||
_value.value = value;
|
||||
}
|
||||
|
||||
VoidCallback addListener(VoidCallback callback) {
|
||||
_value.addListener(callback);
|
||||
|
||||
return () => _value.removeListener(callback);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user