Bläddra i källkod

chore: refactor row cache

appflowy 3 år sedan
förälder
incheckning
b3a99be7f8

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart

@@ -51,13 +51,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
 
   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)),
     );
   }
 

+ 13 - 20
frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart

@@ -53,23 +53,17 @@ class RowBloc extends Bloc<RowEvent, RowState> {
 
   void _handleRowUpdate(Row row, Emitter<RowState> 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<void> _handleFieldUpdate(Emitter<RowState> 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<RowEvent, RowState> {
   }
 
   Future<void> _loadRow(Emitter<RowState> 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<Field> fields) {
@@ -134,13 +129,11 @@ class RowEvent with _$RowEvent {
 class RowState with _$RowState {
   const factory RowState({
     required GridRow rowData,
-    required Future<Option<Row>> row,
     required Option<CellDataMap> cellDataMap,
   }) = _RowState;
 
   factory RowState.initial(GridRow rowData) => RowState(
         rowData: rowData,
-        row: Future(() => none()),
         cellDataMap: none(),
       );
 }

+ 111 - 76
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<GridRow> _rows = [];
-  GridRowChangeReason _changeReason = const InitialListState();
-
-  void updateRows(List<GridRow> rows, GridRowChangeReason changeReason) {
-    _rows = rows;
-    _changeReason = changeReason;
-
-    changeReason.map(
-      insert: (_) => notifyListeners(),
-      delete: (_) => notifyListeners(),
-      update: (_) => notifyListeners(),
-      initial: (_) {},
-    );
-  }
-
-  List<GridRow> get rows => _rows;
-}
-
 class GridRowCache {
   final String gridId;
   final GridRowListener _rowsListener;
-  final RowsNotifier _rowNotifier = RowsNotifier();
-  final HashMap<String, Row> _rowDataMap = HashMap();
+  late final _RowsNotifier _rowNotifier;
   UnmodifiableListView<Field> _fields = UnmodifiableListView([]);
 
+  List<GridRow> 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<GridRow> get clonedRows => [..._rowNotifier.rows];
-
   void addListener({
     void Function(List<GridRow>, 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<Option<Row>> getRowData(String rowId) async {
-    final Row? data = _rowDataMap[rowId];
+  Option<Row> 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<GridBlockOrder> blocks, UnmodifiableListView<Field> 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<RowOrder> deletedRows) {
+    _rowNotifier.deleteRows(deletedRows);
+  }
+
+  void _insertRows(List<IndexRowOrder> createdRows) {
+    _rowNotifier.insertRows(createdRows);
+  }
+
+  void _updateRows(List<RowOrder> rowOrders) {
+    _rowNotifier.updateRows(rowOrders);
+  }
+}
+
+class _RowsNotifier extends ChangeNotifier {
+  List<GridRow> _rows = [];
+  HashMap<String, Row> _rowDataMap = HashMap();
+  GridRowChangeReason _changeReason = const InitialListState();
+  final GridRow Function(RowOrder) rowBuilder;
+
+  _RowsNotifier({
+    required this.rowBuilder,
+  });
+
+  void reset(List<RowOrder> rowOrders) {
+    _rowDataMap = HashMap();
+    final rows = rowOrders.map((rowOrder) => rowBuilder(rowOrder)).toList();
+    _update(rows, const GridRowChangeReason.initial());
+  }
+
+  void deleteRows(List<RowOrder> deletedRows) {
     if (deletedRows.isEmpty) {
       return;
     }
@@ -149,7 +161,7 @@ class GridRowCache {
     final DeletedIndexs deletedIndex = [];
     final Map<String, RowOrder> 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<IndexRowOrder> createdRows) {
+  void insertRows(List<IndexRowOrder> createdRows) {
     if (createdRows.isEmpty) {
       return;
     }
 
     InsertedIndexs insertIndexs = [];
-    final List<GridRow> newRows = _rowNotifier.rows;
+    final List<GridRow> 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<RowOrder> updatedRows) {
+  void updateRows(List<RowOrder> updatedRows) {
     if (updatedRows.isEmpty) {
       return;
     }
 
     final UpdatedIndexs updatedIndexs = UpdatedIndexs();
-    final List<GridRow> newRows = _rowNotifier.rows;
+    final List<GridRow> 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<GridRow> 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<GridRow> 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<Field> fields,
     required double height,
-    required Future<Option<Row>> data,
+    Row? data,
   }) = _GridRow;
-
-  factory GridRow.fromBlockRow(String gridId, RowOrder row, List<Field> fields) {
-    return GridRow(
-      gridId: gridId,
-      fields: fields,
-      rowId: row.rowId,
-      data: Future(() => none()),
-      height: row.height.toDouble(),
-    );
-  }
 }
 
 typedef InsertedIndexs = List<InsertedIndex>;

+ 5 - 1
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<double> animation) {
+  Widget _renderRow(
+    BuildContext context,
+    GridRow rowData,
+    Animation<double> animation,
+  ) {
     final bloc = context.read<GridBloc>();
     final fieldCache = bloc.fieldCache;
     final rowCache = bloc.rowCache;

+ 2 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart

@@ -47,7 +47,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
                   () => widget.setFocus(context, false),
                 );
               },
-              child: Row(children: children),
+              child: ClipRRect(child: Row(children: children)),
             ),
           );
         },
@@ -103,7 +103,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
                   () => widget.setFocus(context, false),
                 );
               },
-              child: Row(children: children),
+              child: ClipRRect(child: Row(children: children)),
             ),
           );
         },

+ 0 - 1
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 {