Browse Source

chore: replace SliverList with SliverAnimatedList

appflowy 3 years ago
parent
commit
31b2ace48f

+ 5 - 1
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart

@@ -59,7 +59,11 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
       rowId: state.cellData.rowId,
       rowId: state.cellData.rowId,
     );
     );
     result.fold(
     result.fold(
-      (cell) => add(CheckboxCellEvent.didReceiveCellUpdate(cell)),
+      (cell) {
+        if (!isClosed) {
+          add(CheckboxCellEvent.didReceiveCellUpdate(cell));
+        }
+      },
       (err) => Log.error(err),
       (err) => Log.error(err),
     );
     );
   }
   }

+ 5 - 1
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/date_cell_bloc.dart

@@ -72,7 +72,11 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
       rowId: state.cellData.rowId,
       rowId: state.cellData.rowId,
     );
     );
     result.fold(
     result.fold(
-      (cell) => add(DateCellEvent.didReceiveCellUpdate(cell)),
+      (cell) {
+        if (!isClosed) {
+          add(DateCellEvent.didReceiveCellUpdate(cell));
+        }
+      },
       (err) => Log.error(err),
       (err) => Log.error(err),
     );
     );
   }
   }

+ 17 - 9
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/number_cell_bloc.dart

@@ -59,21 +59,29 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
     _listener.updateCellNotifier.addPublishListener((result) {
     _listener.updateCellNotifier.addPublishListener((result) {
       result.fold(
       result.fold(
         (notificationData) async {
         (notificationData) async {
-          final result = await _service.getCell(
-            gridId: state.cellData.gridId,
-            fieldId: state.cellData.field.id,
-            rowId: state.cellData.rowId,
-          );
-          result.fold(
-            (cell) => add(NumberCellEvent.didReceiveCellUpdate(cell)),
-            (err) => Log.error(err),
-          );
+          await _getCellData();
         },
         },
         (err) => Log.error(err),
         (err) => Log.error(err),
       );
       );
     });
     });
     _listener.start();
     _listener.start();
   }
   }
+
+  Future<void> _getCellData() async {
+    final result = await _service.getCell(
+      gridId: state.cellData.gridId,
+      fieldId: state.cellData.field.id,
+      rowId: state.cellData.rowId,
+    );
+    result.fold(
+      (cell) {
+        if (!isClosed) {
+          add(NumberCellEvent.didReceiveCellUpdate(cell));
+        }
+      },
+      (err) => Log.error(err),
+    );
+  }
 }
 }
 
 
 @freezed
 @freezed

+ 8 - 4
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/selection_cell_bloc.dart

@@ -51,10 +51,14 @@ class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
     );
     );
 
 
     result.fold(
     result.fold(
-      (selectOptionContext) => add(SelectionCellEvent.didReceiveOptions(
-        selectOptionContext.options,
-        selectOptionContext.selectOptions,
-      )),
+      (selectOptionContext) {
+        if (!isClosed) {
+          add(SelectionCellEvent.didReceiveOptions(
+            selectOptionContext.options,
+            selectOptionContext.selectOptions,
+          ));
+        }
+      },
       (err) => Log.error(err),
       (err) => Log.error(err),
     );
     );
   }
   }

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

@@ -9,6 +9,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 import 'field/grid_listenr.dart';
 import 'field/grid_listenr.dart';
 import 'grid_listener.dart';
 import 'grid_listener.dart';
 import 'grid_service.dart';
 import 'grid_service.dart';
+import 'row/row_service.dart';
 
 
 part 'grid_bloc.freezed.dart';
 part 'grid_bloc.freezed.dart';
 
 
@@ -32,12 +33,16 @@ class GridBloc extends Bloc<GridEvent, GridState> {
           createRow: (_CreateRow value) {
           createRow: (_CreateRow value) {
             _gridService.createRow();
             _gridService.createRow();
           },
           },
-          updateDesc: (_Desc value) {},
           didReceiveRowUpdate: (_DidReceiveRowUpdate value) {
           didReceiveRowUpdate: (_DidReceiveRowUpdate value) {
-            emit(state.copyWith(rows: value.rows));
+            emit(state.copyWith(rows: value.rows, listState: value.listState));
           },
           },
           didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
           didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
-            emit(state.copyWith(fields: value.fields));
+            final rows = state.rows.map((row) => row.copyWith(fields: value.fields)).toList();
+            emit(state.copyWith(
+              rows: rows,
+              fields: value.fields,
+              listState: const GridListState.reload(),
+            ));
           },
           },
         );
         );
       },
       },
@@ -103,7 +108,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
           emit(state.copyWith(
           emit(state.copyWith(
             grid: Some(grid),
             grid: Some(grid),
             fields: fields.items,
             fields: fields.items,
-            rows: _buildRows(grid.blockOrders),
+            rows: _buildRows(grid.blockOrders, fields.items),
             loadingState: GridLoadingState.finish(left(unit)),
             loadingState: GridLoadingState.finish(left(unit)),
           ));
           ));
         },
         },
@@ -113,49 +118,65 @@ class GridBloc extends Bloc<GridEvent, GridState> {
   }
   }
 
 
   void _deleteRows(List<RowOrder> deletedRows) {
   void _deleteRows(List<RowOrder> deletedRows) {
-    final List<RowOrder> rows = List.from(state.rows);
-    rows.retainWhere(
-      (row) => deletedRows.where((deletedRow) => deletedRow.rowId == row.rowId).isEmpty,
-    );
+    final List<RowData> rows = [];
+    final List<Tuple2<int, RowData>> deletedIndex = [];
+    final Map<String, RowOrder> deletedRowMap = {for (var rowOrder in deletedRows) rowOrder.rowId: rowOrder};
+    state.rows.asMap().forEach((index, value) {
+      if (deletedRowMap[value.rowId] == null) {
+        rows.add(value);
+      } else {
+        deletedIndex.add(Tuple2(index, value));
+      }
+    });
 
 
-    add(GridEvent.didReceiveRowUpdate(rows));
+    add(GridEvent.didReceiveRowUpdate(rows, GridListState.delete(deletedIndex)));
   }
   }
 
 
   void _insertRows(List<IndexRowOrder> createdRows) {
   void _insertRows(List<IndexRowOrder> createdRows) {
-    final List<RowOrder> rows = List.from(state.rows);
+    final List<RowData> rows = List.from(state.rows);
+    List<int> insertIndexs = [];
     for (final newRow in createdRows) {
     for (final newRow in createdRows) {
       if (newRow.hasIndex()) {
       if (newRow.hasIndex()) {
-        rows.insert(newRow.index, newRow.rowOrder);
+        insertIndexs.add(newRow.index);
+        rows.insert(newRow.index, _toRowData(newRow.rowOrder));
       } else {
       } else {
-        rows.add(newRow.rowOrder);
+        insertIndexs.add(rows.length);
+        rows.add(_toRowData(newRow.rowOrder));
       }
       }
     }
     }
-    add(GridEvent.didReceiveRowUpdate(rows));
+    add(GridEvent.didReceiveRowUpdate(rows, GridListState.insert(insertIndexs)));
   }
   }
 
 
   void _updateRows(List<RowOrder> updatedRows) {
   void _updateRows(List<RowOrder> updatedRows) {
-    final List<RowOrder> rows = List.from(state.rows);
+    final List<RowData> rows = List.from(state.rows);
+    final List<int> updatedIndexs = [];
     for (final updatedRow in updatedRows) {
     for (final updatedRow in updatedRows) {
       final index = rows.indexWhere((row) => row.rowId == updatedRow.rowId);
       final index = rows.indexWhere((row) => row.rowId == updatedRow.rowId);
       if (index != -1) {
       if (index != -1) {
         rows.removeAt(index);
         rows.removeAt(index);
-        rows.insert(index, updatedRow);
+        rows.insert(index, _toRowData(updatedRow));
+        updatedIndexs.add(index);
       }
       }
     }
     }
-    add(GridEvent.didReceiveRowUpdate(rows));
+    add(GridEvent.didReceiveRowUpdate(rows, const GridListState.reload()));
   }
   }
 
 
-  List<RowOrder> _buildRows(List<GridBlockOrder> blockOrders) {
-    return blockOrders.expand((blockOrder) => blockOrder.rowOrders).toList();
+  List<RowData> _buildRows(List<GridBlockOrder> blockOrders, List<Field> fields) {
+    return blockOrders.expand((blockOrder) => blockOrder.rowOrders).map((rowOrder) {
+      return RowData.fromBlockRow(state.gridId, rowOrder, fields);
+    }).toList();
+  }
+
+  RowData _toRowData(RowOrder rowOrder) {
+    return RowData.fromBlockRow(state.gridId, rowOrder, state.fields);
   }
   }
 }
 }
 
 
 @freezed
 @freezed
 class GridEvent with _$GridEvent {
 class GridEvent with _$GridEvent {
   const factory GridEvent.initial() = InitialGrid;
   const factory GridEvent.initial() = InitialGrid;
-  const factory GridEvent.updateDesc(String gridId, String desc) = _Desc;
   const factory GridEvent.createRow() = _CreateRow;
   const factory GridEvent.createRow() = _CreateRow;
-  const factory GridEvent.didReceiveRowUpdate(List<RowOrder> rows) = _DidReceiveRowUpdate;
+  const factory GridEvent.didReceiveRowUpdate(List<RowData> rows, GridListState listState) = _DidReceiveRowUpdate;
   const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
   const factory GridEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
 }
 }
 
 
@@ -163,18 +184,20 @@ class GridEvent with _$GridEvent {
 class GridState with _$GridState {
 class GridState with _$GridState {
   const factory GridState({
   const factory GridState({
     required String gridId,
     required String gridId,
-    required GridLoadingState loadingState,
-    required List<Field> fields,
-    required List<RowOrder> rows,
     required Option<Grid> grid,
     required Option<Grid> grid,
+    required List<Field> fields,
+    required List<RowData> rows,
+    required GridLoadingState loadingState,
+    required GridListState listState,
   }) = _GridState;
   }) = _GridState;
 
 
   factory GridState.initial(String gridId) => GridState(
   factory GridState.initial(String gridId) => GridState(
-        loadingState: const _Loading(),
         fields: [],
         fields: [],
         rows: [],
         rows: [],
         grid: none(),
         grid: none(),
         gridId: gridId,
         gridId: gridId,
+        loadingState: const _Loading(),
+        listState: const _Reload(),
       );
       );
 }
 }
 
 
@@ -183,3 +206,10 @@ class GridLoadingState with _$GridLoadingState {
   const factory GridLoadingState.loading() = _Loading;
   const factory GridLoadingState.loading() = _Loading;
   const factory GridLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
   const factory GridLoadingState.finish(Either<Unit, FlowyError> successOrFail) = _Finish;
 }
 }
+
+@freezed
+class GridListState with _$GridListState {
+  const factory GridListState.insert(List<int> indexs) = _Insert;
+  const factory GridListState.delete(List<Tuple2<int, RowData>> indexs) = _Delete;
+  const factory GridListState.reload() = _Reload;
+}

+ 59 - 56
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart

@@ -5,7 +5,9 @@ import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart';
 import 'package:flowy_infra_ui/widget/error_page.dart';
 import 'package:flowy_infra_ui/widget/error_page.dart';
+import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.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' show Field;
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'controller/grid_scroll.dart';
 import 'controller/grid_scroll.dart';
@@ -90,26 +92,21 @@ class _FlowyGridState extends State<FlowyGrid> {
           return const Center(child: CircularProgressIndicator.adaptive());
           return const Center(child: CircularProgressIndicator.adaptive());
         }
         }
 
 
-// _key.currentState.insertItem(index)
-        final child = BlocBuilder<GridBloc, GridState>(
-          builder: (context, state) {
-            return SizedBox(
-              width: GridLayout.headerWidth(state.fields),
-              child: ScrollConfiguration(
-                behavior: const ScrollBehavior().copyWith(scrollbars: false),
-                child: CustomScrollView(
-                  physics: StyledScrollPhysics(),
-                  controller: _scrollController.verticalController,
-                  slivers: [
-                    _renderToolbar(state.gridId),
-                    GridHeader(gridId: state.gridId, fields: List.from(state.fields)),
-                    _renderRows(gridId: state.gridId, context: context),
-                    const GridFooter(),
-                  ],
-                ),
-              ),
-            );
-          },
+        final child = SizedBox(
+          width: GridLayout.headerWidth(state.fields),
+          child: ScrollConfiguration(
+            behavior: const ScrollBehavior().copyWith(scrollbars: false),
+            child: CustomScrollView(
+              physics: StyledScrollPhysics(),
+              controller: _scrollController.verticalController,
+              slivers: [
+                _renderToolbar(state.gridId),
+                _renderGridHeader(state.gridId),
+                _renderRows(gridId: state.gridId, context: context),
+                const GridFooter(),
+              ],
+            ),
+          ),
         );
         );
 
 
         return _wrapScrollbar(child);
         return _wrapScrollbar(child);
@@ -130,12 +127,22 @@ class _FlowyGridState extends State<FlowyGrid> {
     );
     );
   }
   }
 
 
+  Widget _renderGridHeader(String gridId) {
+    return BlocSelector<GridBloc, GridState, List<Field>>(
+      selector: (state) => state.fields,
+      builder: (context, fields) {
+        return GridHeader(gridId: gridId, fields: List.from(fields));
+      },
+    );
+  }
+
   Widget _renderToolbar(String gridId) {
   Widget _renderToolbar(String gridId) {
-    return BlocBuilder<GridBloc, GridState>(
-      builder: (context, state) {
+    return BlocSelector<GridBloc, GridState, List<Field>>(
+      selector: (state) => state.fields,
+      builder: (context, fields) {
         final toolbarContext = GridToolbarContext(
         final toolbarContext = GridToolbarContext(
           gridId: gridId,
           gridId: gridId,
-          fields: state.fields,
+          fields: fields,
         );
         );
 
 
         return SliverToBoxAdapter(
         return SliverToBoxAdapter(
@@ -146,44 +153,40 @@ class _FlowyGridState extends State<FlowyGrid> {
   }
   }
 
 
   Widget _renderRows({required String gridId, required BuildContext context}) {
   Widget _renderRows({required String gridId, required BuildContext context}) {
-    return BlocBuilder<GridBloc, GridState>(
-      buildWhen: (previous, current) {
-        final rowChanged = previous.rows.length != current.rows.length;
-        // final fieldChanged = previous.fields.length != current.fields.length;
-        return rowChanged;
+    return BlocConsumer<GridBloc, GridState>(
+      listener: (context, state) {
+        state.listState.map(
+          insert: (value) {
+            for (final index in value.indexs) {
+              _key.currentState?.insertItem(index);
+            }
+          },
+          delete: (value) {
+            for (final index in value.indexs) {
+              _key.currentState?.removeItem(index.value1, (context, animation) => _renderRow(index.value2, animation));
+            }
+          },
+          reload: (updatedIndexs) {},
+        );
       },
       },
+      buildWhen: (previous, current) => false,
       builder: (context, state) {
       builder: (context, state) {
-        return SliverList(
-            delegate: SliverChildBuilderDelegate(
-          (context, index) {
-            final blockRow = context.read<GridBloc>().state.rows[index];
-            final fields = context.read<GridBloc>().state.fields;
-            final rowData = RowData.fromBlockRow(gridId, blockRow, fields);
-            return GridRowWidget(data: rowData, key: ValueKey(rowData.rowId));
+        return SliverAnimatedList(
+          key: _key,
+          initialItemCount: context.read<GridBloc>().state.rows.length,
+          itemBuilder: (BuildContext context, int index, Animation<double> animation) {
+            final rowData = context.read<GridBloc>().state.rows[index];
+            return _renderRow(rowData, animation);
           },
           },
-          childCount: context.read<GridBloc>().state.rows.length,
-          addRepaintBoundaries: true,
-          addAutomaticKeepAlives: true,
-        ));
-
-        // return SliverAnimatedList(
-        //   key: _key,
-        //   initialItemCount: context.read<GridBloc>().state.rows.length,
-        //   itemBuilder: (BuildContext context, int index, Animation<double> animation) {
-        //     final blockRow = context.read<GridBloc>().state.rows[index];
-        //     final fields = context.read<GridBloc>().state.fields;
-        //     final rowData = RowData.fromBlockRow(blockRow, fields);
-        //     return _renderRow(rowData, animation);
-        //   },
-        // );
+        );
       },
       },
     );
     );
   }
   }
 
 
-  // Widget _renderRow(RowData rowData, Animation<double> animation) {
-  //   return SizeTransition(
-  //     sizeFactor: animation,
-  //     child: GridRowWidget(data: rowData, key: ValueKey(rowData.rowId)),
-  //   );
-  // }
+  Widget _renderRow(RowData rowData, Animation<double> animation) {
+    return SizeTransition(
+      sizeFactor: animation,
+      child: GridRowWidget(data: rowData, key: ValueKey(rowData.rowId)),
+    );
+  }
 }
 }

+ 4 - 4
shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs

@@ -163,10 +163,10 @@ impl GridBlockMetaPad {
                     None => Ok(None),
                     None => Ok(None),
                     Some(delta) => {
                     Some(delta) => {
                         tracing::debug!("[GridBlockMeta] Composing delta {}", delta.to_delta_str());
                         tracing::debug!("[GridBlockMeta] Composing delta {}", delta.to_delta_str());
-                        tracing::debug!(
-                            "[GridBlockMeta] current delta: {}",
-                            self.delta.to_str().unwrap_or_else(|_| "".to_string())
-                        );
+                        // tracing::debug!(
+                        //     "[GridBlockMeta] current delta: {}",
+                        //     self.delta.to_str().unwrap_or_else(|_| "".to_string())
+                        // );
                         self.delta = self.delta.compose(&delta)?;
                         self.delta = self.delta.compose(&delta)?;
                         Ok(Some(GridBlockMetaChange { delta, md5: self.md5() }))
                         Ok(Some(GridBlockMetaChange { delta, md5: self.md5() }))
                     }
                     }