From b3a99be7f88d3b90d5d2c75cba6f9ab40382ab2e Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 19 Apr 2022 11:41:37 +0800 Subject: [PATCH] chore: refactor row cache --- .../workspace/application/grid/grid_bloc.dart | 4 +- .../application/grid/row/row_bloc.dart | 33 ++-- .../application/grid/row/row_service.dart | 187 +++++++++++------- .../plugins/grid/src/grid_page.dart | 6 +- .../cell/selection_cell/selection_cell.dart | 4 +- .../grid/src/widgets/row/grid_row.dart | 1 - 6 files changed, 133 insertions(+), 102 deletions(-) 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 f38e874453..6e19f1f3a5 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -51,13 +51,13 @@ class GridBloc extends Bloc { void _startListening() { fieldCache.addListener( - onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)), listenWhen: () => !isClosed, + onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)), ); rowCache.addListener( - onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)), listenWhen: () => !isClosed, + onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)), ); } 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 7d853ce97e..5de5bcd4ab 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 @@ -53,23 +53,17 @@ class RowBloc extends Bloc { void _handleRowUpdate(Row row, Emitter emit) { final CellDataMap cellDataMap = _makeCellDatas(row, state.rowData.fields); - emit(state.copyWith( - row: Future(() => Some(row)), - cellDataMap: Some(cellDataMap), - )); + emit(state.copyWith(cellDataMap: Some(cellDataMap))); } Future _handleFieldUpdate(Emitter emit) async { - final optionRow = await state.row; - final CellDataMap cellDataMap = optionRow.fold( - () => CellDataMap.identity(), - (row) => _makeCellDatas(row, _fieldCache.unmodifiableFields), - ); + final data = state.rowData.data; + if (data == null) { + return; + } - emit(state.copyWith( - rowData: state.rowData.copyWith(fields: _fieldCache.unmodifiableFields), - cellDataMap: Some(cellDataMap), - )); + final CellDataMap cellDataMap = _makeCellDatas(data, state.rowData.fields); + emit(state.copyWith(cellDataMap: Some(cellDataMap))); } @override @@ -98,11 +92,12 @@ class RowBloc extends Bloc { } Future _loadRow(Emitter emit) async { - final data = await _rowCache.getRowData(state.rowData.rowId); - if (isClosed) { - return; - } - data.foldRight(null, (data, _) => add(RowEvent.didLoadRow(data))); + final data = _rowCache.loadRow(state.rowData.rowId); + data.foldRight(null, (data, _) { + if (!isClosed) { + add(RowEvent.didLoadRow(data)); + } + }); } CellDataMap _makeCellDatas(Row row, List fields) { @@ -134,13 +129,11 @@ class RowEvent with _$RowEvent { class RowState with _$RowState { const factory RowState({ required GridRow rowData, - required Future> row, required Option cellDataMap, }) = _RowState; factory RowState.initial(GridRow rowData) => RowState( rowData: rowData, - row: Future(() => none()), cellDataMap: none(), ); } 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 9cb1ac81a9..d1b77f84e9 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,4 +1,5 @@ import 'dart:collection'; + import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/log.dart'; @@ -7,37 +8,31 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; + import 'package:app_flowy/workspace/application/grid/grid_listener.dart'; part 'row_service.freezed.dart'; -class RowsNotifier extends ChangeNotifier { - List _rows = []; - GridRowChangeReason _changeReason = const InitialListState(); - - void updateRows(List rows, GridRowChangeReason changeReason) { - _rows = rows; - _changeReason = changeReason; - - changeReason.map( - insert: (_) => notifyListeners(), - delete: (_) => notifyListeners(), - update: (_) => notifyListeners(), - initial: (_) {}, - ); - } - - List get rows => _rows; -} - class GridRowCache { final String gridId; final GridRowListener _rowsListener; - final RowsNotifier _rowNotifier = RowsNotifier(); - final HashMap _rowDataMap = HashMap(); + late final _RowsNotifier _rowNotifier; UnmodifiableListView _fields = UnmodifiableListView([]); + List get clonedRows => _rowNotifier.clonedRows; + GridRowCache({required this.gridId}) : _rowsListener = GridRowListener(gridId: gridId) { + _rowNotifier = _RowsNotifier( + rowBuilder: (rowOrder) { + return GridRow( + gridId: gridId, + fields: _fields, + rowId: rowOrder.rowId, + height: rowOrder.height.toDouble(), + ); + }, + ); + _rowsListener.rowsUpdateNotifier.addPublishListener((result) { result.fold( (changesets) { @@ -58,8 +53,6 @@ class GridRowCache { _rowNotifier.dispose(); } - List get clonedRows => [..._rowNotifier.rows]; - void addListener({ void Function(List, GridRowChangeReason)? onChanged, bool Function()? listenWhen, @@ -80,7 +73,7 @@ class GridRowCache { void Function(Row)? onUpdated, bool Function()? listenWhen, }) { - f() { + listenrHandler() { if (onUpdated == null) { return; } @@ -90,57 +83,76 @@ class GridRowCache { } _rowNotifier._changeReason.whenOrNull(update: (indexs) { - final row = _rowDataMap[rowId]; + final row = _rowNotifier.rowDataWithId(rowId); if (indexs[rowId] != null && row != null) { onUpdated(row); } }); } - _rowNotifier.addListener(f); - return f; + _rowNotifier.addListener(listenrHandler); + return listenrHandler; } void removeRowListener(VoidCallback callback) { _rowNotifier.removeListener(callback); } - Future> getRowData(String rowId) async { - final Row? data = _rowDataMap[rowId]; + Option loadRow(String rowId) { + final Row? data = _rowNotifier.rowDataWithId(rowId); if (data != null) { - return Future(() => Some(data)); + return Some(data); } final payload = RowIdentifierPayload.create() ..gridId = gridId ..rowId = rowId; - final result = await GridEventGetRow(payload).send(); - return Future(() { - return result.fold( - (data) { - data.freeze(); - _rowDataMap[data.id] = data; - return Some(data); - }, - (err) { - Log.error(err); - return none(); - }, + GridEventGetRow(payload).send().then((result) { + result.fold( + (rowData) => _rowNotifier.rowData = rowData, + (err) => Log.error(err), ); }); + return none(); } void updateWithBlock(List blocks, UnmodifiableListView fields) { _fields = fields; - final newRows = blocks.expand((block) => block.rowOrders).map((rowOrder) { - return GridRow.fromBlockRow(gridId, rowOrder, _fields); - }).toList(); - - _rowNotifier.updateRows(newRows, const GridRowChangeReason.initial()); + final rowOrders = blocks.expand((block) => block.rowOrders).toList(); + _rowNotifier.reset(rowOrders); } void _deleteRows(List deletedRows) { + _rowNotifier.deleteRows(deletedRows); + } + + void _insertRows(List createdRows) { + _rowNotifier.insertRows(createdRows); + } + + void _updateRows(List rowOrders) { + _rowNotifier.updateRows(rowOrders); + } +} + +class _RowsNotifier extends ChangeNotifier { + List _rows = []; + HashMap _rowDataMap = HashMap(); + GridRowChangeReason _changeReason = const InitialListState(); + final GridRow Function(RowOrder) rowBuilder; + + _RowsNotifier({ + required this.rowBuilder, + }); + + void reset(List rowOrders) { + _rowDataMap = HashMap(); + final rows = rowOrders.map((rowOrder) => rowBuilder(rowOrder)).toList(); + _update(rows, const GridRowChangeReason.initial()); + } + + void deleteRows(List deletedRows) { if (deletedRows.isEmpty) { return; } @@ -149,7 +161,7 @@ class GridRowCache { final DeletedIndexs deletedIndex = []; final Map deletedRowMap = {for (var rowOrder in deletedRows) rowOrder.rowId: rowOrder}; - _rowNotifier.rows.asMap().forEach((index, row) { + _rows.asMap().forEach((index, row) { if (deletedRowMap[row.rowId] == null) { newRows.add(row); } else { @@ -157,48 +169,81 @@ class GridRowCache { } }); - _rowNotifier.updateRows(newRows, GridRowChangeReason.delete(deletedIndex)); + _update(newRows, GridRowChangeReason.delete(deletedIndex)); } - void _insertRows(List createdRows) { + void insertRows(List createdRows) { if (createdRows.isEmpty) { return; } InsertedIndexs insertIndexs = []; - final List newRows = _rowNotifier.rows; + final List newRows = _rows; for (final createdRow in createdRows) { - final gridRow = GridRow.fromBlockRow(gridId, createdRow.rowOrder, _fields); - insertIndexs.add( - InsertedIndex( - index: createdRow.index, - rowId: gridRow.rowId, - ), - ); - newRows.insert(createdRow.index, gridRow); + final rowOrder = createdRow.rowOrder; + final insertIndex = InsertedIndex(index: createdRow.index, rowId: rowOrder.rowId); + insertIndexs.add(insertIndex); + newRows.insert(createdRow.index, (rowBuilder(rowOrder))); } - _rowNotifier.updateRows(newRows, GridRowChangeReason.insert(insertIndexs)); + _update(newRows, GridRowChangeReason.insert(insertIndexs)); } - void _updateRows(List updatedRows) { + void updateRows(List updatedRows) { if (updatedRows.isEmpty) { return; } final UpdatedIndexs updatedIndexs = UpdatedIndexs(); - final List newRows = _rowNotifier.rows; + final List newRows = _rows; for (final rowOrder in updatedRows) { final index = newRows.indexWhere((row) => row.rowId == rowOrder.rowId); if (index != -1) { newRows.removeAt(index); - newRows.insert(index, GridRow.fromBlockRow(gridId, rowOrder, _fields)); + // Remove the cache data _rowDataMap.remove(rowOrder.rowId); + newRows.insert(index, rowBuilder(rowOrder)); updatedIndexs[rowOrder.rowId] = UpdatedIndex(index: index, rowId: rowOrder.rowId); } } - _rowNotifier.updateRows(newRows, GridRowChangeReason.update(updatedIndexs)); + _update(newRows, GridRowChangeReason.update(updatedIndexs)); } + + void _update(List rows, GridRowChangeReason changeReason) { + _rows = rows; + _changeReason = changeReason; + + changeReason.map( + insert: (_) => notifyListeners(), + delete: (_) => notifyListeners(), + update: (_) => notifyListeners(), + initial: (_) {}, + ); + } + + set rowData(Row rowData) { + rowData.freeze(); + + _rowDataMap[rowData.id] = rowData; + final index = _rows.indexWhere((row) => row.rowId == rowData.id); + if (index != -1) { + if (_rows[index].data != rowData) { + final row = _rows.removeAt(index).copyWith(data: rowData); + _rows.insert(index, row); + + final UpdatedIndexs updatedIndexs = UpdatedIndexs(); + updatedIndexs[row.rowId] = UpdatedIndex(index: index, rowId: row.rowId); + _changeReason = GridRowChangeReason.update(updatedIndexs); + notifyListeners(); + } + } + } + + Row? rowDataWithId(String rowId) { + return _rowDataMap[rowId]; + } + + List get clonedRows => [..._rows]; } class RowService { @@ -258,7 +303,7 @@ class GridCellIdentifier with _$GridCellIdentifier { required String rowId, required Field field, Cell? cell, - }) = _CellData; + }) = _GridCellIdentifier; } @freezed @@ -268,18 +313,8 @@ class GridRow with _$GridRow { required String rowId, required List fields, required double height, - required Future> data, + Row? data, }) = _GridRow; - - factory GridRow.fromBlockRow(String gridId, RowOrder row, List fields) { - return GridRow( - gridId: gridId, - fields: fields, - rowId: row.rowId, - data: Future(() => none()), - height: row.height.toDouble(), - ); - } } typedef InsertedIndexs = List; 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 a8d564c6c0..3303cde961 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 @@ -222,7 +222,11 @@ class _GridRowsState extends State<_GridRows> { ); } - Widget _renderRow(BuildContext context, GridRow rowData, Animation animation) { + Widget _renderRow( + BuildContext context, + GridRow rowData, + Animation animation, + ) { final bloc = context.read(); final fieldCache = bloc.fieldCache; final rowCache = bloc.rowCache; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart index 4e0cba65bb..15a7813397 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart @@ -47,7 +47,7 @@ class _SingleSelectCellState extends State { () => widget.setFocus(context, false), ); }, - child: Row(children: children), + child: ClipRRect(child: Row(children: children)), ), ); }, @@ -103,7 +103,7 @@ class _MultiSelectCellState extends State { () => widget.setFocus(context, false), ); }, - child: Row(children: children), + child: ClipRRect(child: Row(children: children)), ), ); }, 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 7f704991b7..316b5ec9c0 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 @@ -7,7 +7,6 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; - import 'row_action_sheet.dart'; class GridRowWidget extends StatefulWidget {