| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 | 
							- import 'dart:collection';
 
- import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 
- import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 
- import 'package:appflowy_backend/dispatch/dispatch.dart';
 
- import 'package:appflowy_backend/log.dart';
 
- import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
 
- import 'package:flutter/foundation.dart';
 
- import 'package:freezed_annotation/freezed_annotation.dart';
 
- import 'row_list.dart';
 
- part 'row_cache.freezed.dart';
 
- typedef RowUpdateCallback = void Function();
 
- abstract class RowChangesetNotifierForward {
 
-   void onRowFieldsChanged(VoidCallback callback);
 
-   void onRowFieldChanged(void Function(FieldInfo) callback);
 
- }
 
- abstract class RowCacheDelegate {
 
-   UnmodifiableListView<FieldInfo> get fields;
 
-   void onRowDispose();
 
- }
 
- /// 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 databaseId;
 
-   final List<RowPB> rows;
 
-   /// _rows containers the current block's rows
 
-   /// Use List to reverse the order of the GridRow.
 
-   final RowList _rowList = RowList();
 
-   final GridCellCache _cellCache;
 
-   final RowCacheDelegate _delegate;
 
-   final RowChangesetNotifier _rowChangeReasonNotifier;
 
-   UnmodifiableListView<RowInfo> get visibleRows {
 
-     var visibleRows = [..._rowList.rows];
 
-     return UnmodifiableListView(visibleRows);
 
-   }
 
-   GridCellCache get cellCache => _cellCache;
 
-   GridRowCache({
 
-     required this.databaseId,
 
-     required this.rows,
 
-     required RowChangesetNotifierForward notifier,
 
-     required RowCacheDelegate delegate,
 
-   })  : _cellCache = GridCellCache(databaseId: databaseId),
 
-         _rowChangeReasonNotifier = RowChangesetNotifier(),
 
-         _delegate = delegate {
 
-     //
 
-     notifier.onRowFieldsChanged(() => _rowChangeReasonNotifier
 
-         .receive(const RowsChangedReason.fieldDidChange()));
 
-     notifier.onRowFieldChanged(
 
-         (field) => _cellCache.removeCellWithFieldId(field.id));
 
-   }
 
-   void initializeRows(List<RowPB> rows) {
 
-     for (final row in rows) {
 
-       final rowInfo = buildGridRow(row);
 
-       _rowList.add(rowInfo);
 
-     }
 
-   }
 
-   Future<void> dispose() async {
 
-     _delegate.onRowDispose();
 
-     _rowChangeReasonNotifier.dispose();
 
-     await _cellCache.dispose();
 
-   }
 
-   void applyRowsChanged(ViewRowsChangesetPB changeset) {
 
-     _deleteRows(changeset.deletedRows);
 
-     _insertRows(changeset.insertedRows);
 
-     _updateRows(changeset.updatedRows);
 
-   }
 
-   void applyRowsVisibility(ViewRowsVisibilityChangesetPB changeset) {
 
-     _hideRows(changeset.invisibleRows);
 
-     _showRows(changeset.visibleRows);
 
-   }
 
-   void reorderAllRows(List<String> rowIds) {
 
-     _rowList.reorderWithRowIds(rowIds);
 
-     _rowChangeReasonNotifier.receive(const RowsChangedReason.reorderRows());
 
-   }
 
-   void reorderSingleRow(ReorderSingleRowPB reorderRow) {
 
-     final rowInfo = _rowList.get(reorderRow.rowId);
 
-     if (rowInfo != null) {
 
-       _rowList.moveRow(
 
-           reorderRow.rowId, reorderRow.oldIndex, reorderRow.newIndex);
 
-       _rowChangeReasonNotifier.receive(RowsChangedReason.reorderSingleRow(
 
-         reorderRow,
 
-         rowInfo,
 
-       ));
 
-     }
 
-   }
 
-   void _deleteRows(List<String> deletedRowIds) {
 
-     for (final rowId in deletedRowIds) {
 
-       final deletedRow = _rowList.remove(rowId);
 
-       if (deletedRow != null) {
 
-         _rowChangeReasonNotifier.receive(RowsChangedReason.delete(deletedRow));
 
-       }
 
-     }
 
-   }
 
-   void _insertRows(List<InsertedRowPB> insertRows) {
 
-     for (final insertedRow in insertRows) {
 
-       final insertedIndex =
 
-           _rowList.insert(insertedRow.index, buildGridRow(insertedRow.row));
 
-       if (insertedIndex != null) {
 
-         _rowChangeReasonNotifier
 
-             .receive(RowsChangedReason.insert(insertedIndex));
 
-       }
 
-     }
 
-   }
 
-   void _updateRows(List<UpdatedRowPB> updatedRows) {
 
-     if (updatedRows.isEmpty) return;
 
-     List<RowPB> rowPBs = [];
 
-     for (final updatedRow in updatedRows) {
 
-       for (final fieldId in updatedRow.fieldIds) {
 
-         final key = GridCellCacheKey(
 
-           fieldId: fieldId,
 
-           rowId: updatedRow.row.id,
 
-         );
 
-         _cellCache.remove(key);
 
-       }
 
-       rowPBs.add(updatedRow.row);
 
-     }
 
-     final updatedIndexs =
 
-         _rowList.updateRows(rowPBs, (rowPB) => buildGridRow(rowPB));
 
-     if (updatedIndexs.isNotEmpty) {
 
-       _rowChangeReasonNotifier.receive(RowsChangedReason.update(updatedIndexs));
 
-     }
 
-   }
 
-   void _hideRows(List<String> invisibleRows) {
 
-     for (final rowId in invisibleRows) {
 
-       final deletedRow = _rowList.remove(rowId);
 
-       if (deletedRow != null) {
 
-         _rowChangeReasonNotifier.receive(RowsChangedReason.delete(deletedRow));
 
-       }
 
-     }
 
-   }
 
-   void _showRows(List<InsertedRowPB> visibleRows) {
 
-     for (final insertedRow in visibleRows) {
 
-       final insertedIndex =
 
-           _rowList.insert(insertedRow.index, buildGridRow(insertedRow.row));
 
-       if (insertedIndex != null) {
 
-         _rowChangeReasonNotifier
 
-             .receive(RowsChangedReason.insert(insertedIndex));
 
-       }
 
-     }
 
-   }
 
-   void onRowsChanged(void Function(RowsChangedReason) onRowChanged) {
 
-     _rowChangeReasonNotifier.addListener(() {
 
-       onRowChanged(_rowChangeReasonNotifier.reason);
 
-     });
 
-   }
 
-   RowUpdateCallback addListener({
 
-     required String rowId,
 
-     void Function(GridCellMap, RowsChangedReason)? onCellUpdated,
 
-     bool Function()? listenWhen,
 
-   }) {
 
-     listenerHandler() async {
 
-       if (listenWhen != null && listenWhen() == false) {
 
-         return;
 
-       }
 
-       notifyUpdate() {
 
-         if (onCellUpdated != null) {
 
-           final rowInfo = _rowList.get(rowId);
 
-           if (rowInfo != null) {
 
-             final GridCellMap cellDataMap =
 
-                 _makeGridCells(rowId, rowInfo.rowPB);
 
-             onCellUpdated(cellDataMap, _rowChangeReasonNotifier.reason);
 
-           }
 
-         }
 
-       }
 
-       _rowChangeReasonNotifier.reason.whenOrNull(
 
-         update: (indexs) {
 
-           if (indexs[rowId] != null) notifyUpdate();
 
-         },
 
-         fieldDidChange: () => notifyUpdate(),
 
-       );
 
-     }
 
-     _rowChangeReasonNotifier.addListener(listenerHandler);
 
-     return listenerHandler;
 
-   }
 
-   void removeRowListener(VoidCallback callback) {
 
-     _rowChangeReasonNotifier.removeListener(callback);
 
-   }
 
-   GridCellMap loadGridCells(String rowId) {
 
-     final RowPB? data = _rowList.get(rowId)?.rowPB;
 
-     if (data == null) {
 
-       _loadRow(rowId);
 
-     }
 
-     return _makeGridCells(rowId, data);
 
-   }
 
-   Future<void> _loadRow(String rowId) async {
 
-     final payload = RowIdPB.create()
 
-       ..databaseId = databaseId
 
-       ..rowId = rowId;
 
-     final result = await DatabaseEventGetRow(payload).send();
 
-     result.fold(
 
-       (optionRow) => _refreshRow(optionRow),
 
-       (err) => Log.error(err),
 
-     );
 
-   }
 
-   GridCellMap _makeGridCells(String rowId, RowPB? row) {
 
-     // ignore: prefer_collection_literals
 
-     var cellDataMap = GridCellMap();
 
-     for (final field in _delegate.fields) {
 
-       if (field.visibility) {
 
-         cellDataMap[field.id] = GridCellIdentifier(
 
-           rowId: rowId,
 
-           databaseId: databaseId,
 
-           fieldInfo: field,
 
-         );
 
-       }
 
-     }
 
-     return cellDataMap;
 
-   }
 
-   void _refreshRow(OptionalRowPB optionRow) {
 
-     if (!optionRow.hasRow()) {
 
-       return;
 
-     }
 
-     final updatedRow = optionRow.row;
 
-     updatedRow.freeze();
 
-     final rowInfo = _rowList.get(updatedRow.id);
 
-     final rowIndex = _rowList.indexOfRow(updatedRow.id);
 
-     if (rowInfo != null && rowIndex != null) {
 
-       final updatedRowInfo = rowInfo.copyWith(rowPB: updatedRow);
 
-       _rowList.remove(updatedRow.id);
 
-       _rowList.insert(rowIndex, updatedRowInfo);
 
-       final UpdatedIndexMap updatedIndexs = UpdatedIndexMap();
 
-       updatedIndexs[rowInfo.rowPB.id] = UpdatedIndex(
 
-         index: rowIndex,
 
-         rowId: updatedRowInfo.rowPB.id,
 
-       );
 
-       _rowChangeReasonNotifier.receive(RowsChangedReason.update(updatedIndexs));
 
-     }
 
-   }
 
-   RowInfo buildGridRow(RowPB rowPB) {
 
-     return RowInfo(
 
-       databaseId: databaseId,
 
-       fields: _delegate.fields,
 
-       rowPB: rowPB,
 
-     );
 
-   }
 
- }
 
- class RowChangesetNotifier extends ChangeNotifier {
 
-   RowsChangedReason reason = const InitialListState();
 
-   RowChangesetNotifier();
 
-   void receive(RowsChangedReason newReason) {
 
-     reason = newReason;
 
-     reason.map(
 
-       insert: (_) => notifyListeners(),
 
-       delete: (_) => notifyListeners(),
 
-       update: (_) => notifyListeners(),
 
-       fieldDidChange: (_) => notifyListeners(),
 
-       initial: (_) {},
 
-       reorderRows: (_) => notifyListeners(),
 
-       reorderSingleRow: (_) => notifyListeners(),
 
-     );
 
-   }
 
- }
 
- @unfreezed
 
- class RowInfo with _$RowInfo {
 
-   factory RowInfo({
 
-     required String databaseId,
 
-     required UnmodifiableListView<FieldInfo> fields,
 
-     required RowPB rowPB,
 
-   }) = _RowInfo;
 
- }
 
- typedef InsertedIndexs = List<InsertedIndex>;
 
- typedef DeletedIndexs = List<DeletedIndex>;
 
- // key: id of the row
 
- // value: UpdatedIndex
 
- typedef UpdatedIndexMap = LinkedHashMap<String, UpdatedIndex>;
 
- @freezed
 
- class RowsChangedReason with _$RowsChangedReason {
 
-   const factory RowsChangedReason.insert(InsertedIndex item) = _Insert;
 
-   const factory RowsChangedReason.delete(DeletedIndex item) = _Delete;
 
-   const factory RowsChangedReason.update(UpdatedIndexMap indexs) = _Update;
 
-   const factory RowsChangedReason.fieldDidChange() = _FieldDidChange;
 
-   const factory RowsChangedReason.initial() = InitialListState;
 
-   const factory RowsChangedReason.reorderRows() = _ReorderRows;
 
-   const factory RowsChangedReason.reorderSingleRow(
 
-       ReorderSingleRowPB reorderRow, RowInfo rowInfo) = _ReorderSingleRow;
 
- }
 
- class InsertedIndex {
 
-   final int index;
 
-   final String rowId;
 
-   InsertedIndex({
 
-     required this.index,
 
-     required this.rowId,
 
-   });
 
- }
 
- class DeletedIndex {
 
-   final int index;
 
-   final RowInfo rowInfo;
 
-   DeletedIndex({
 
-     required this.index,
 
-     required this.rowInfo,
 
-   });
 
- }
 
- class UpdatedIndex {
 
-   final int index;
 
-   final String rowId;
 
-   UpdatedIndex({
 
-     required this.index,
 
-     required this.rowId,
 
-   });
 
- }
 
 
  |