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 5782371e7a..4d54081980 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/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 { 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( @@ -41,7 +34,7 @@ class GridBloc extends Bloc { 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 { @override Future 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(); + fieldCache.addListener( + onChanged: (fields) => add(GridEvent.didReceiveFieldUpdate(fields)), + listenWhen: () => !isClosed, + ); - _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))); - - rowCache.updateRows(changeset.updatedRows); - } - }, - (err) => Log.error(err), - ); - }); - _gridListener.start(); + rowCache.addListener( + onChanged: (rows, listState) => add(GridEvent.didReceiveRowUpdate(rowCache.clonedRows, listState)), + listenWhen: () => !isClosed, + ); } Future _loadGrid(Emitter emit) async { @@ -106,13 +77,13 @@ class GridBloc extends Bloc { 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 { class GridEvent with _$GridEvent { const factory GridEvent.initial() = InitialGrid; const factory GridEvent.createRow() = _CreateRow; - const factory GridEvent.didReceiveRowUpdate(List rows, GridListState listState) = _DidReceiveRowUpdate; + const factory GridEvent.didReceiveRowUpdate(List rows, GridRowChangeReason listState) = _DidReceiveRowUpdate; const factory GridEvent.didReceiveFieldUpdate(List fields) = _DidReceiveFieldUpdate; } @@ -138,7 +109,7 @@ class GridState with _$GridState { required List fields, required List rows, required GridLoadingState loadingState, - required GridListState listState, + required GridRowChangeReason listState, }) = _GridState; factory GridState.initial(String gridId) => GridState( diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart index aa4d7343b9..90007ba8c2 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart @@ -37,7 +37,6 @@ class GridHeaderBloc extends Bloc { Future _startListening() async { fieldCache.addListener( - () {}, onChanged: (fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)), listenWhen: () => !isClosed, ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart index 862cd95d74..fcff6fa15f 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart +++ b/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, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null); GridNotificationListener? _listener; - GridListener({required this.gridId}); + GridRowListener({required this.gridId}); void start() { _listener = GridNotificationListener( diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart index 441ce4c963..0d24e9974c 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart +++ b/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(); - - void applyChangeset(GridFieldChangeset changeset) { - _removeFields(changeset.deletedFields); - _insertFields(changeset.insertedFields); - _updateFields(changeset.updatedFields); + 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(); } + Future dispose() async { + await _fieldListener.stop(); + _fieldNotifier.dispose(); + } + + void applyChangeset(GridFieldChangeset changeset) {} + UnmodifiableListView get unmodifiableFields => UnmodifiableListView(_fieldNotifier.fields); List get clonedFields => [..._fieldNotifier.fields]; - set clonedFields(List fields) { + set fields(List fields) { _fieldNotifier.fields = [...fields]; } - void listenOnFieldChanged(void Function(List) onFieldChanged) { - _fieldNotifier.addListener(() => onFieldChanged(clonedFields)); - } - - void addListener(VoidCallback listener, {void Function(List)? onChanged, bool Function()? listenWhen}) { + void addListener({VoidCallback? listener, void Function(List)? onChanged, bool Function()? listenWhen}) { _fieldNotifier.addListener(() { - if (onChanged != null) { - onChanged(clonedFields); - } - if (listenWhen != null && listenWhen() == false) { return; } - listener(); + if (onChanged != null) { + onChanged(clonedFields); + } + + if (listener != null) { + listener(); + } }); } - void _removeFields(List deletedFields) { + void _deleteFields(List deletedFields) { if (deletedFields.isEmpty) { return; } @@ -127,8 +143,4 @@ class GridFieldCache { } _fieldNotifier.fields = fields; } - - void dispose() { - _fieldNotifier.dispose(); - } } 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 e27c3a7b29..4ec7ce3a17 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 @@ -92,7 +92,7 @@ class RowBloc extends Bloc { ); _fieldCache.addListener( - () => add(const RowEvent.fieldsDidUpdate()), + listener: () => add(const RowEvent.fieldsDidUpdate()), listenWhen: () => !isClosed, ); 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 25985cb924..23cecb1489 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,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 _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; + late GridRowListener _rowsListener; + final HashMap _rowDataMap = HashMap(); + UnmodifiableListView _fields = UnmodifiableListView([]); - HashMap rowDataMap = HashMap(); + final RowsNotifier _rowNotifier = RowsNotifier(); - List _rows = []; + 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(); + } - GridRowCache({required this.gridId}); + List get clonedRows => [..._rowNotifier.rows]; - List get rows => [..._rows]; + Future dispose() async { + await _rowsListener.stop(); + _rowNotifier.dispose(); + } + + void addListener({void Function(List, GridRowChangeReason)? onChanged, bool Function()? listenWhen}) { + _rowNotifier.addListener(() { + if (listenWhen != null && listenWhen() == false) { + return; + } + + if (onChanged != null) { + onChanged(clonedRows, _rowNotifier._changeReason); + } + }); + } Future> 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 blocks, UnmodifiableListView 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(); + + _rowNotifier.updateRows(newRows, const GridRowChangeReason.initial()); } - void updateFields(UnmodifiableListView fields) { - if (fields.isEmpty) { - return; - } - - _fields = fields; - _rows = _rows.map((row) => row.copyWith(fields: fields)).toList(); - } - - Option deleteRows(List deletedRows) { + void _deleteRows(List deletedRows) { if (deletedRows.isEmpty) { - return none(); + return; } final List newRows = []; final DeletedIndex deletedIndex = []; final Map 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 insertRows(List createdRows) { + void _insertRows(List createdRows) { if (createdRows.isEmpty) { - return none(); + return; } InsertedIndexs insertIndexs = []; + final List 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 updatedRows) { + void _updateRows(List updatedRows) { if (updatedRows.isEmpty) { return; } final List updatedIndexs = []; - for (final updatedRow in updatedRows) { - final index = _rows.indexWhere((row) => row.rowId == updatedRow.rowId); + final List 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>; typedef DeletedIndex = List>; @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 indexs) = _Update; + const factory GridRowChangeReason.initial() = InitialListState; } diff --git a/frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart index 634bb1e56d..bf87851ebb 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart +++ b/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 { final FieldService _service; - final GridFieldsListener _fieldListener; final GridFieldCache _fieldCache; GridPropertyBloc({required String gridId, required List fields}) : _service = FieldService(gridId: gridId), - _fieldListener = GridFieldsListener(gridId: gridId), - _fieldCache = GridFieldCache(), + _fieldCache = GridFieldCache(gridId: gridId), super(GridPropertyState.initial(gridId, fields)) { on( (event, emit) async { @@ -45,22 +42,15 @@ class GridPropertyBloc extends Bloc { @override Future 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, + ); } } 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 1acdf5db2f..1a5f64c03a 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 @@ -205,7 +205,8 @@ class _GridRowsState extends State<_GridRows> { ); } }, - initial: (updatedIndexs) {}, + initial: (_) {}, + update: (_) {}, ); }, buildWhen: (previous, current) => false, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart index 22bd59eb4d..63a397710c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart +++ b/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'; 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 6a22c88ca5..7f704991b7 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 @@ -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'; diff --git a/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs b/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs index 2cf56fd5f0..f40b93233e 100644 --- a/shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs +++ b/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(()))