diff --git a/frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart b/frontend/app_flowy/lib/workspace/application/grid/block/block_cache.dart similarity index 84% rename from frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart rename to frontend/app_flowy/lib/workspace/application/grid/block/block_cache.dart index 2a4c82bdd4..caf61e64b3 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/block/block_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/block/block_cache.dart @@ -6,21 +6,22 @@ import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart'; import 'block_listener.dart'; +/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information class GridBlockCache { final String gridId; final GridBlock block; - late GridRowsCache _rowCache; + late GridRowCache _rowCache; late GridBlockListener _listener; List get rows => _rowCache.rows; - GridRowsCache get rowCache => _rowCache; + GridRowCache get rowCache => _rowCache; GridBlockCache({ required this.gridId, required this.block, required GridFieldCache fieldCache, }) { - _rowCache = GridRowsCache( + _rowCache = GridRowCache( gridId: gridId, block: block, notifier: GridRowCacheFieldNotifierImpl(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/cell_cache.dart similarity index 67% rename from frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cache.dart rename to frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_cache.dart index dd50e98cd5..1f14c7c54a 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/cell_cache.dart @@ -2,11 +2,9 @@ part of 'cell_service.dart'; typedef GridCellMap = LinkedHashMap; -class _GridCellCacheValue { - GridCellCacheKey key; +class GridCell { dynamic object; - _GridCellCacheValue({ - required this.key, + GridCell({ required this.object, }); } @@ -22,14 +20,16 @@ class GridCellCacheKey { }); } -/// GridCellsCache is used to cache cell data of each Grid. +/// GridCellCache is used to cache cell data of each block. /// We use GridCellCacheKey to index the cell in the cache. -class GridCellsCache { +/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid +/// for more information +class GridCellCache { final String gridId; - /// fieldId: {cacheKey: cacheData} + /// fieldId: {cacheKey: GridCell} final Map> _cellDataByFieldId = {}; - GridCellsCache({ + GridCellCache({ required this.gridId, }); @@ -37,14 +37,14 @@ class GridCellsCache { _cellDataByFieldId.remove(fieldId); } - void insert(T value) { - var map = _cellDataByFieldId[value.key.fieldId]; + void insert(GridCellCacheKey key, T value) { + var map = _cellDataByFieldId[key.fieldId]; if (map == null) { - _cellDataByFieldId[value.key.fieldId] = {}; - map = _cellDataByFieldId[value.key.fieldId]; + _cellDataByFieldId[key.fieldId] = {}; + map = _cellDataByFieldId[key.fieldId]; } - map![value.key.rowId] = value.object; + map![key.rowId] = value.object; } T? get(GridCellCacheKey key) { 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 dfbd7e197b..324c65c5f5 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 @@ -11,22 +11,18 @@ abstract class ICellDataParser { class GridCellDataLoader { final CellService service = CellService(); - final GridCellIdentifier gridCell; + final GridCellIdentifier cellId; final ICellDataParser parser; final bool reloadOnFieldChanged; GridCellDataLoader({ - required this.gridCell, + required this.cellId, required this.parser, this.reloadOnFieldChanged = false, }); Future loadData() { - final fut = service.getCell( - gridId: gridCell.gridId, - fieldId: gridCell.field.id, - rowId: gridCell.rowId, - ); + final fut = service.getCell(cellId: cellId); return fut.then( (result) => result.fold((Cell cell) { try { 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 4eebda7839..4959adf3ed 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 @@ -7,21 +7,16 @@ abstract class IGridCellDataPersistence { } class CellDataPersistence implements IGridCellDataPersistence { - final GridCellIdentifier gridCell; + final GridCellIdentifier cellId; CellDataPersistence({ - required this.gridCell, + required this.cellId, }); final CellService _cellService = CellService(); @override Future> save(String data) async { - final fut = _cellService.updateCell( - gridId: gridCell.gridId, - fieldId: gridCell.field.id, - rowId: gridCell.rowId, - data: data, - ); + final fut = _cellService.updateCell(cellId: cellId, data: data); return fut.then((result) { return result.fold( @@ -38,14 +33,14 @@ class CalendarData with _$CalendarData { } class DateCellDataPersistence implements IGridCellDataPersistence { - final GridCellIdentifier gridCell; + final GridCellIdentifier cellId; DateCellDataPersistence({ - required this.gridCell, + required this.cellId, }); @override Future> save(CalendarData data) { - var payload = DateChangesetPayload.create()..cellIdentifier = _cellIdentifier(gridCell); + var payload = DateChangesetPayload.create()..cellIdentifier = _makeCellIdPayload(cellId); final date = (data.date.millisecondsSinceEpoch ~/ 1000).toString(); payload.date = date; @@ -63,9 +58,9 @@ class DateCellDataPersistence implements IGridCellDataPersistence } } -CellIdentifierPayload _cellIdentifier(GridCellIdentifier gridCell) { +CellIdentifierPayload _makeCellIdPayload(GridCellIdentifier cellId) { return CellIdentifierPayload.create() - ..gridId = gridCell.gridId - ..fieldId = gridCell.field.id - ..rowId = gridCell.rowId; + ..gridId = cellId.gridId + ..fieldId = cellId.fieldId + ..rowId = cellId.rowId; } 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 2cf9536f2d..d9924d38bb 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 @@ -23,7 +23,7 @@ import 'cell_field_notifier.dart'; part 'cell_service.freezed.dart'; part 'cell_data_loader.dart'; part 'context_builder.dart'; -part 'cache.dart'; +part 'cell_cache.dart'; part 'cell_data_persistence.dart'; // key: rowId @@ -32,28 +32,24 @@ class CellService { CellService(); Future> updateCell({ - required String gridId, - required String fieldId, - required String rowId, + required GridCellIdentifier cellId, required String data, }) { final payload = CellChangeset.create() - ..gridId = gridId - ..fieldId = fieldId - ..rowId = rowId + ..gridId = cellId.gridId + ..fieldId = cellId.fieldId + ..rowId = cellId.rowId ..content = data; return GridEventUpdateCell(payload).send(); } Future> getCell({ - required String gridId, - required String fieldId, - required String rowId, + required GridCellIdentifier cellId, }) { final payload = CellIdentifierPayload.create() - ..gridId = gridId - ..fieldId = fieldId - ..rowId = rowId; + ..gridId = cellId.gridId + ..fieldId = cellId.fieldId + ..rowId = cellId.rowId; return GridEventGetCell(payload).send(); } } @@ -71,7 +67,11 @@ class GridCellIdentifier with _$GridCellIdentifier { // ignore: unused_element const GridCellIdentifier._(); + String get fieldId => field.id; + + FieldType get fieldType => field.fieldType; + ValueKey key() { - return ValueKey(rowId + field.id + "${field.fieldType}"); + return ValueKey(rowId + fieldId + "${field.fieldType}"); } } 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 0ec9e1753f..124835cf94 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,97 +6,99 @@ typedef GridDateCellController = IGridCellController typedef GridURLCellController = IGridCellController; class GridCellControllerBuilder { - final GridCellIdentifier _gridCell; - final GridCellsCache _cellCache; + final GridCellIdentifier _cellId; + final GridCellCache _cellCache; final GridFieldCache _fieldCache; - GridCellControllerBuilder( - {required GridCellIdentifier gridCell, required GridCellsCache cellCache, required GridFieldCache fieldCache}) - : _cellCache = cellCache, + GridCellControllerBuilder({ + required GridCellIdentifier cellId, + required GridCellCache cellCache, + required GridFieldCache fieldCache, + }) : _cellCache = cellCache, _fieldCache = fieldCache, - _gridCell = gridCell; + _cellId = cellId; IGridCellController build() { final cellFieldNotifier = GridCellFieldNotifier(notifier: _GridFieldChangedNotifierImpl(_fieldCache)); - switch (_gridCell.field.fieldType) { + switch (_cellId.fieldType) { case FieldType.Checkbox: final cellDataLoader = GridCellDataLoader( - gridCell: _gridCell, + cellId: _cellId, parser: StringCellDataParser(), ); return GridCellController( - gridCell: _gridCell, + cellId: _cellId, cellCache: _cellCache, cellDataLoader: cellDataLoader, fieldNotifier: cellFieldNotifier, - cellDataPersistence: CellDataPersistence(gridCell: _gridCell), + cellDataPersistence: CellDataPersistence(cellId: _cellId), ); case FieldType.DateTime: final cellDataLoader = GridCellDataLoader( - gridCell: _gridCell, + cellId: _cellId, parser: DateCellDataParser(), reloadOnFieldChanged: true, ); return GridDateCellController( - gridCell: _gridCell, + cellId: _cellId, cellCache: _cellCache, cellDataLoader: cellDataLoader, fieldNotifier: cellFieldNotifier, - cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell), + cellDataPersistence: DateCellDataPersistence(cellId: _cellId), ); case FieldType.Number: final cellDataLoader = GridCellDataLoader( - gridCell: _gridCell, + cellId: _cellId, parser: StringCellDataParser(), ); return GridCellController( - gridCell: _gridCell, + cellId: _cellId, cellCache: _cellCache, cellDataLoader: cellDataLoader, fieldNotifier: cellFieldNotifier, - cellDataPersistence: CellDataPersistence(gridCell: _gridCell), + cellDataPersistence: CellDataPersistence(cellId: _cellId), ); case FieldType.RichText: final cellDataLoader = GridCellDataLoader( - gridCell: _gridCell, + cellId: _cellId, parser: StringCellDataParser(), ); return GridCellController( - gridCell: _gridCell, + cellId: _cellId, cellCache: _cellCache, cellDataLoader: cellDataLoader, fieldNotifier: cellFieldNotifier, - cellDataPersistence: CellDataPersistence(gridCell: _gridCell), + cellDataPersistence: CellDataPersistence(cellId: _cellId), ); case FieldType.MultiSelect: case FieldType.SingleSelect: final cellDataLoader = GridCellDataLoader( - gridCell: _gridCell, + cellId: _cellId, parser: SelectOptionCellDataParser(), reloadOnFieldChanged: true, ); return GridSelectOptionCellController( - gridCell: _gridCell, + cellId: _cellId, cellCache: _cellCache, cellDataLoader: cellDataLoader, fieldNotifier: cellFieldNotifier, - cellDataPersistence: CellDataPersistence(gridCell: _gridCell), + cellDataPersistence: CellDataPersistence(cellId: _cellId), ); case FieldType.URL: final cellDataLoader = GridCellDataLoader( - gridCell: _gridCell, + cellId: _cellId, parser: URLCellDataParser(), ); return GridURLCellController( - gridCell: _gridCell, + cellId: _cellId, cellCache: _cellCache, cellDataLoader: cellDataLoader, fieldNotifier: cellFieldNotifier, - cellDataPersistence: CellDataPersistence(gridCell: _gridCell), + cellDataPersistence: CellDataPersistence(cellId: _cellId), ); } throw UnimplementedError; @@ -112,8 +114,8 @@ class GridCellControllerBuilder { /// // ignore: must_be_immutable class IGridCellController extends Equatable { - final GridCellIdentifier gridCell; - final GridCellsCache _cellsCache; + final GridCellIdentifier cellId; + final GridCellCache _cellsCache; final GridCellCacheKey _cacheKey; final FieldService _fieldService; final GridCellFieldNotifier _fieldNotifier; @@ -130,8 +132,8 @@ class IGridCellController extends Equatable { bool _isDispose = false; IGridCellController({ - required this.gridCell, - required GridCellsCache cellCache, + required this.cellId, + required GridCellCache cellCache, required GridCellFieldNotifier fieldNotifier, required GridCellDataLoader cellDataLoader, required IGridCellDataPersistence cellDataPersistence, @@ -139,29 +141,27 @@ class IGridCellController extends Equatable { _cellDataLoader = cellDataLoader, _cellDataPersistence = cellDataPersistence, _fieldNotifier = fieldNotifier, - _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id), - _cacheKey = GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id); + _fieldService = FieldService(gridId: cellId.gridId, fieldId: cellId.field.id), + _cacheKey = GridCellCacheKey(rowId: cellId.rowId, fieldId: cellId.field.id); IGridCellController clone() { return IGridCellController( - gridCell: gridCell, + cellId: cellId, cellDataLoader: _cellDataLoader, cellCache: _cellsCache, fieldNotifier: _fieldNotifier, cellDataPersistence: _cellDataPersistence); } - String get gridId => gridCell.gridId; + String get gridId => cellId.gridId; - String get rowId => gridCell.rowId; + String get rowId => cellId.rowId; - String get cellId => gridCell.rowId + gridCell.field.id; + String get fieldId => cellId.field.id; - String get fieldId => gridCell.field.id; + Field get field => cellId.field; - Field get field => gridCell.field; - - FieldType get fieldType => gridCell.field.fieldType; + FieldType get fieldType => cellId.field.fieldType; VoidCallback? startListening({required void Function(T?) onCellChanged, VoidCallback? onCellFieldChanged}) { if (isListening) { @@ -177,7 +177,7 @@ class IGridCellController extends Equatable { /// user input: 12 /// cell display: $12 _cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey)); - _cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id); + _cellListener = CellListener(rowId: cellId.rowId, fieldId: cellId.field.id); /// 1.Listen on user edit event and load the new cell data if needed. _cellListener.start(onCellChanged: (result) { @@ -264,7 +264,7 @@ class IGridCellController extends Equatable { _loadDataOperation = Timer(const Duration(milliseconds: 10), () { _cellDataLoader.loadData().then((data) { _cellDataNotifier?.value = data; - _cellsCache.insert(_GridCellCacheValue(key: _cacheKey, object: data)); + _cellsCache.insert(_cacheKey, GridCell(object: data)); }); }); } @@ -287,7 +287,7 @@ class IGridCellController extends Equatable { } @override - List get props => [_cellsCache.get(_cacheKey) ?? "", cellId]; + List get props => [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.field.id]; } class _GridFieldChangedNotifierImpl extends GridFieldChangedNotifier { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart index b531955ab7..2daabe1a98 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_editor_bloc.dart @@ -17,7 +17,7 @@ class SelectOptionCellEditorBloc extends Bloc( (event, emit) async { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart index 147b24d468..7b6fffa310 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart @@ -7,12 +7,12 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart'; import 'cell_service/cell_service.dart'; class SelectOptionService { - final GridCellIdentifier gridCell; - SelectOptionService({required this.gridCell}); + final GridCellIdentifier cellId; + SelectOptionService({required this.cellId}); - String get gridId => gridCell.gridId; - String get fieldId => gridCell.field.id; - String get rowId => gridCell.rowId; + String get gridId => cellId.gridId; + String get fieldId => cellId.field.id; + String get rowId => cellId.rowId; Future> create({required String name}) { return TypeOptionService(gridId: gridId, fieldId: fieldId).newOption(name: name).then( diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart index 2815d5519d..3779b4b418 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart @@ -9,7 +9,7 @@ class FieldEditorBloc extends Bloc { FieldEditorBloc({ required String gridId, required String fieldName, - required IFieldContextLoader fieldContextLoader, + required IFieldTypeOptionLoader fieldContextLoader, }) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) { on( (event, emit) async { @@ -53,7 +53,7 @@ class FieldEditorState with _$FieldEditorState { required Option fieldContext, }) = _FieldEditorState; - factory FieldEditorState.initial(String gridId, String fieldName, IFieldContextLoader loader) => FieldEditorState( + factory FieldEditorState.initial(String gridId, String fieldName, IFieldTypeOptionLoader loader) => FieldEditorState( gridId: gridId, fieldContext: none(), errorText: '', diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart index 879cf599dd..e8e09a725b 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart @@ -141,7 +141,7 @@ class GridFieldCellContext with _$GridFieldCellContext { }) = _GridFieldCellContext; } -abstract class IFieldContextLoader { +abstract class IFieldTypeOptionLoader { String get gridId; Future> load(); @@ -155,10 +155,10 @@ abstract class IFieldContextLoader { } } -class NewFieldContextLoader extends IFieldContextLoader { +class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader { @override final String gridId; - NewFieldContextLoader({ + NewFieldTypeOptionLoader({ required this.gridId, }); @@ -172,12 +172,12 @@ class NewFieldContextLoader extends IFieldContextLoader { } } -class FieldContextLoader extends IFieldContextLoader { +class FieldTypeOptionLoader extends IFieldTypeOptionLoader { @override final String gridId; final Field field; - FieldContextLoader({ + FieldTypeOptionLoader({ required this.gridId, required this.field, }); @@ -195,14 +195,14 @@ class FieldContextLoader extends IFieldContextLoader { class GridFieldContext { final String gridId; - final IFieldContextLoader _loader; + final IFieldTypeOptionLoader _loader; late FieldTypeOptionData _data; ValueNotifier? _fieldNotifier; GridFieldContext({ required this.gridId, - required IFieldContextLoader loader, + required IFieldTypeOptionLoader loader, }) : _loader = loader; Future> loadData() async { 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 3098ca1609..7f120cf3d8 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -7,7 +7,7 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'block/block_service.dart'; +import 'block/block_cache.dart'; import 'grid_service.dart'; import 'row/row_service.dart'; import 'dart:collection'; @@ -68,7 +68,7 @@ class GridBloc extends Bloc { return super.close(); } - GridRowsCache? getRowCache(String blockId, String rowId) { + GridRowCache? getRowCache(String blockId, String rowId) { final GridBlockCache? 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 0e176df919..4e742f9f42 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart @@ -61,13 +61,12 @@ typedef FieldsCallback = void Function(List); class GridFieldCache { final String gridId; - late final GridFieldsListener _fieldListener; + final GridFieldsListener _fieldListener; FieldsNotifier? _fieldNotifier = FieldsNotifier(); final Map _fieldsCallbackMap = {}; final Map _changesetCallbackMap = {}; - GridFieldCache({required this.gridId}) { - _fieldListener = GridFieldsListener(gridId: gridId); + GridFieldCache({required this.gridId}) : _fieldListener = GridFieldsListener(gridId: gridId) { _fieldListener.start(onFieldsChanged: (result) { result.fold( (changeset) { diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart index 8754917223..7d570c9412 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart @@ -15,7 +15,7 @@ class RowActionSheetBloc extends Bloc : _rowService = RowService( gridId: rowData.gridId, blockId: rowData.blockId, - rowId: rowData.rowId, + rowId: rowData.id, ), super(RowActionSheetState.initial(rowData)) { on( 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 5af7c02a5d..69d2a95059 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,19 +11,19 @@ part 'row_bloc.freezed.dart'; class RowBloc extends Bloc { final RowService _rowService; - final GridRowsCache _rowCache; + final GridRowCache _rowCache; void Function()? _rowListenFn; RowBloc({ required GridRow rowData, - required GridRowsCache rowCache, + required GridRowCache rowCache, }) : _rowService = RowService( gridId: rowData.gridId, blockId: rowData.blockId, - rowId: rowData.rowId, + rowId: rowData.id, ), _rowCache = rowCache, - super(RowState.initial(rowData, rowCache.loadGridCells(rowData.rowId))) { + super(RowState.initial(rowData, rowCache.loadGridCells(rowData.id))) { on( (event, emit) async { await event.map( @@ -58,7 +58,7 @@ class RowBloc extends Bloc { Future _startListening() async { _rowListenFn = _rowCache.addListener( - rowId: state.rowData.rowId, + rowId: state.rowData.id, onCellUpdated: (cellDatas, reason) => add(RowEvent.didReceiveCellDatas(cellDatas, reason)), listenWhen: () => !isClosed, ); 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 405c0ef523..b75caf32cf 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 GridRowsCache _rowCache; + final GridRowCache _rowCache; void Function()? _rowListenFn; RowDetailBloc({ required this.rowData, - required GridRowsCache rowCache, + required GridRowCache rowCache, }) : _rowCache = rowCache, super(RowDetailState.initial()) { on( @@ -41,14 +41,14 @@ class RowDetailBloc extends Bloc { Future _startListening() async { _rowListenFn = _rowCache.addListener( - rowId: rowData.rowId, + rowId: rowData.id, onCellUpdated: (cellDatas, reason) => add(RowDetailEvent.didReceiveCellDatas(cellDatas.values.toList())), listenWhen: () => !isClosed, ); } Future _loadCellData() async { - final cellDataMap = _rowCache.loadGridCells(rowData.rowId); + final cellDataMap = _rowCache.loadGridCells(rowData.id); if (!isClosed) { add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList())); } 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 df8a068598..7296fd51a8 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 @@ -21,35 +21,46 @@ abstract class GridRowCacheFieldNotifier { void dispose(); } -class GridRowsCache { +/// Cache the rows in memory +/// Insert / delete / update row +/// +/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information. + +class GridRowCache { final String gridId; final GridBlock block; - final _Notifier _notifier; + + /// _rows containers the current block's rows + /// Use List to reverse the order of the GridRow. List _rows = []; + + /// Use Map for faster access the raw row data. final HashMap _rowByRowId; + + final GridCellCache _cellCache; final GridRowCacheFieldNotifier _fieldNotifier; - final GridCellsCache _cellCache; + final _GridRowChangesetNotifier _rowChangeReasonNotifier; - List get rows => _rows; - GridCellsCache get cellCache => _cellCache; + UnmodifiableListView get rows => UnmodifiableListView(_rows); + GridCellCache get cellCache => _cellCache; - GridRowsCache({ + GridRowCache({ required this.gridId, required this.block, required GridRowCacheFieldNotifier notifier, - }) : _cellCache = GridCellsCache(gridId: gridId), + }) : _cellCache = GridCellCache(gridId: gridId), _rowByRowId = HashMap(), - _notifier = _Notifier(), + _rowChangeReasonNotifier = _GridRowChangesetNotifier(), _fieldNotifier = notifier { // - notifier.onFieldsChanged(() => _notifier.receive(const GridRowChangeReason.fieldDidChange())); + notifier.onFieldsChanged(() => _rowChangeReasonNotifier.receive(const GridRowChangeReason.fieldDidChange())); notifier.onFieldChanged((field) => _cellCache.remove(field.id)); _rows = block.rowInfos.map((rowInfo) => buildGridRow(rowInfo.rowId, rowInfo.height.toDouble())).toList(); } Future dispose() async { _fieldNotifier.dispose(); - _notifier.dispose(); + _rowChangeReasonNotifier.dispose(); await _cellCache.dispose(); } @@ -73,14 +84,15 @@ class GridRowsCache { final Map deletedRowByRowId = {for (var rowId in deletedRows) rowId: rowId}; _rows.asMap().forEach((index, row) { - if (deletedRowByRowId[row.rowId] == null) { + if (deletedRowByRowId[row.id] == null) { newRows.add(row); } else { + _rowByRowId.remove(row.id); deletedIndex.add(DeletedIndex(index: index, row: row)); } }); _rows = newRows; - _notifier.receive(GridRowChangeReason.delete(deletedIndex)); + _rowChangeReasonNotifier.receive(GridRowChangeReason.delete(deletedIndex)); } void _insertRows(List insertRows) { @@ -89,17 +101,16 @@ class GridRowsCache { } InsertedIndexs insertIndexs = []; - final List newRows = _rows; for (final insertRow in insertRows) { final insertIndex = InsertedIndex( index: insertRow.index, rowId: insertRow.rowId, ); insertIndexs.add(insertIndex); - newRows.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble()))); + _rows.insert(insertRow.index, (buildGridRow(insertRow.rowId, insertRow.height.toDouble()))); } - _notifier.receive(GridRowChangeReason.insert(insertIndexs)); + _rowChangeReasonNotifier.receive(GridRowChangeReason.insert(insertIndexs)); } void _updateRows(List updatedRows) { @@ -108,20 +119,19 @@ class GridRowsCache { } final UpdatedIndexs updatedIndexs = UpdatedIndexs(); - final List newRows = _rows; for (final updatedRow in updatedRows) { final rowId = updatedRow.rowId; - final index = newRows.indexWhere((row) => row.rowId == rowId); + final index = _rows.indexWhere((row) => row.id == rowId); if (index != -1) { _rowByRowId[rowId] = updatedRow.row; - newRows.removeAt(index); - newRows.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble())); + _rows.removeAt(index); + _rows.insert(index, buildGridRow(rowId, updatedRow.row.height.toDouble())); updatedIndexs[rowId] = UpdatedIndex(index: index, rowId: rowId); } } - _notifier.receive(GridRowChangeReason.update(updatedIndexs)); + _rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs)); } void _hideRows(List hideRows) {} @@ -131,8 +141,8 @@ class GridRowsCache { void onRowsChanged( void Function(GridRowChangeReason) onRowChanged, ) { - _notifier.addListener(() { - onRowChanged(_notifier._reason); + _rowChangeReasonNotifier.addListener(() { + onRowChanged(_rowChangeReasonNotifier.reason); }); } @@ -151,12 +161,12 @@ class GridRowsCache { final row = _rowByRowId[rowId]; if (row != null) { final GridCellMap cellDataMap = _makeGridCells(rowId, row); - onCellUpdated(cellDataMap, _notifier._reason); + onCellUpdated(cellDataMap, _rowChangeReasonNotifier.reason); } } } - _notifier._reason.whenOrNull( + _rowChangeReasonNotifier.reason.whenOrNull( update: (indexs) { if (indexs[rowId] != null) notifyUpdate(); }, @@ -164,12 +174,12 @@ class GridRowsCache { ); } - _notifier.addListener(listenrHandler); + _rowChangeReasonNotifier.addListener(listenrHandler); return listenrHandler; } void removeRowListener(VoidCallback callback) { - _notifier.removeListener(callback); + _rowChangeReasonNotifier.removeListener(callback); } GridCellMap loadGridCells(String rowId) { @@ -215,19 +225,19 @@ class GridRowsCache { updatedRow.freeze(); _rowByRowId[updatedRow.id] = updatedRow; - final index = _rows.indexWhere((gridRow) => gridRow.rowId == updatedRow.id); + final index = _rows.indexWhere((gridRow) => gridRow.id == updatedRow.id); if (index != -1) { // update the corresponding row in _rows if they are not the same - if (_rows[index].data != updatedRow) { - final row = _rows.removeAt(index).copyWith(data: updatedRow); + if (_rows[index].rawRow != updatedRow) { + final row = _rows.removeAt(index).copyWith(rawRow: updatedRow); _rows.insert(index, row); // Calculate the update index final UpdatedIndexs updatedIndexs = UpdatedIndexs(); - updatedIndexs[row.rowId] = UpdatedIndex(index: index, rowId: row.rowId); + updatedIndexs[row.id] = UpdatedIndex(index: index, rowId: row.id); // - _notifier.receive(GridRowChangeReason.update(updatedIndexs)); + _rowChangeReasonNotifier.receive(GridRowChangeReason.update(updatedIndexs)); } } } @@ -237,19 +247,19 @@ class GridRowsCache { gridId: gridId, blockId: block.id, fields: _fieldNotifier.fields, - rowId: rowId, + id: rowId, height: rowHeight, ); } } -class _Notifier extends ChangeNotifier { - GridRowChangeReason _reason = const InitialListState(); +class _GridRowChangesetNotifier extends ChangeNotifier { + GridRowChangeReason reason = const InitialListState(); - _Notifier(); + _GridRowChangesetNotifier(); - void receive(GridRowChangeReason reason) { - _reason = reason; + void receive(GridRowChangeReason newReason) { + reason = newReason; reason.map( insert: (_) => notifyListeners(), delete: (_) => notifyListeners(), @@ -319,10 +329,10 @@ class GridRow with _$GridRow { const factory GridRow({ required String gridId, required String blockId, - required String rowId, + required String id, required UnmodifiableListView fields, required double height, - Row? data, + Row? rawRow, }) = _GridRow; } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart index 6b980e468f..d7d2999dd1 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart @@ -227,7 +227,7 @@ class _GridRowsState extends State<_GridRows> { GridRow rowData, Animation animation, ) { - final rowCache = context.read().getRowCache(rowData.blockId, rowData.rowId); + final rowCache = context.read().getRowCache(rowData.blockId, rowData.id); final fieldCache = context.read().fieldCache; if (rowCache != null) { return SizeTransition( @@ -236,7 +236,7 @@ class _GridRowsState extends State<_GridRows> { rowData: rowData, rowCache: rowCache, fieldCache: fieldCache, - key: ValueKey(rowData.rowId), + key: ValueKey(rowData.id), ), ); } else { 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 1fd4c726be..10efcbb0de 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 @@ -14,7 +14,7 @@ import 'text_cell.dart'; import 'url_cell/url_cell.dart'; class GridCellBuilder { - final GridCellsCache cellCache; + final GridCellCache cellCache; final GridFieldCache fieldCache; GridCellBuilder({ required this.cellCache, @@ -23,12 +23,12 @@ class GridCellBuilder { GridCellWidget build(GridCellIdentifier cell, {GridCellStyle? style}) { final cellControllerBuilder = GridCellControllerBuilder( - gridCell: cell, + cellId: cell, cellCache: cellCache, fieldCache: fieldCache, ); final key = cell.key(); - switch (cell.field.fieldType) { + switch (cell.fieldType) { case FieldType.Checkbox: return CheckboxCell(cellControllerBuilder: cellControllerBuilder, key: key); case FieldType.DateTime: diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index 9237c71be0..2ace435bf3 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -65,7 +65,7 @@ class GridFieldCell extends StatelessWidget { FieldEditor( gridId: state.gridId, fieldName: field.name, - contextLoader: FieldContextLoader( + contextLoader: FieldTypeOptionLoader( gridId: state.gridId, field: field, ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart index 148727752c..d3345b5b6f 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart @@ -14,7 +14,7 @@ class FieldEditor extends StatelessWidget with FlowyOverlayDelegate { final String gridId; final String fieldName; - final IFieldContextLoader contextLoader; + final IFieldTypeOptionLoader contextLoader; const FieldEditor({ required this.gridId, required this.fieldName, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart index cc968d15cb..c63bd37d2e 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart @@ -151,7 +151,7 @@ class CreateFieldButton extends StatelessWidget { onTap: () => FieldEditor( gridId: gridId, fieldName: "", - contextLoader: NewFieldContextLoader(gridId: gridId), + contextLoader: NewFieldTypeOptionLoader(gridId: gridId), ).show(context), leftIcon: svgWidget("home/add"), ); 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 0e1c6a0041..b0c1cd2ea0 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 @@ -16,7 +16,7 @@ import 'row_detail.dart'; class GridRowWidget extends StatefulWidget { final GridRow rowData; - final GridRowsCache rowCache; + final GridRowCache rowCache; final GridCellBuilder cellBuilder; GridRowWidget({ @@ -183,12 +183,12 @@ class _RowCells extends StatelessWidget { List _makeCells(BuildContext context, GridCellMap gridCellMap) { return gridCellMap.values.map( - (gridCell) { - final GridCellWidget child = builder.build(gridCell); + (cellId) { + final GridCellWidget child = builder.build(cellId); accessoryBuilder(GridCellAccessoryBuildContext buildContext) { final builder = child.accessoryBuilder; List accessories = []; - if (gridCell.field.isPrimary) { + if (cellId.field.isPrimary) { accessories.add(PrimaryCellAccessory( onTapCallback: onExpand, isCellEditing: buildContext.isCellEditing, @@ -202,7 +202,7 @@ class _RowCells extends StatelessWidget { } return CellContainer( - width: gridCell.field.width.toDouble(), + width: cellId.field.width.toDouble(), child: child, rowStateNotifier: Provider.of(context, listen: false), accessoryBuilder: accessoryBuilder, 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 b342101bc8..b10eeecb2c 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,7 +22,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate { final GridRow rowData; - final GridRowsCache rowCache; + final GridRowCache rowCache; final GridCellBuilder cellBuilder; const RowDetailPage({ @@ -122,7 +122,7 @@ class _PropertyList extends StatelessWidget { itemCount: state.gridCells.length, itemBuilder: (BuildContext context, int index) { return _RowDetailCell( - gridCell: state.gridCells[index], + cellId: state.gridCells[index], cellBuilder: cellBuilder, ); }, @@ -137,10 +137,10 @@ class _PropertyList extends StatelessWidget { } class _RowDetailCell extends StatelessWidget { - final GridCellIdentifier gridCell; + final GridCellIdentifier cellId; final GridCellBuilder cellBuilder; const _RowDetailCell({ - required this.gridCell, + required this.cellId, required this.cellBuilder, Key? key, }) : super(key: key); @@ -148,8 +148,8 @@ class _RowDetailCell extends StatelessWidget { @override Widget build(BuildContext context) { final theme = context.watch(); - final style = _customCellStyle(theme, gridCell.field.fieldType); - final cell = cellBuilder.build(gridCell, style: style); + final style = _customCellStyle(theme, cellId.fieldType); + final cell = cellBuilder.build(cellId, style: style); final gesture = GestureDetector( behavior: HitTestBehavior.translucent, @@ -169,7 +169,7 @@ class _RowDetailCell extends StatelessWidget { children: [ SizedBox( width: 150, - child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)), + child: FieldCellButton(field: cellId.field, onTap: () => _showFieldEditor(context)), ), const HSpace(10), Expanded(child: gesture), @@ -181,11 +181,11 @@ class _RowDetailCell extends StatelessWidget { void _showFieldEditor(BuildContext context) { FieldEditor( - gridId: gridCell.gridId, - fieldName: gridCell.field.name, - contextLoader: FieldContextLoader( - gridId: gridCell.gridId, - field: gridCell.field, + gridId: cellId.gridId, + fieldName: cellId.field.name, + contextLoader: FieldTypeOptionLoader( + gridId: cellId.gridId, + field: cellId.field, ), ).show(context); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart index e19c90ecea..ac28ce9dbc 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart @@ -116,7 +116,7 @@ class _GridPropertyCell extends StatelessWidget { FieldEditor( gridId: gridId, fieldName: field.name, - contextLoader: FieldContextLoader(gridId: gridId, field: field), + contextLoader: FieldTypeOptionLoader(gridId: gridId, field: field), ).show(context, anchorDirection: AnchorDirection.bottomRight); }, );