From 3844d6aad1c95829f08f1e29db79886e35932cb0 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 14 Jul 2022 20:07:47 +0800 Subject: [PATCH] refactor: add documentation to GridCellContext --- .../application/grid/block/block_service.dart | 6 +- .../grid/cell/cell_service/cache.dart | 60 ++------- .../cell/cell_service/cell_data_loader.dart | 33 ----- .../cell_service/cell_data_persistence.dart | 6 +- .../cell_service/cell_field_notifier.dart | 56 ++++++++ .../grid/cell/cell_service/cell_service.dart | 1 - .../cell/cell_service/context_builder.dart | 126 ++++++++++++------ .../workspace/application/grid/grid_bloc.dart | 2 +- .../application/grid/grid_service.dart | 2 +- .../application/grid/row/row_bloc.dart | 4 +- .../application/grid/row/row_detail_bloc.dart | 4 +- .../application/grid/row/row_service.dart | 17 +-- .../grid/src/widgets/cell/cell_builder.dart | 51 ++++++- .../grid/src/widgets/row/grid_row.dart | 32 +++-- .../grid/src/widgets/row/row_detail.dart | 18 +-- 15 files changed, 254 insertions(+), 164 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_field_notifier.dart diff --git a/frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart b/frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart index 3fb734db70..a531e65d2e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart @@ -9,18 +9,18 @@ import 'block_listener.dart'; class GridBlockCacheService { final String gridId; final GridBlock block; - late GridRowCacheService _rowCache; + late GridRowsCache _rowCache; late GridBlockListener _listener; List get rows => _rowCache.rows; - GridRowCacheService get rowCache => _rowCache; + GridRowsCache get rowCache => _rowCache; GridBlockCacheService({ required this.gridId, required this.block, required GridFieldCache fieldCache, }) { - _rowCache = GridRowCacheService( + _rowCache = GridRowsCache( gridId: gridId, block: block, delegate: GridRowCacheDelegateImpl(fieldCache), diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cache.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cache.dart index ccf47fddb3..e10c73c7bb 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cache.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cache.dart @@ -3,7 +3,7 @@ part of 'cell_service.dart'; typedef GridCellMap = LinkedHashMap; class _GridCellCacheObject { - _GridCellCacheKey key; + GridCellCacheKey key; dynamic object; _GridCellCacheObject({ required this.key, @@ -11,67 +11,26 @@ class _GridCellCacheObject { }); } -class _GridCellCacheKey { +class GridCellCacheKey { final String fieldId; final String rowId; - _GridCellCacheKey({ + GridCellCacheKey({ required this.fieldId, required this.rowId, }); } -abstract class GridCellCacheDelegate { - void onFieldUpdated(void Function(Field) callback); -} - -class GridCellCacheService { +class GridCellsCache { final String gridId; - final GridCellCacheDelegate delegate; - - /// fieldId: {objectId: callback} - final Map>> _fieldListenerByFieldId = {}; /// fieldId: {cacheKey: cacheData} final Map> _cellDataByFieldId = {}; - GridCellCacheService({ + GridCellsCache({ required this.gridId, - required this.delegate, - }) { - delegate.onFieldUpdated((field) { - _cellDataByFieldId.remove(field.id); - final map = _fieldListenerByFieldId[field.id]; - if (map != null) { - for (final callbacks in map.values) { - for (final callback in callbacks) { - callback(); - } - } - } - }); - } + }); - void addFieldListener(_GridCellCacheKey cacheKey, VoidCallback onFieldChanged) { - var map = _fieldListenerByFieldId[cacheKey.fieldId]; - if (map == null) { - _fieldListenerByFieldId[cacheKey.fieldId] = {}; - map = _fieldListenerByFieldId[cacheKey.fieldId]; - map![cacheKey.rowId] = [onFieldChanged]; - } else { - var objects = map[cacheKey.rowId]; - if (objects == null) { - map[cacheKey.rowId] = [onFieldChanged]; - } else { - objects.add(onFieldChanged); - } - } - } - - void removeFieldListener(_GridCellCacheKey cacheKey, VoidCallback fn) { - var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId]; - final index = callbacks?.indexWhere((callback) => callback == fn); - if (index != null && index != -1) { - callbacks?.removeAt(index); - } + void remove(String fieldId) { + _cellDataByFieldId.remove(fieldId); } void insert(T item) { @@ -84,7 +43,7 @@ class GridCellCacheService { map![item.key.rowId] = item.object; } - T? get(_GridCellCacheKey key) { + T? get(GridCellCacheKey key) { final map = _cellDataByFieldId[key.fieldId]; if (map == null) { return null; @@ -103,7 +62,6 @@ class GridCellCacheService { } Future dispose() async { - _fieldListenerByFieldId.clear(); _cellDataByFieldId.clear(); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart index 676e3f66d0..95b7b77b61 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_loader.dart @@ -3,23 +3,13 @@ part of 'cell_service.dart'; abstract class IGridCellDataConfig { // The cell data will reload if it receives the field's change notification. bool get reloadOnFieldChanged; - - // When the reloadOnCellChanged is true, it will load the cell data after user input. - // For example: The number cell reload the cell data that carries the format - // user input: 12 - // cell display: $12 - bool get reloadOnCellChanged; } class GridCellDataConfig implements IGridCellDataConfig { - @override - final bool reloadOnCellChanged; - @override final bool reloadOnFieldChanged; const GridCellDataConfig({ - this.reloadOnCellChanged = false, this.reloadOnFieldChanged = false, }); } @@ -72,29 +62,6 @@ class GridCellDataLoader extends IGridCellDataLoader { } } -class SelectOptionCellDataLoader extends IGridCellDataLoader { - final SelectOptionService service; - final GridCell gridCell; - SelectOptionCellDataLoader({ - required this.gridCell, - }) : service = SelectOptionService(gridCell: gridCell); - @override - Future loadData() async { - return service.getOpitonContext().then((result) { - return result.fold( - (data) => data, - (err) { - Log.error(err); - return null; - }, - ); - }); - } - - @override - IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true); -} - class StringCellDataParser implements ICellDataParser { @override String? parserData(List data) { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart index 2ad217e062..9e2f577d7c 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart @@ -1,10 +1,10 @@ part of 'cell_service.dart'; -abstract class _GridCellDataPersistence { +abstract class IGridCellDataPersistence { Future> save(D data); } -class CellDataPersistence implements _GridCellDataPersistence { +class CellDataPersistence implements IGridCellDataPersistence { final GridCell gridCell; CellDataPersistence({ @@ -35,7 +35,7 @@ class CalendarData with _$CalendarData { const factory CalendarData({required DateTime date, String? time}) = _CalendarData; } -class DateCellDataPersistence implements _GridCellDataPersistence { +class DateCellDataPersistence implements IGridCellDataPersistence { final GridCell gridCell; DateCellDataPersistence({ required this.gridCell, diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_field_notifier.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_field_notifier.dart new file mode 100644 index 0000000000..18dae774d7 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_field_notifier.dart @@ -0,0 +1,56 @@ +import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; +import 'package:flutter/foundation.dart'; + +import 'cell_service.dart'; + +abstract class GridFieldChangedNotifier { + void onFieldChanged(void Function(Field) callback); +} + +class GridCellFieldNotifier { + /// fieldId: {objectId: callback} + final Map>> _fieldListenerByFieldId = {}; + + GridCellFieldNotifier({required GridFieldChangedNotifier notifier}) { + notifier.onFieldChanged( + (field) { + final map = _fieldListenerByFieldId[field.id]; + if (map != null) { + for (final callbacks in map.values) { + for (final callback in callbacks) { + callback(); + } + } + } + }, + ); + } + + void addFieldListener(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) { + var map = _fieldListenerByFieldId[cacheKey.fieldId]; + if (map == null) { + _fieldListenerByFieldId[cacheKey.fieldId] = {}; + map = _fieldListenerByFieldId[cacheKey.fieldId]; + map![cacheKey.rowId] = [onFieldChanged]; + } else { + var objects = map[cacheKey.rowId]; + if (objects == null) { + map[cacheKey.rowId] = [onFieldChanged]; + } else { + objects.add(onFieldChanged); + } + } + } + + void removeFieldListener(GridCellCacheKey cacheKey, VoidCallback fn) { + var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId]; + final index = callbacks?.indexWhere((callback) => callback == fn); + if (index != null && index != -1) { + callbacks?.removeAt(index); + } + } + + Future dispose() async { + _fieldListenerByFieldId.clear(); + } +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart index 5e406c0c26..02561f80f8 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart @@ -14,7 +14,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; -import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'dart:convert' show utf8; part 'cell_service.freezed.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index 00264ad4b0..2ff3e60c3e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -6,15 +6,19 @@ typedef GridDateCellContext = _GridCellContext; typedef GridURLCellContext = _GridCellContext; class GridCellContextBuilder { - final GridCellCacheService _cellCache; final GridCell _gridCell; - GridCellContextBuilder({ - required GridCellCacheService cellCache, - required GridCell gridCell, - }) : _cellCache = cellCache, + final GridCellsCache _cellCache; + final GridFieldCache _fieldCache; + + GridCellContextBuilder( + {required GridCell gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache}) + : _cellCache = cellCache, + _fieldCache = fieldCache, _gridCell = gridCell; _GridCellContext build() { + final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache)); + switch (_gridCell.field.fieldType) { case FieldType.Checkbox: final cellDataLoader = GridCellDataLoader( @@ -25,6 +29,7 @@ class GridCellContextBuilder { gridCell: _gridCell, cellCache: _cellCache, cellDataLoader: cellDataLoader, + cellFieldNotifier: cellFieldNotifier, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.DateTime: @@ -38,18 +43,20 @@ class GridCellContextBuilder { gridCell: _gridCell, cellCache: _cellCache, cellDataLoader: cellDataLoader, + cellFieldNotifier: cellFieldNotifier, cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell), ); case FieldType.Number: final cellDataLoader = GridCellDataLoader( gridCell: _gridCell, parser: StringCellDataParser(), - config: const GridCellDataConfig(reloadOnCellChanged: true, reloadOnFieldChanged: true), + config: const GridCellDataConfig(reloadOnFieldChanged: true), ); return GridCellContext( gridCell: _gridCell, cellCache: _cellCache, cellDataLoader: cellDataLoader, + cellFieldNotifier: cellFieldNotifier, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.RichText: @@ -61,6 +68,7 @@ class GridCellContextBuilder { gridCell: _gridCell, cellCache: _cellCache, cellDataLoader: cellDataLoader, + cellFieldNotifier: cellFieldNotifier, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); case FieldType.MultiSelect: @@ -75,6 +83,7 @@ class GridCellContextBuilder { gridCell: _gridCell, cellCache: _cellCache, cellDataLoader: cellDataLoader, + cellFieldNotifier: cellFieldNotifier, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); @@ -87,6 +96,7 @@ class GridCellContextBuilder { gridCell: _gridCell, cellCache: _cellCache, cellDataLoader: cellDataLoader, + cellFieldNotifier: cellFieldNotifier, cellDataPersistence: CellDataPersistence(gridCell: _gridCell), ); } @@ -99,14 +109,17 @@ class GridCellContextBuilder { // ignore: must_be_immutable class _GridCellContext extends Equatable { final GridCell gridCell; - final GridCellCacheService cellCache; - final _GridCellCacheKey _cacheKey; - final IGridCellDataLoader cellDataLoader; - final _GridCellDataPersistence cellDataPersistence; + final GridCellsCache _cellsCache; + final GridCellCacheKey _cacheKey; final FieldService _fieldService; + final GridCellFieldNotifier _cellFieldNotifier; + // final GridCellFieldNotifier _fieldNotifier; + final IGridCellDataLoader _cellDataLoader; + final IGridCellDataPersistence _cellDataPersistence; late final CellListener _cellListener; late final ValueNotifier? _cellDataNotifier; + bool isListening = false; VoidCallback? _onFieldChangedFn; Timer? _loadDataOperation; @@ -114,18 +127,25 @@ class _GridCellContext extends Equatable { _GridCellContext({ 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); + required GridCellsCache cellCache, + required GridCellFieldNotifier cellFieldNotifier, + required IGridCellDataLoader cellDataLoader, + required IGridCellDataPersistence cellDataPersistence, + // required GridFieldChangedNotifier notifierDelegate, + }) : _cellsCache = cellCache, + _cellDataLoader = cellDataLoader, + _cellDataPersistence = cellDataPersistence, + _cellFieldNotifier = cellFieldNotifier, + _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id), + _cacheKey = GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id); _GridCellContext clone() { return _GridCellContext( gridCell: gridCell, - cellDataLoader: cellDataLoader, - cellCache: cellCache, - cellDataPersistence: cellDataPersistence); + cellDataLoader: _cellDataLoader, + cellCache: _cellsCache, + cellFieldNotifier: _cellFieldNotifier, + cellDataPersistence: _cellDataPersistence); } String get gridId => gridCell.gridId; @@ -145,10 +165,18 @@ class _GridCellContext extends Equatable { Log.error("Already started. It seems like you should call clone first"); return null; } - 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); + + /// Listen on user edit event and load the new cell data if needed. _cellListener.start(onCellChanged: (result) { result.fold( (_) => _loadData(), @@ -156,21 +184,14 @@ class _GridCellContext extends Equatable { ); }); - if (cellDataLoader.config.reloadOnFieldChanged) { - _onFieldChangedFn = () { - _loadData(); - }; - cellCache.addFieldListener(_cacheKey, _onFieldChangedFn!); - } - - onCellChangedFn() { - onCellChanged(_cellDataNotifier?.value); - - if (cellDataLoader.config.reloadOnCellChanged) { - _loadData(); - } + /// Listen on the field event and load the cell data if needed. + if (_cellDataLoader.config.reloadOnFieldChanged) { + _onFieldChangedFn = () => _loadData(); + _cellFieldNotifier.addFieldListener(_cacheKey, _onFieldChangedFn!); } + /// Notify the listener, the cell data was changed. + onCellChangedFn() => onCellChanged(_cellDataNotifier?.value); _cellDataNotifier?.addListener(onCellChangedFn); return onCellChangedFn; } @@ -180,7 +201,7 @@ class _GridCellContext extends Equatable { } T? getCellData({bool loadIfNoCache = true}) { - final data = cellCache.get(_cacheKey); + final data = _cellsCache.get(_cacheKey); if (data == null && loadIfNoCache) { _loadData(); } @@ -195,13 +216,13 @@ class _GridCellContext extends Equatable { if (deduplicate) { _loadDataOperation?.cancel(); _loadDataOperation = Timer(const Duration(milliseconds: 300), () async { - final result = await cellDataPersistence.save(data); + final result = await _cellDataPersistence.save(data); if (resultCallback != null) { resultCallback(result); } }); } else { - final result = await cellDataPersistence.save(data); + final result = await _cellDataPersistence.save(data); if (resultCallback != null) { resultCallback(result); } @@ -211,9 +232,9 @@ class _GridCellContext extends Equatable { void _loadData() { _loadDataOperation?.cancel(); _loadDataOperation = Timer(const Duration(milliseconds: 10), () { - cellDataLoader.loadData().then((data) { + _cellDataLoader.loadData().then((data) { _cellDataNotifier?.value = data; - cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data)); + _cellsCache.insert(_GridCellCacheObject(key: _cacheKey, object: data)); }); }); } @@ -224,11 +245,36 @@ class _GridCellContext extends Equatable { _saveDataOperation?.cancel(); if (_onFieldChangedFn != null) { - cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!); + _cellFieldNotifier.removeFieldListener(_cacheKey, _onFieldChangedFn!); _onFieldChangedFn = null; } } @override - List get props => [cellCache.get(_cacheKey) ?? "", cellId]; + List 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); + } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart index 49e90b6a9e..e6fff41317 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -68,7 +68,7 @@ class GridBloc extends Bloc { return super.close(); } - GridRowCacheService? getRowCache(String blockId, String rowId) { + GridRowsCache? getRowCache(String blockId, String rowId) { final GridBlockCacheService? blockCache = _blocks[blockId]; return blockCache?.rowCache; } diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart index 391a5d13a2..3e9cbc2eee 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart @@ -202,7 +202,7 @@ class GridRowCacheDelegateImpl extends GridRowCacheDelegate { } @override - void onFieldUpdated(void Function(Field) callback) { + void onFieldChanged(void Function(Field) callback) { _onChangesetFn = (GridFieldChangeset changeset) { for (final updatedField in changeset.updatedFields) { callback(updatedField); diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart index 7f565b500a..5af7c02a5d 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart @@ -11,12 +11,12 @@ part 'row_bloc.freezed.dart'; class RowBloc extends Bloc { final RowService _rowService; - final GridRowCacheService _rowCache; + final GridRowsCache _rowCache; void Function()? _rowListenFn; RowBloc({ required GridRow rowData, - required GridRowCacheService rowCache, + required GridRowsCache rowCache, }) : _rowService = RowService( gridId: rowData.gridId, blockId: rowData.blockId, diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart index f17f0f4cb6..0613b388a4 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart @@ -8,12 +8,12 @@ part 'row_detail_bloc.freezed.dart'; class RowDetailBloc extends Bloc { final GridRow rowData; - final GridRowCacheService _rowCache; + final GridRowsCache _rowCache; void Function()? _rowListenFn; RowDetailBloc({ required this.rowData, - required GridRowCacheService rowCache, + required GridRowsCache rowCache, }) : _rowCache = rowCache, super(RowDetailState.initial()) { on( diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart index 78e4e6715f..27749083b4 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart @@ -1,5 +1,4 @@ import 'dart:collection'; - import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; @@ -15,34 +14,36 @@ part 'row_service.freezed.dart'; typedef RowUpdateCallback = void Function(); -abstract class GridRowCacheDelegate with GridCellCacheDelegate { +abstract class GridRowCacheDelegate { UnmodifiableListView get fields; - void onFieldsChanged(void Function() callback); + void onFieldsChanged(VoidCallback callback); + void onFieldChanged(void Function(Field) callback); void dispose(); } -class GridRowCacheService { +class GridRowsCache { final String gridId; final GridBlock block; final _Notifier _notifier; List _rows = []; final HashMap _rowByRowId; final GridRowCacheDelegate _delegate; - final GridCellCacheService _cellCache; + final GridCellsCache _cellCache; List get rows => _rows; - GridCellCacheService get cellCache => _cellCache; + GridCellsCache get cellCache => _cellCache; - GridRowCacheService({ + GridRowsCache({ required this.gridId, required this.block, required GridRowCacheDelegate delegate, - }) : _cellCache = GridCellCacheService(gridId: gridId, delegate: delegate), + }) : _cellCache = GridCellsCache(gridId: gridId), _rowByRowId = HashMap(), _notifier = _Notifier(), _delegate = delegate { // delegate.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange())); + delegate.onFieldChanged((field) => _cellCache.remove(field.id)); _rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList(); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart index 9f3c7a632e..0267ed74b4 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart @@ -1,4 +1,5 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; +import 'package:app_flowy/workspace/application/grid/grid_service.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -12,10 +13,56 @@ import 'select_option_cell/select_option_cell.dart'; import 'text_cell.dart'; import 'url_cell/url_cell.dart'; -GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCacheService cellCache, {GridCellStyle? style}) { +class GridCellBuilder { + final GridCellsCache cellCache; + final GridFieldCache fieldCache; + GridCellBuilder({ + required this.cellCache, + required this.fieldCache, + }); + + GridCellWidget build(GridCell cell, {GridCellStyle? style}) { + final key = ValueKey(gridCell.cellId()); + + final cellContextBuilder = GridCellContextBuilder( + gridCell: gridCell, + cellCache: cellCache, + fieldCache: fieldCache, + ); + + switch (gridCell.field.fieldType) { + case FieldType.Checkbox: + return CheckboxCell(cellContextBuilder: cellContextBuilder, key: key); + case FieldType.DateTime: + return DateCell(cellContextBuilder: cellContextBuilder, key: key, style: style); + case FieldType.SingleSelect: + return SingleSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key); + case FieldType.MultiSelect: + return MultiSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key); + case FieldType.Number: + return NumberCell(cellContextBuilder: cellContextBuilder, key: key); + case FieldType.RichText: + return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key); + case FieldType.URL: + return GridURLCell(cellContextBuilder: cellContextBuilder, style: style, key: key); + } + throw UnimplementedError; + } +} + +GridCellWidget buildGridCellWidget( + GridCell gridCell, { + required GridCellsCache cellCache, + required GridFieldCache fieldCache, + GridCellStyle? style, +}) { final key = ValueKey(gridCell.cellId()); - final cellContextBuilder = GridCellContextBuilder(gridCell: gridCell, cellCache: cellCache); + final cellContextBuilder = GridCellContextBuilder( + gridCell: gridCell, + cellCache: cellCache, + fieldCache: fieldCache, + ); switch (gridCell.field.fieldType) { case FieldType.Checkbox: diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index de7f117d51..0e1c6a0041 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -10,19 +10,25 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; -import 'row_action_sheet.dart'; +import 'row_action_sheet.dart'; import 'row_detail.dart'; class GridRowWidget extends StatefulWidget { final GridRow rowData; - final GridRowCacheService rowCache; + final GridRowsCache rowCache; + final GridCellBuilder cellBuilder; - const GridRowWidget({ + GridRowWidget({ required this.rowData, required this.rowCache, + required GridFieldCache fieldCache, Key? key, - }) : super(key: key); + }) : cellBuilder = GridCellBuilder( + cellCache: rowCache.cellCache, + fieldCache: fieldCache, + ), + super(key: key); @override State createState() => _GridRowWidgetState(); @@ -52,7 +58,11 @@ class _GridRowWidgetState extends State { return Row( children: [ const _RowLeading(), - Expanded(child: _RowCells(cellCache: widget.rowCache.cellCache, onExpand: () => _expandRow(context))), + Expanded( + child: _RowCells( + builder: widget.cellBuilder, + onExpand: () => _expandRow(context), + )), const _RowTrailing(), ], ); @@ -72,6 +82,7 @@ class _GridRowWidgetState extends State { final page = RowDetailPage( rowData: widget.rowData, rowCache: widget.rowCache, + cellBuilder: widget.cellBuilder, ); page.show(context); } @@ -146,9 +157,13 @@ class _DeleteRowButton extends StatelessWidget { } class _RowCells extends StatelessWidget { - final GridCellCacheService cellCache; final VoidCallback onExpand; - const _RowCells({required this.cellCache, required this.onExpand, Key? key}) : super(key: key); + final GridCellBuilder builder; + const _RowCells({ + required this.builder, + required this.onExpand, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -169,8 +184,7 @@ class _RowCells extends StatelessWidget { List _makeCells(BuildContext context, GridCellMap gridCellMap) { return gridCellMap.values.map( (gridCell) { - final GridCellWidget child = buildGridCellWidget(gridCell, cellCache); - + final GridCellWidget child = builder.build(gridCell); accessoryBuilder(GridCellAccessoryBuildContext buildContext) { final builder = child.accessoryBuilder; List accessories = []; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart index c4ccc32a1a..45dd63eb7f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart @@ -22,11 +22,13 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate { final GridRow rowData; - final GridRowCacheService rowCache; + final GridRowsCache rowCache; + final GridCellBuilder cellBuilder; const RowDetailPage({ required this.rowData, required this.rowCache, + required this.cellBuilder, Key? key, }) : super(key: key); @@ -74,7 +76,7 @@ class _RowDetailPageState extends State { children: const [Spacer(), _CloseButton()], ), ), - Expanded(child: _PropertyList(cellCache: widget.rowCache.cellCache)), + Expanded(child: _PropertyList(cellBuilder: widget.cellBuilder)), ], ), ), @@ -98,10 +100,10 @@ class _CloseButton extends StatelessWidget { } class _PropertyList extends StatelessWidget { - final GridCellCacheService cellCache; + final GridCellBuilder cellBuilder; final ScrollController _scrollController; _PropertyList({ - required this.cellCache, + required this.cellBuilder, Key? key, }) : _scrollController = ScrollController(), super(key: key); @@ -121,7 +123,7 @@ class _PropertyList extends StatelessWidget { itemBuilder: (BuildContext context, int index) { return _RowDetailCell( gridCell: state.gridCells[index], - cellCache: cellCache, + cellBuilder: cellBuilder, ); }, separatorBuilder: (BuildContext context, int index) { @@ -136,10 +138,10 @@ class _PropertyList extends StatelessWidget { class _RowDetailCell extends StatelessWidget { final GridCell gridCell; - final GridCellCacheService cellCache; + final GridCellBuilder cellBuilder; const _RowDetailCell({ required this.gridCell, - required this.cellCache, + required this.cellBuilder, Key? key, }) : super(key: key); @@ -147,7 +149,7 @@ class _RowDetailCell extends StatelessWidget { Widget build(BuildContext context) { final theme = context.watch(); final style = _customCellStyle(theme, gridCell.field.fieldType); - final cell = buildGridCellWidget(gridCell, cellCache, style: style); + final cell = cellBuilder.build(gridCell, style: style); final gesture = GestureDetector( behavior: HitTestBehavior.translucent,