|
@@ -1,30 +1,35 @@
|
|
part of 'cell_service.dart';
|
|
part of 'cell_service.dart';
|
|
|
|
|
|
-typedef GridCellContext = _GridCellContext<String, String>;
|
|
|
|
-typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
|
|
|
|
-typedef GridDateCellContext = _GridCellContext<DateCellData, CalendarData>;
|
|
|
|
-typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
|
|
|
|
|
|
+typedef GridCellController = IGridCellController<String, String>;
|
|
|
|
+typedef GridSelectOptionCellController = IGridCellController<SelectOptionCellData, String>;
|
|
|
|
+typedef GridDateCellController = IGridCellController<DateCellData, CalendarData>;
|
|
|
|
+typedef GridURLCellController = IGridCellController<URLCellData, String>;
|
|
|
|
|
|
-class GridCellContextBuilder {
|
|
|
|
- final GridCellCacheService _cellCache;
|
|
|
|
|
|
+class GridCellControllerBuilder {
|
|
final GridCell _gridCell;
|
|
final GridCell _gridCell;
|
|
- GridCellContextBuilder({
|
|
|
|
- required GridCellCacheService cellCache,
|
|
|
|
- required GridCell gridCell,
|
|
|
|
- }) : _cellCache = cellCache,
|
|
|
|
|
|
+ final GridCellsCache _cellCache;
|
|
|
|
+ final GridFieldCache _fieldCache;
|
|
|
|
+
|
|
|
|
+ GridCellControllerBuilder(
|
|
|
|
+ {required GridCell gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache})
|
|
|
|
+ : _cellCache = cellCache,
|
|
|
|
+ _fieldCache = fieldCache,
|
|
_gridCell = gridCell;
|
|
_gridCell = gridCell;
|
|
|
|
|
|
- _GridCellContext build() {
|
|
|
|
|
|
+ IGridCellController build() {
|
|
|
|
+ final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache));
|
|
|
|
+
|
|
switch (_gridCell.field.fieldType) {
|
|
switch (_gridCell.field.fieldType) {
|
|
case FieldType.Checkbox:
|
|
case FieldType.Checkbox:
|
|
final cellDataLoader = GridCellDataLoader(
|
|
final cellDataLoader = GridCellDataLoader(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
parser: StringCellDataParser(),
|
|
parser: StringCellDataParser(),
|
|
);
|
|
);
|
|
- return GridCellContext(
|
|
|
|
|
|
+ return GridCellController(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
cellCache: _cellCache,
|
|
cellCache: _cellCache,
|
|
cellDataLoader: cellDataLoader,
|
|
cellDataLoader: cellDataLoader,
|
|
|
|
+ cellFieldNotifier: cellFieldNotifier,
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
);
|
|
);
|
|
case FieldType.DateTime:
|
|
case FieldType.DateTime:
|
|
@@ -34,22 +39,24 @@ class GridCellContextBuilder {
|
|
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
|
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
|
);
|
|
);
|
|
|
|
|
|
- return GridDateCellContext(
|
|
|
|
|
|
+ return GridDateCellController(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
cellCache: _cellCache,
|
|
cellCache: _cellCache,
|
|
cellDataLoader: cellDataLoader,
|
|
cellDataLoader: cellDataLoader,
|
|
|
|
+ cellFieldNotifier: cellFieldNotifier,
|
|
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
|
|
cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
|
|
);
|
|
);
|
|
case FieldType.Number:
|
|
case FieldType.Number:
|
|
final cellDataLoader = GridCellDataLoader(
|
|
final cellDataLoader = GridCellDataLoader(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
parser: StringCellDataParser(),
|
|
parser: StringCellDataParser(),
|
|
- config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true),
|
|
|
|
|
|
+ config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
|
);
|
|
);
|
|
- return GridCellContext(
|
|
|
|
|
|
+ return GridCellController(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
cellCache: _cellCache,
|
|
cellCache: _cellCache,
|
|
cellDataLoader: cellDataLoader,
|
|
cellDataLoader: cellDataLoader,
|
|
|
|
+ cellFieldNotifier: cellFieldNotifier,
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
);
|
|
);
|
|
case FieldType.RichText:
|
|
case FieldType.RichText:
|
|
@@ -57,10 +64,11 @@ class GridCellContextBuilder {
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
parser: StringCellDataParser(),
|
|
parser: StringCellDataParser(),
|
|
);
|
|
);
|
|
- return GridCellContext(
|
|
|
|
|
|
+ return GridCellController(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
cellCache: _cellCache,
|
|
cellCache: _cellCache,
|
|
cellDataLoader: cellDataLoader,
|
|
cellDataLoader: cellDataLoader,
|
|
|
|
+ cellFieldNotifier: cellFieldNotifier,
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
);
|
|
);
|
|
case FieldType.MultiSelect:
|
|
case FieldType.MultiSelect:
|
|
@@ -71,10 +79,11 @@ class GridCellContextBuilder {
|
|
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
|
config: const GridCellDataConfig(reloadOnFieldChanged: true),
|
|
);
|
|
);
|
|
|
|
|
|
- return GridSelectOptionCellContext(
|
|
|
|
|
|
+ return GridSelectOptionCellController(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
cellCache: _cellCache,
|
|
cellCache: _cellCache,
|
|
cellDataLoader: cellDataLoader,
|
|
cellDataLoader: cellDataLoader,
|
|
|
|
+ cellFieldNotifier: cellFieldNotifier,
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
);
|
|
);
|
|
|
|
|
|
@@ -83,10 +92,11 @@ class GridCellContextBuilder {
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
parser: URLCellDataParser(),
|
|
parser: URLCellDataParser(),
|
|
);
|
|
);
|
|
- return GridURLCellContext(
|
|
|
|
|
|
+ return GridURLCellController(
|
|
gridCell: _gridCell,
|
|
gridCell: _gridCell,
|
|
cellCache: _cellCache,
|
|
cellCache: _cellCache,
|
|
cellDataLoader: cellDataLoader,
|
|
cellDataLoader: cellDataLoader,
|
|
|
|
+ cellFieldNotifier: cellFieldNotifier,
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
|
|
);
|
|
);
|
|
}
|
|
}
|
|
@@ -95,37 +105,48 @@ class GridCellContextBuilder {
|
|
}
|
|
}
|
|
|
|
|
|
// T: the type of the CellData
|
|
// T: the type of the CellData
|
|
-// D: the type of the data that will be save to disk
|
|
|
|
|
|
+// D: the type of the data that will be saved to disk
|
|
// ignore: must_be_immutable
|
|
// ignore: must_be_immutable
|
|
-class _GridCellContext<T, D> extends Equatable {
|
|
|
|
|
|
+class IGridCellController<T, D> extends Equatable {
|
|
final GridCell gridCell;
|
|
final GridCell gridCell;
|
|
- final GridCellCacheService cellCache;
|
|
|
|
- final _GridCellCacheKey _cacheKey;
|
|
|
|
- final IGridCellDataLoader<T> cellDataLoader;
|
|
|
|
- final _GridCellDataPersistence<D> cellDataPersistence;
|
|
|
|
|
|
+ final GridCellsCache _cellsCache;
|
|
|
|
+ final GridCellId _cacheKey;
|
|
final FieldService _fieldService;
|
|
final FieldService _fieldService;
|
|
|
|
+ final GridCellFieldNotifier _cellFieldNotifier;
|
|
|
|
+ // final GridCellFieldNotifier _fieldNotifier;
|
|
|
|
+ final IGridCellDataLoader<T> _cellDataLoader;
|
|
|
|
+ final IGridCellDataPersistence<D> _cellDataPersistence;
|
|
|
|
|
|
late final CellListener _cellListener;
|
|
late final CellListener _cellListener;
|
|
- late final ValueNotifier<T?>? _cellDataNotifier;
|
|
|
|
|
|
+ ValueNotifier<T?>? _cellDataNotifier;
|
|
|
|
+
|
|
bool isListening = false;
|
|
bool isListening = false;
|
|
VoidCallback? _onFieldChangedFn;
|
|
VoidCallback? _onFieldChangedFn;
|
|
Timer? _loadDataOperation;
|
|
Timer? _loadDataOperation;
|
|
Timer? _saveDataOperation;
|
|
Timer? _saveDataOperation;
|
|
|
|
+ bool isDispose = false;
|
|
|
|
|
|
- _GridCellContext({
|
|
|
|
|
|
+ IGridCellController({
|
|
required this.gridCell,
|
|
required this.gridCell,
|
|
- required this.cellCache,
|
|
|
|
- required this.cellDataLoader,
|
|
|
|
- required this.cellDataPersistence,
|
|
|
|
- }) : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
|
|
|
|
- _cacheKey = _GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
|
|
|
-
|
|
|
|
- _GridCellContext<T, D> clone() {
|
|
|
|
- return _GridCellContext(
|
|
|
|
|
|
+ required GridCellsCache cellCache,
|
|
|
|
+ required GridCellFieldNotifier cellFieldNotifier,
|
|
|
|
+ required IGridCellDataLoader<T> cellDataLoader,
|
|
|
|
+ required IGridCellDataPersistence<D> cellDataPersistence,
|
|
|
|
+ // required GridFieldChangedNotifier notifierDelegate,
|
|
|
|
+ }) : _cellsCache = cellCache,
|
|
|
|
+ _cellDataLoader = cellDataLoader,
|
|
|
|
+ _cellDataPersistence = cellDataPersistence,
|
|
|
|
+ _cellFieldNotifier = cellFieldNotifier,
|
|
|
|
+ _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
|
|
|
|
+ _cacheKey = GridCellId(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
|
|
|
+
|
|
|
|
+ IGridCellController<T, D> clone() {
|
|
|
|
+ return IGridCellController(
|
|
gridCell: gridCell,
|
|
gridCell: gridCell,
|
|
- cellDataLoader: cellDataLoader,
|
|
|
|
- cellCache: cellCache,
|
|
|
|
- cellDataPersistence: cellDataPersistence);
|
|
|
|
|
|
+ cellDataLoader: _cellDataLoader,
|
|
|
|
+ cellCache: _cellsCache,
|
|
|
|
+ cellFieldNotifier: _cellFieldNotifier,
|
|
|
|
+ cellDataPersistence: _cellDataPersistence);
|
|
}
|
|
}
|
|
|
|
|
|
String get gridId => gridCell.gridId;
|
|
String get gridId => gridCell.gridId;
|
|
@@ -140,15 +161,23 @@ class _GridCellContext<T, D> extends Equatable {
|
|
|
|
|
|
FieldType get fieldType => gridCell.field.fieldType;
|
|
FieldType get fieldType => gridCell.field.fieldType;
|
|
|
|
|
|
- VoidCallback? startListening({required void Function(T?) onCellChanged}) {
|
|
|
|
|
|
+ VoidCallback? startListening({required void Function(T?) onCellChanged, VoidCallback? onCellFieldChanged}) {
|
|
if (isListening) {
|
|
if (isListening) {
|
|
Log.error("Already started. It seems like you should call clone first");
|
|
Log.error("Already started. It seems like you should call clone first");
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
-
|
|
|
|
isListening = true;
|
|
isListening = true;
|
|
- _cellDataNotifier = ValueNotifier(cellCache.get(_cacheKey));
|
|
|
|
|
|
+
|
|
|
|
+ /// The cell data will be changed by two reasons:
|
|
|
|
+ /// 1. User edit the cell
|
|
|
|
+ /// 2. User edit the field
|
|
|
|
+ /// For example: The number cell reload the cell data that carries the format
|
|
|
|
+ /// user input: 12
|
|
|
|
+ /// cell display: $12
|
|
|
|
+ _cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
|
|
_cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
|
_cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id);
|
|
|
|
+
|
|
|
|
+ /// 1.Listen on user edit event and load the new cell data if needed.
|
|
_cellListener.start(onCellChanged: (result) {
|
|
_cellListener.start(onCellChanged: (result) {
|
|
result.fold(
|
|
result.fold(
|
|
(_) => _loadData(),
|
|
(_) => _loadData(),
|
|
@@ -156,21 +185,21 @@ class _GridCellContext<T, D> extends Equatable {
|
|
);
|
|
);
|
|
});
|
|
});
|
|
|
|
|
|
- if (cellDataLoader.config.reloadOnFieldChanged) {
|
|
|
|
- _onFieldChangedFn = () {
|
|
|
|
- _loadData();
|
|
|
|
- };
|
|
|
|
- cellCache.addFieldListener(_cacheKey, _onFieldChangedFn!);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- onCellChangedFn() {
|
|
|
|
- onCellChanged(_cellDataNotifier?.value);
|
|
|
|
|
|
+ /// 2.Listen on the field event and load the cell data if needed.
|
|
|
|
+ _onFieldChangedFn = () {
|
|
|
|
+ if (onCellFieldChanged != null) {
|
|
|
|
+ onCellFieldChanged();
|
|
|
|
+ }
|
|
|
|
|
|
- if (cellDataLoader.config.reloadOnCellChanged) {
|
|
|
|
|
|
+ if (_cellDataLoader.config.reloadOnFieldChanged) {
|
|
_loadData();
|
|
_loadData();
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ _cellFieldNotifier.addFieldListener(_cacheKey, _onFieldChangedFn!);
|
|
|
|
|
|
|
|
+ /// Notify the listener, the cell data was changed.
|
|
|
|
+ onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
|
|
_cellDataNotifier?.addListener(onCellChangedFn);
|
|
_cellDataNotifier?.addListener(onCellChangedFn);
|
|
return onCellChangedFn;
|
|
return onCellChangedFn;
|
|
}
|
|
}
|
|
@@ -180,7 +209,7 @@ class _GridCellContext<T, D> extends Equatable {
|
|
}
|
|
}
|
|
|
|
|
|
T? getCellData({bool loadIfNoCache = true}) {
|
|
T? getCellData({bool loadIfNoCache = true}) {
|
|
- final data = cellCache.get(_cacheKey);
|
|
|
|
|
|
+ final data = _cellsCache.get(_cacheKey);
|
|
if (data == null && loadIfNoCache) {
|
|
if (data == null && loadIfNoCache) {
|
|
_loadData();
|
|
_loadData();
|
|
}
|
|
}
|
|
@@ -195,13 +224,13 @@ class _GridCellContext<T, D> extends Equatable {
|
|
if (deduplicate) {
|
|
if (deduplicate) {
|
|
_loadDataOperation?.cancel();
|
|
_loadDataOperation?.cancel();
|
|
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
|
|
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
|
|
- final result = await cellDataPersistence.save(data);
|
|
|
|
|
|
+ final result = await _cellDataPersistence.save(data);
|
|
if (resultCallback != null) {
|
|
if (resultCallback != null) {
|
|
resultCallback(result);
|
|
resultCallback(result);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
- final result = await cellDataPersistence.save(data);
|
|
|
|
|
|
+ final result = await _cellDataPersistence.save(data);
|
|
if (resultCallback != null) {
|
|
if (resultCallback != null) {
|
|
resultCallback(result);
|
|
resultCallback(result);
|
|
}
|
|
}
|
|
@@ -211,24 +240,55 @@ class _GridCellContext<T, D> extends Equatable {
|
|
void _loadData() {
|
|
void _loadData() {
|
|
_loadDataOperation?.cancel();
|
|
_loadDataOperation?.cancel();
|
|
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
|
|
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
|
|
- cellDataLoader.loadData().then((data) {
|
|
|
|
|
|
+ _cellDataLoader.loadData().then((data) {
|
|
_cellDataNotifier?.value = data;
|
|
_cellDataNotifier?.value = data;
|
|
- cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data));
|
|
|
|
|
|
+ _cellsCache.insert(_GridCellCacheItem(key: _cacheKey, object: data));
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
void dispose() {
|
|
void dispose() {
|
|
|
|
+ if (isDispose) {
|
|
|
|
+ Log.error("$this should only dispose once");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ isDispose = true;
|
|
_cellListener.stop();
|
|
_cellListener.stop();
|
|
_loadDataOperation?.cancel();
|
|
_loadDataOperation?.cancel();
|
|
_saveDataOperation?.cancel();
|
|
_saveDataOperation?.cancel();
|
|
|
|
+ _cellDataNotifier = null;
|
|
|
|
|
|
if (_onFieldChangedFn != null) {
|
|
if (_onFieldChangedFn != null) {
|
|
- cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!);
|
|
|
|
|
|
+ _cellFieldNotifier.removeFieldListener(_cacheKey, _onFieldChangedFn!);
|
|
_onFieldChangedFn = null;
|
|
_onFieldChangedFn = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@override
|
|
@override
|
|
- List<Object> get props => [cellCache.get(_cacheKey) ?? "", cellId];
|
|
|
|
|
|
+ List<Object> get props => [_cellsCache.get(_cacheKey) ?? "", cellId];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier {
|
|
|
|
+ final GridFieldCache _cache;
|
|
|
|
+ FieldChangesetCallback? _onChangesetFn;
|
|
|
|
+
|
|
|
|
+ _GridFieldChangedNotifierImpl(GridFieldCache cache) : _cache = cache;
|
|
|
|
+
|
|
|
|
+ @override
|
|
|
|
+ void dispose() {
|
|
|
|
+ if (_onChangesetFn != null) {
|
|
|
|
+ _cache.removeListener(onChangsetListener: _onChangesetFn!);
|
|
|
|
+ _onChangesetFn = null;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @override
|
|
|
|
+ void onFieldChanged(void Function(Field p1) callback) {
|
|
|
|
+ _onChangesetFn = (GridFieldChangeset changeset) {
|
|
|
|
+ for (final updatedField in changeset.updatedFields) {
|
|
|
|
+ callback(updatedField);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ _cache.addListener(onChangeset: _onChangesetFn);
|
|
|
|
+ }
|
|
}
|
|
}
|