瀏覽代碼

chore: refactor row cache with row listener

appflowy 3 年之前
父節點
當前提交
c82754f284

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

@@ -1,13 +1,10 @@
 import 'dart:async';
 import 'package:dartz/dartz.dart';
-import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
-import 'field/grid_listenr.dart';
-import 'grid_listener.dart';
 import 'grid_service.dart';
 import 'row/row_service.dart';
 
@@ -15,16 +12,12 @@ part 'grid_bloc.freezed.dart';
 
 class GridBloc extends Bloc<GridEvent, GridState> {
   final GridService _gridService;
-  final GridListener _gridListener;
-  final GridFieldsListener _fieldListener;
   final GridFieldCache fieldCache;
   final GridRowCache rowCache;
 
   GridBloc({required View view})
-      : _fieldListener = GridFieldsListener(gridId: view.id),
-        _gridService = GridService(gridId: view.id),
-        _gridListener = GridListener(gridId: view.id),
-        fieldCache = GridFieldCache(),
+      : _gridService = GridService(gridId: view.id),
+        fieldCache = GridFieldCache(gridId: view.id),
         rowCache = GridRowCache(gridId: view.id),
         super(GridState.initial(view.id)) {
     on<GridEvent>(
@@ -41,7 +34,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
             emit(state.copyWith(rows: value.rows, listState: value.listState));
           },
           didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
-            emit(state.copyWith(rows: rowCache.rows, fields: value.fields));
+            emit(state.copyWith(rows: rowCache.clonedRows, fields: value.fields));
           },
         );
       },
@@ -51,44 +44,22 @@ class GridBloc extends Bloc<GridEvent, GridState> {
   @override
   Future<void> close() async {
     await _gridService.closeGrid();
-    await _fieldListener.stop();
-    await _gridListener.stop();
+    await fieldCache.dispose();
+    await rowCache.dispose();
     fieldCache.dispose();
     return super.close();
   }
 
   void _startListening() {
-    _fieldListener.updateFieldsNotifier?.addPublishListener((result) {
-      result.fold(
-        (changeset) {
-          fieldCache.applyChangeset(changeset);
-          rowCache.updateFields(fieldCache.unmodifiableFields);
-          add(GridEvent.didReceiveFieldUpdate(fieldCache.clonedFields));
-        },
-        (err) => Log.error(err),
-      );
-    });
-    _fieldListener.start();
-
-    _gridListener.rowsUpdateNotifier.addPublishListener((result) {
-      result.fold(
-        (changesets) {
-          for (final changeset in changesets) {
-            rowCache
-                .deleteRows(changeset.deletedRows)
-                .foldRight(null, (listState, _) => add(GridEvent.didReceiveRowUpdate(rowCache.rows, listState)));
-
-            rowCache
-                .insertRows(changeset.insertedRows)
-                .foldRight(null, (listState, _) => add(GridEvent.didReceiveRowUpdate(rowCache.rows, listState)));
+    fieldCache.addListener(
+      onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)),
+      listenWhen: () => !isClosed,
+    );
 
-            rowCache.updateRows(changeset.updatedRows);
-          }
-        },
-        (err) => Log.error(err),
-      );
-    });
-    _gridListener.start();
+    rowCache.addListener(
+      onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)),
+      listenWhen: () => !isClosed,
+    );
   }
 
   Future<void> _loadGrid(Emitter<GridState> emit) async {
@@ -106,13 +77,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
     return Future(
       () => result.fold(
         (fields) {
-          fieldCache.clonedFields = fields.items;
+          fieldCache.fields = fields.items;
           rowCache.updateWithBlock(grid.blockOrders, fieldCache.unmodifiableFields);
 
           emit(state.copyWith(
             grid: Some(grid),
             fields: fieldCache.clonedFields,
-            rows: rowCache.rows,
+            rows: rowCache.clonedRows,
             loadingState: GridLoadingState.finish(left(unit)),
           ));
         },
@@ -126,7 +97,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
 class GridEvent with _$GridEvent {
   const factory GridEvent.initial() = InitialGrid;
   const factory GridEvent.createRow() = _CreateRow;
-  const factory GridEvent.didReceiveRowUpdate(List<GridRow> rows, GridListState listState) = _DidReceiveRowUpdate;
+  const factory GridEvent.didReceiveRowUpdate(List<GridRow> rows, GridRowChangeReason listState) = _DidReceiveRowUpdate;
   const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
 }
 
@@ -138,7 +109,7 @@ class GridState with _$GridState {
     required List<Field> fields,
     required List<GridRow> rows,
     required GridLoadingState loadingState,
-    required GridListState listState,
+    required GridRowChangeReason listState,
   }) = _GridState;
 
   factory GridState.initial(String gridId) => GridState(

+ 0 - 1
frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart

@@ -37,7 +37,6 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
 
   Future<void> _startListening() async {
     fieldCache.addListener(
-      () {},
       onChanged: (fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),
       listenWhen: () => !isClosed,
     );

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

@@ -7,12 +7,12 @@ import 'dart:async';
 import 'dart:typed_data';
 import 'package:app_flowy/core/notification_helper.dart';
 
-class GridListener {
+class GridRowListener {
   final String gridId;
   PublishNotifier<Either<List<GridRowsChangeset>, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null);
   GridNotificationListener? _listener;
 
-  GridListener({required this.gridId});
+  GridRowListener({required this.gridId});
 
   void start() {
     _listener = GridNotificationListener(

+ 32 - 20
frontend/app_flowy/lib/workspace/application/grid/grid_service.dart

@@ -1,5 +1,7 @@
+import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
@@ -50,42 +52,56 @@ class FieldsNotifier extends ChangeNotifier {
 }
 
 class GridFieldCache {
+  final String gridId;
+  late final GridFieldsListener _fieldListener;
   final FieldsNotifier _fieldNotifier = FieldsNotifier();
-  GridFieldCache();
+  GridFieldCache({required this.gridId}) {
+    _fieldListener = GridFieldsListener(gridId: gridId);
+    _fieldListener.updateFieldsNotifier?.addPublishListener((result) {
+      result.fold(
+        (changeset) {
+          _deleteFields(changeset.deletedFields);
+          _insertFields(changeset.insertedFields);
+          _updateFields(changeset.updatedFields);
+        },
+        (err) => Log.error(err),
+      );
+    });
+    _fieldListener.start();
+  }
 
-  void applyChangeset(GridFieldChangeset changeset) {
-    _removeFields(changeset.deletedFields);
-    _insertFields(changeset.insertedFields);
-    _updateFields(changeset.updatedFields);
+  Future<void> dispose() async {
+    await _fieldListener.stop();
+    _fieldNotifier.dispose();
   }
 
+  void applyChangeset(GridFieldChangeset changeset) {}
+
   UnmodifiableListView<Field> get unmodifiableFields => UnmodifiableListView(_fieldNotifier.fields);
 
   List<Field> get clonedFields => [..._fieldNotifier.fields];
 
-  set clonedFields(List<Field> fields) {
+  set fields(List<Field> fields) {
     _fieldNotifier.fields = [...fields];
   }
 
-  void listenOnFieldChanged(void Function(List<Field>) onFieldChanged) {
-    _fieldNotifier.addListener(() => onFieldChanged(clonedFields));
-  }
-
-  void addListener(VoidCallback listener, {void Function(List<Field>)? onChanged, bool Function()? listenWhen}) {
+  void addListener({VoidCallback? listener, void Function(List<Field>)? onChanged, bool Function()? listenWhen}) {
     _fieldNotifier.addListener(() {
+      if (listenWhen != null && listenWhen() == false) {
+        return;
+      }
+
       if (onChanged != null) {
         onChanged(clonedFields);
       }
 
-      if (listenWhen != null && listenWhen() == false) {
-        return;
+      if (listener != null) {
+        listener();
       }
-
-      listener();
     });
   }
 
-  void _removeFields(List<FieldOrder> deletedFields) {
+  void _deleteFields(List<FieldOrder> deletedFields) {
     if (deletedFields.isEmpty) {
       return;
     }
@@ -127,8 +143,4 @@ class GridFieldCache {
     }
     _fieldNotifier.fields = fields;
   }
-
-  void dispose() {
-    _fieldNotifier.dispose();
-  }
 }

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

@@ -92,7 +92,7 @@ class RowBloc extends Bloc<RowEvent, RowState> {
     );
 
     _fieldCache.addListener(
-      () => add(const RowEvent.fieldsDidUpdate()),
+      listener: () => add(const RowEvent.fieldsDidUpdate()),
       listenWhen: () => !isClosed,
     );
 

+ 84 - 37
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -1,11 +1,13 @@
 import 'dart:collection';
 
+import 'package:app_flowy/workspace/application/grid/grid_listener.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 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';
 
 part 'row_service.freezed.dart';
@@ -60,19 +62,71 @@ class RowService {
   }
 }
 
+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;
+  late GridRowListener _rowsListener;
+  final HashMap<String, Row> _rowDataMap = HashMap();
+
   UnmodifiableListView<Field> _fields = UnmodifiableListView([]);
-  HashMap<String, Row> rowDataMap = HashMap();
+  final RowsNotifier _rowNotifier = RowsNotifier();
+
+  GridRowCache({required this.gridId}) {
+    _rowsListener = GridRowListener(gridId: gridId);
+    _rowsListener.rowsUpdateNotifier.addPublishListener((result) {
+      result.fold(
+        (changesets) {
+          for (final changeset in changesets) {
+            _deleteRows(changeset.deletedRows);
+            _insertRows(changeset.insertedRows);
+            _updateRows(changeset.updatedRows);
+          }
+        },
+        (err) => Log.error(err),
+      );
+    });
+    _rowsListener.start();
+  }
 
-  List<GridRow> _rows = [];
+  List<GridRow> get clonedRows => [..._rowNotifier.rows];
+
+  Future<void> dispose() async {
+    await _rowsListener.stop();
+    _rowNotifier.dispose();
+  }
 
-  GridRowCache({required this.gridId});
+  void addListener({void Function(List<GridRow>, GridRowChangeReason)? onChanged, bool Function()? listenWhen}) {
+    _rowNotifier.addListener(() {
+      if (listenWhen != null && listenWhen() == false) {
+        return;
+      }
 
-  List<GridRow> get rows => [..._rows];
+      if (onChanged != null) {
+        onChanged(clonedRows, _rowNotifier._changeReason);
+      }
+    });
+  }
 
   Future<Option<Row>> getRowData(String rowId) async {
-    final Row? data = rowDataMap[rowId];
+    final Row? data = _rowDataMap[rowId];
     if (data != null) {
       return Future(() => Some(data));
     }
@@ -86,7 +140,7 @@ class GridRowCache {
       return result.fold(
         (data) {
           data.freeze();
-          rowDataMap[data.id] = data;
+          _rowDataMap[data.id] = data;
           return Some(data);
         },
         (err) {
@@ -99,73 +153,65 @@ class GridRowCache {
 
   void updateWithBlock(List<GridBlockOrder> blocks, UnmodifiableListView<Field> fields) {
     _fields = fields;
-    _rows = blocks.expand((block) => block.rowOrders).map((rowOrder) {
+    final newRows = blocks.expand((block) => block.rowOrders).map((rowOrder) {
       return GridRow.fromBlockRow(gridId, rowOrder, _fields);
     }).toList();
-  }
 
-  void updateFields(UnmodifiableListView<Field> fields) {
-    if (fields.isEmpty) {
-      return;
-    }
-
-    _fields = fields;
-    _rows = _rows.map((row) => row.copyWith(fields: fields)).toList();
+    _rowNotifier.updateRows(newRows, const GridRowChangeReason.initial());
   }
 
-  Option<GridListState> deleteRows(List<RowOrder> deletedRows) {
+  void _deleteRows(List<RowOrder> deletedRows) {
     if (deletedRows.isEmpty) {
-      return none();
+      return;
     }
 
     final List<GridRow> newRows = [];
     final DeletedIndex deletedIndex = [];
     final Map<String, RowOrder> deletedRowMap = {for (var rowOrder in deletedRows) rowOrder.rowId: rowOrder};
-    _rows.asMap().forEach((index, value) {
+
+    _rowNotifier.rows.asMap().forEach((index, value) {
       if (deletedRowMap[value.rowId] == null) {
         newRows.add(value);
       } else {
         deletedIndex.add(Tuple2(index, value));
       }
     });
-    _rows = newRows;
 
-    return Some(GridListState.delete(deletedIndex));
+    _rowNotifier.updateRows(newRows, GridRowChangeReason.delete(deletedIndex));
   }
 
-  Option<GridListState> insertRows(List<IndexRowOrder> createdRows) {
+  void _insertRows(List<IndexRowOrder> createdRows) {
     if (createdRows.isEmpty) {
-      return none();
+      return;
     }
 
     InsertedIndexs insertIndexs = [];
+    final List<GridRow> newRows = _rowNotifier.rows;
     for (final createdRow in createdRows) {
       final gridRow = GridRow.fromBlockRow(gridId, createdRow.rowOrder, _fields);
       insertIndexs.add(Tuple2(createdRow.index, gridRow.rowId));
-      _rows.insert(createdRow.index, gridRow);
+      newRows.insert(createdRow.index, gridRow);
     }
-
-    return Some(GridListState.insert(insertIndexs));
+    _rowNotifier.updateRows(newRows, GridRowChangeReason.insert(insertIndexs));
   }
 
-  void updateRows(List<RowOrder> updatedRows) {
+  void _updateRows(List<RowOrder> updatedRows) {
     if (updatedRows.isEmpty) {
       return;
     }
 
     final List<int> updatedIndexs = [];
-    for (final updatedRow in updatedRows) {
-      final index = _rows.indexWhere((row) => row.rowId == updatedRow.rowId);
+    final List<GridRow> newRows = _rowNotifier.rows;
+    for (final rowOrder in updatedRows) {
+      final index = newRows.indexWhere((row) => row.rowId == rowOrder.rowId);
       if (index != -1) {
-        _rows.removeAt(index);
-        _rows.insert(index, _toRowData(updatedRow));
+        newRows.removeAt(index);
+        newRows.insert(index, GridRow.fromBlockRow(gridId, rowOrder, _fields));
         updatedIndexs.add(index);
       }
     }
-  }
 
-  GridRow _toRowData(RowOrder rowOrder) {
-    return GridRow.fromBlockRow(gridId, rowOrder, _fields);
+    _rowNotifier.updateRows(newRows, GridRowChangeReason.update(updatedIndexs));
   }
 }
 
@@ -204,8 +250,9 @@ typedef InsertedIndexs = List<Tuple2<int, String>>;
 typedef DeletedIndex = List<Tuple2<int, GridRow>>;
 
 @freezed
-class GridListState with _$GridListState {
-  const factory GridListState.insert(InsertedIndexs items) = _Insert;
-  const factory GridListState.delete(DeletedIndex items) = _Delete;
-  const factory GridListState.initial() = InitialListState;
+class GridRowChangeReason with _$GridRowChangeReason {
+  const factory GridRowChangeReason.insert(InsertedIndexs items) = _Insert;
+  const factory GridRowChangeReason.delete(DeletedIndex items) = _Delete;
+  const factory GridRowChangeReason.update(List<int> indexs) = _Update;
+  const factory GridRowChangeReason.initial() = InitialListState;
 }

+ 6 - 16
frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart

@@ -1,5 +1,4 @@
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
-import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart';
 import 'package:app_flowy/workspace/application/grid/grid_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
@@ -11,13 +10,11 @@ part 'property_bloc.freezed.dart';
 
 class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
   final FieldService _service;
-  final GridFieldsListener _fieldListener;
   final GridFieldCache _fieldCache;
 
   GridPropertyBloc({required String gridId, required List<Field> fields})
       : _service = FieldService(gridId: gridId),
-        _fieldListener = GridFieldsListener(gridId: gridId),
-        _fieldCache = GridFieldCache(),
+        _fieldCache = GridFieldCache(gridId: gridId),
         super(GridPropertyState.initial(gridId, fields)) {
     on<GridPropertyEvent>(
       (event, emit) async {
@@ -45,22 +42,15 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
 
   @override
   Future<void> close() async {
-    await _fieldListener.stop();
-    _fieldCache.dispose();
+    await _fieldCache.dispose();
     return super.close();
   }
 
   void _startListening() {
-    _fieldListener.updateFieldsNotifier?.addPublishListener((result) {
-      result.fold(
-        (changeset) {
-          _fieldCache.applyChangeset(changeset);
-          add(GridPropertyEvent.didReceiveFieldUpdate(_fieldCache.clonedFields));
-        },
-        (err) => Log.error(err),
-      );
-    });
-    _fieldListener.start();
+    _fieldCache.addListener(
+      onChanged: (fields) => add(GridPropertyEvent.didReceiveFieldUpdate(_fieldCache.clonedFields)),
+      listenWhen: () => !isClosed,
+    );
   }
 }
 

+ 2 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart

@@ -205,7 +205,8 @@ class _GridRowsState extends State<_GridRows> {
               );
             }
           },
-          initial: (updatedIndexs) {},
+          initial: (_) {},
+          update: (_) {},
         );
       },
       buildWhen: (previous, current) => false,

+ 0 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart

@@ -5,7 +5,6 @@ import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 0 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart

@@ -1,4 +1,3 @@
-import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/prelude.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart';

+ 1 - 1
shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs

@@ -215,7 +215,7 @@ impl GridMetaPad {
             |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
                 None => Ok(None),
                 Some(index) => {
-                    debug_assert_eq!(index, from_index);
+                    // debug_assert_eq!(index, from_index);
                     let field_meta = grid_meta.fields.remove(index);
                     grid_meta.fields.insert(to_index, field_meta);
                     Ok(Some(()))