refactor: fetch block data

This commit is contained in:
appflowy 2022-06-24 15:23:39 +08:00
parent a08590bcba
commit 0f868f48f3
19 changed files with 245 additions and 164 deletions

View File

@ -0,0 +1,90 @@
import 'dart:async';
import 'dart:collection';
import 'dart:typed_data';
import 'package:app_flowy/core/notification_helper.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.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/dart_notification.pb.dart';
class GridBlockCache {
final String gridId;
void Function(GridBlockUpdateNotifierValue)? _onBlockChanged;
final LinkedHashMap<String, _GridBlockListener> _listeners = LinkedHashMap();
GridBlockCache({required this.gridId});
void start(void Function(GridBlockUpdateNotifierValue) onBlockChanged) {
_onBlockChanged = onBlockChanged;
for (final listener in _listeners.values) {
listener.start(onBlockChanged);
}
}
Future<void> dispose() async {
for (final listener in _listeners.values) {
await listener.stop();
}
}
void addBlockListener(String blockId) {
if (_onBlockChanged == null) {
Log.error("Should call start() first");
return;
}
if (_listeners.containsKey(blockId)) {
Log.error("Duplicate block listener");
return;
}
final listener = _GridBlockListener(blockId: blockId);
listener.start(_onBlockChanged!);
_listeners[blockId] = listener;
}
}
typedef GridBlockUpdateNotifierValue = Either<List<GridRowsChangeset>, FlowyError>;
class _GridBlockListener {
final String blockId;
PublishNotifier<GridBlockUpdateNotifierValue>? _rowsUpdateNotifier = PublishNotifier();
GridNotificationListener? _listener;
_GridBlockListener({required this.blockId});
void start(void Function(GridBlockUpdateNotifierValue) onBlockChanged) {
if (_listener != null) {
_listener?.stop();
}
_listener = GridNotificationListener(
objectId: blockId,
handler: _handler,
);
_rowsUpdateNotifier?.addPublishListener(onBlockChanged);
}
void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridNotification.DidUpdateGridRow:
result.fold(
(payload) => _rowsUpdateNotifier?.value = left([GridRowsChangeset.fromBuffer(payload)]),
(error) => _rowsUpdateNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_rowsUpdateNotifier?.dispose();
_rowsUpdateNotifier = null;
}
}

View File

@ -2,21 +2,21 @@ part of 'cell_service.dart';
typedef GridCellMap = LinkedHashMap<String, GridCell>; typedef GridCellMap = LinkedHashMap<String, GridCell>;
class GridCellCacheData { class _GridCellCacheObject {
GridCellCacheKey key; _GridCellCacheKey key;
dynamic object; dynamic object;
GridCellCacheData({ _GridCellCacheObject({
required this.key, required this.key,
required this.object, required this.object,
}); });
} }
class GridCellCacheKey { class _GridCellCacheKey {
final String fieldId; final String fieldId;
final String objectId; final String rowId;
GridCellCacheKey({ _GridCellCacheKey({
required this.fieldId, required this.fieldId,
required this.objectId, required this.rowId,
}); });
} }
@ -51,46 +51,46 @@ class GridCellCache {
}); });
} }
void addFieldListener(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) { void addFieldListener(_GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
var map = _fieldListenerByFieldId[cacheKey.fieldId]; var map = _fieldListenerByFieldId[cacheKey.fieldId];
if (map == null) { if (map == null) {
_fieldListenerByFieldId[cacheKey.fieldId] = {}; _fieldListenerByFieldId[cacheKey.fieldId] = {};
map = _fieldListenerByFieldId[cacheKey.fieldId]; map = _fieldListenerByFieldId[cacheKey.fieldId];
map![cacheKey.objectId] = [onFieldChanged]; map![cacheKey.rowId] = [onFieldChanged];
} else { } else {
var objects = map[cacheKey.objectId]; var objects = map[cacheKey.rowId];
if (objects == null) { if (objects == null) {
map[cacheKey.objectId] = [onFieldChanged]; map[cacheKey.rowId] = [onFieldChanged];
} else { } else {
objects.add(onFieldChanged); objects.add(onFieldChanged);
} }
} }
} }
void removeFieldListener(GridCellCacheKey cacheKey, VoidCallback fn) { void removeFieldListener(_GridCellCacheKey cacheKey, VoidCallback fn) {
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.objectId]; var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
final index = callbacks?.indexWhere((callback) => callback == fn); final index = callbacks?.indexWhere((callback) => callback == fn);
if (index != null && index != -1) { if (index != null && index != -1) {
callbacks?.removeAt(index); callbacks?.removeAt(index);
} }
} }
void insert<T extends GridCellCacheData>(T item) { void insert<T extends _GridCellCacheObject>(T item) {
var map = _cellDataByFieldId[item.key.fieldId]; var map = _cellDataByFieldId[item.key.fieldId];
if (map == null) { if (map == null) {
_cellDataByFieldId[item.key.fieldId] = {}; _cellDataByFieldId[item.key.fieldId] = {};
map = _cellDataByFieldId[item.key.fieldId]; map = _cellDataByFieldId[item.key.fieldId];
} }
map![item.key.objectId] = item.object; map![item.key.rowId] = item.object;
} }
T? get<T>(GridCellCacheKey key) { T? get<T>(_GridCellCacheKey key) {
final map = _cellDataByFieldId[key.fieldId]; final map = _cellDataByFieldId[key.fieldId];
if (map == null) { if (map == null) {
return null; return null;
} else { } else {
final object = map[key.objectId]; final object = map[key.rowId];
if (object is T) { if (object is T) {
return object; return object;
} else { } else {

View File

@ -19,10 +19,10 @@ import 'package:app_flowy/workspace/application/grid/cell/select_option_service.
import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'dart:convert' show utf8; import 'dart:convert' show utf8;
part 'cell_service.freezed.dart'; part 'cell_service.freezed.dart';
part 'data_loader.dart'; part 'cell_data_loader.dart';
part 'context_builder.dart'; part 'context_builder.dart';
part 'data_cache.dart'; part 'cell_data_cache.dart';
part 'data_persistence.dart'; part 'cell_data_persistence.dart';
// key: rowId // key: rowId

View File

@ -100,7 +100,7 @@ class GridCellContextBuilder {
class _GridCellContext<T, D> extends Equatable { class _GridCellContext<T, D> extends Equatable {
final GridCell gridCell; final GridCell gridCell;
final GridCellCache cellCache; final GridCellCache cellCache;
final GridCellCacheKey _cacheKey; final _GridCellCacheKey _cacheKey;
final IGridCellDataLoader<T> cellDataLoader; final IGridCellDataLoader<T> cellDataLoader;
final _GridCellDataPersistence<D> cellDataPersistence; final _GridCellDataPersistence<D> cellDataPersistence;
final FieldService _fieldService; final FieldService _fieldService;
@ -118,7 +118,7 @@ class _GridCellContext<T, D> extends Equatable {
required this.cellDataLoader, required this.cellDataLoader,
required this.cellDataPersistence, required this.cellDataPersistence,
}) : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id), }) : _fieldService = FieldService(gridId: gridCell.gridId, fieldId: gridCell.field.id),
_cacheKey = GridCellCacheKey(objectId: gridCell.rowId, fieldId: gridCell.field.id); _cacheKey = _GridCellCacheKey(rowId: gridCell.rowId, fieldId: gridCell.field.id);
_GridCellContext<T, D> clone() { _GridCellContext<T, D> clone() {
return _GridCellContext( return _GridCellContext(
@ -213,7 +213,7 @@ class _GridCellContext<T, D> extends Equatable {
_loadDataOperation = Timer(const Duration(milliseconds: 10), () { _loadDataOperation = Timer(const Duration(milliseconds: 10), () {
cellDataLoader.loadData().then((data) { cellDataLoader.loadData().then((data) {
_cellDataNotifier?.value = data; _cellDataNotifier?.value = data;
cellCache.insert(GridCellCacheData(key: _cacheKey, object: data)); cellCache.insert(_GridCellCacheObject(key: _cacheKey, object: data));
}); });
}); });
} }

View File

@ -1,11 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.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-folder-data-model/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'block/block_listener.dart';
import 'cell/cell_service/cell_service.dart'; import 'cell/cell_service/cell_service.dart';
import 'grid_service.dart'; import 'grid_service.dart';
import 'row/row_service.dart'; import 'row/row_service.dart';
@ -19,9 +21,12 @@ class GridBloc extends Bloc<GridEvent, GridState> {
late final GridRowCache rowCache; late final GridRowCache rowCache;
late final GridCellCache cellCache; late final GridCellCache cellCache;
final GridBlockCache blockCache;
GridBloc({required View view}) GridBloc({required View view})
: _gridService = GridService(gridId: view.id), : _gridService = GridService(gridId: view.id),
fieldCache = GridFieldCache(gridId: view.id), fieldCache = GridFieldCache(gridId: view.id),
blockCache = GridBlockCache(gridId: view.id),
super(GridState.initial(view.id)) { super(GridState.initial(view.id)) {
rowCache = GridRowCache( rowCache = GridRowCache(
gridId: view.id, gridId: view.id,
@ -33,6 +38,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
fieldDelegate: GridCellCacheDelegateImpl(fieldCache), fieldDelegate: GridCellCacheDelegateImpl(fieldCache),
); );
blockCache.start((result) {
result.fold(
(changesets) => rowCache.applyChangesets(changesets),
(err) => Log.error(err),
);
});
on<GridEvent>( on<GridEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
@ -60,6 +72,7 @@ class GridBloc extends Bloc<GridEvent, GridState> {
await cellCache.dispose(); await cellCache.dispose();
await rowCache.dispose(); await rowCache.dispose();
await fieldCache.dispose(); await fieldCache.dispose();
await blockCache.dispose();
return super.close(); return super.close();
} }
@ -79,7 +92,15 @@ class GridBloc extends Bloc<GridEvent, GridState> {
final result = await _gridService.loadGrid(); final result = await _gridService.loadGrid();
return Future( return Future(
() => result.fold( () => result.fold(
(grid) async => await _loadFields(grid, emit), (grid) async {
for (final block in grid.blocks) {
blockCache.addBlockListener(block.id);
}
final rowOrders = grid.blocks.expand((block) => block.rowOrders).toList();
rowCache.initialRows(rowOrders);
await _loadFields(grid, emit);
},
(err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))), (err) => emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))),
), ),
); );
@ -91,7 +112,6 @@ class GridBloc extends Bloc<GridEvent, GridState> {
() => result.fold( () => result.fold(
(fields) { (fields) {
fieldCache.fields = fields.items; fieldCache.fields = fields.items;
rowCache.resetRows(grid.blockOrders);
emit(state.copyWith( emit(state.copyWith(
grid: Some(grid), grid: Some(grid),
@ -143,7 +163,6 @@ class GridLoadingState with _$GridLoadingState {
class GridFieldEquatable extends Equatable { class GridFieldEquatable extends Equatable {
final List<Field> _fields; final List<Field> _fields;
const GridFieldEquatable(List<Field> fields) : _fields = fields; const GridFieldEquatable(List<Field> fields) : _fields = fields;
@override @override

View File

@ -1,42 +0,0 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:app_flowy/core/notification_helper.dart';
class GridRowListener {
final String gridId;
PublishNotifier<Either<List<GridRowsChangeset>, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null);
GridNotificationListener? _listener;
GridRowListener({required this.gridId});
void start() {
_listener = GridNotificationListener(
objectId: gridId,
handler: _handler,
);
}
void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridNotification.DidUpdateGridRow:
result.fold(
(payload) => rowsUpdateNotifier.value = left([GridRowsChangeset.fromBuffer(payload)]),
(error) => rowsUpdateNotifier.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
rowsUpdateNotifier.dispose();
}
}

View File

@ -10,7 +10,6 @@ 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:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/grid_listener.dart';
part 'row_service.freezed.dart'; part 'row_service.freezed.dart';
typedef RowUpdateCallback = void Function(); typedef RowUpdateCallback = void Function();
@ -24,7 +23,6 @@ abstract class GridRowFieldDelegate {
class GridRowCache { class GridRowCache {
final String gridId; final String gridId;
final RowsNotifier _rowsNotifier; final RowsNotifier _rowsNotifier;
final GridRowListener _rowsListener;
final GridRowFieldDelegate _fieldDelegate; final GridRowFieldDelegate _fieldDelegate;
List<GridRow> get clonedRows => _rowsNotifier.clonedRows; List<GridRow> get clonedRows => _rowsNotifier.clonedRows;
@ -39,32 +37,23 @@ class GridRowCache {
); );
}, },
), ),
_rowsListener = GridRowListener(gridId: gridId),
_fieldDelegate = fieldDelegate { _fieldDelegate = fieldDelegate {
// //
fieldDelegate.onFieldChanged(() => _rowsNotifier.fieldDidChange()); fieldDelegate.onFieldChanged(() => _rowsNotifier.fieldDidChange());
// listen on the row update
_rowsListener.rowsUpdateNotifier.addPublishListener((result) {
result.fold(
(changesets) {
for (final changeset in changesets) {
_rowsNotifier.deleteRows(changeset.deletedRows);
_rowsNotifier.insertRows(changeset.insertedRows);
_rowsNotifier.updateRows(changeset.updatedRows);
}
},
(err) => Log.error(err),
);
});
_rowsListener.start();
} }
Future<void> dispose() async { Future<void> dispose() async {
await _rowsListener.stop();
_rowsNotifier.dispose(); _rowsNotifier.dispose();
} }
void applyChangesets(List<GridRowsChangeset> changesets) {
for (final changeset in changesets) {
_rowsNotifier.deleteRows(changeset.deletedRows);
_rowsNotifier.insertRows(changeset.insertedRows);
_rowsNotifier.updateRows(changeset.updatedRows);
}
}
void addListener({ void addListener({
void Function(List<GridRow>, GridRowChangeReason)? onChanged, void Function(List<GridRow>, GridRowChangeReason)? onChanged,
bool Function()? listenWhen, bool Function()? listenWhen,
@ -130,9 +119,8 @@ class GridRowCache {
return _makeGridCells(rowId, data); return _makeGridCells(rowId, data);
} }
void resetRows(List<GridBlockOrder> blocks) { void initialRows(List<RowOrder> rowOrders) {
final rowOrders = blocks.expand((block) => block.rowOrders).toList(); _rowsNotifier.initialRows(rowOrders);
_rowsNotifier.reset(rowOrders);
} }
Future<void> _loadRow(String rowId) async { Future<void> _loadRow(String rowId) async {
@ -142,7 +130,11 @@ class GridRowCache {
final result = await GridEventGetRow(payload).send(); final result = await GridEventGetRow(payload).send();
result.fold( result.fold(
(rowData) => _rowsNotifier.rowData = rowData, (rowData) {
if (rowData.hasRow()) {
_rowsNotifier.rowData = rowData.row;
}
},
(err) => Log.error(err), (err) => Log.error(err),
); );
} }
@ -173,7 +165,9 @@ class RowsNotifier extends ChangeNotifier {
required this.rowBuilder, required this.rowBuilder,
}); });
void reset(List<RowOrder> rowOrders) { List<GridRow> get clonedRows => [..._rows];
void initialRows(List<RowOrder> rowOrders) {
_rowDataMap = HashMap(); _rowDataMap = HashMap();
final rows = rowOrders.map((rowOrder) => rowBuilder(rowOrder)).toList(); final rows = rowOrders.map((rowOrder) => rowBuilder(rowOrder)).toList();
_update(rows, const GridRowChangeReason.initial()); _update(rows, const GridRowChangeReason.initial());
@ -199,20 +193,20 @@ class RowsNotifier extends ChangeNotifier {
_update(newRows, GridRowChangeReason.delete(deletedIndex)); _update(newRows, GridRowChangeReason.delete(deletedIndex));
} }
void insertRows(List<IndexRowOrder> createdRows) { void insertRows(List<IndexRowOrder> insertRows) {
if (createdRows.isEmpty) { if (insertRows.isEmpty) {
return; return;
} }
InsertedIndexs insertIndexs = []; InsertedIndexs insertIndexs = [];
final List<GridRow> newRows = clonedRows; final List<GridRow> newRows = clonedRows;
for (final createdRow in createdRows) { for (final insertRow in insertRows) {
final insertIndex = InsertedIndex( final insertIndex = InsertedIndex(
index: createdRow.index, index: insertRow.index,
rowId: createdRow.rowOrder.rowId, rowId: insertRow.rowOrder.rowId,
); );
insertIndexs.add(insertIndex); insertIndexs.add(insertIndex);
newRows.insert(createdRow.index, (rowBuilder(createdRow.rowOrder))); newRows.insert(insertRow.index, (rowBuilder(insertRow.rowOrder)));
} }
_update(newRows, GridRowChangeReason.insert(insertIndexs)); _update(newRows, GridRowChangeReason.insert(insertIndexs));
} }
@ -281,8 +275,6 @@ class RowsNotifier extends ChangeNotifier {
Row? rowDataWithId(String rowId) { Row? rowDataWithId(String rowId) {
return _rowDataMap[rowId]; return _rowDataMap[rowId];
} }
List<GridRow> get clonedRows => [..._rows];
} }
class RowService { class RowService {
@ -310,7 +302,7 @@ class RowService {
return GridEventMoveItem(payload).send(); return GridEventMoveItem(payload).send();
} }
Future<Either<Row, FlowyError>> getRow() { Future<Either<OptionalRow, FlowyError>> getRow() {
final payload = RowIdentifierPayload.create() final payload = RowIdentifierPayload.create()
..gridId = gridId ..gridId = gridId
..rowId = rowId; ..rowId = rowId;

View File

@ -48,12 +48,7 @@ pub(crate) async fn get_grid_blocks_handler(
) -> DataResult<RepeatedGridBlock, FlowyError> { ) -> DataResult<RepeatedGridBlock, FlowyError> {
let params: QueryGridBlocksParams = data.into_inner().try_into()?; let params: QueryGridBlocksParams = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(&params.grid_id)?; let editor = manager.get_grid_editor(&params.grid_id)?;
let block_ids = params let repeated_grid_block = editor.get_blocks(Some(params.block_ids)).await?;
.block_orders
.into_iter()
.map(|block| block.block_id)
.collect::<Vec<String>>();
let repeated_grid_block = editor.get_blocks(Some(block_ids)).await?;
data_result(repeated_grid_block) data_result(repeated_grid_block)
} }
@ -220,13 +215,13 @@ async fn get_type_option_data(field_rev: &FieldRevision, field_type: &FieldType)
pub(crate) async fn get_row_handler( pub(crate) async fn get_row_handler(
data: Data<RowIdentifierPayload>, data: Data<RowIdentifierPayload>,
manager: AppData<Arc<GridManager>>, manager: AppData<Arc<GridManager>>,
) -> DataResult<Row, FlowyError> { ) -> DataResult<OptionalRow, FlowyError> {
let params: RowIdentifier = data.into_inner().try_into()?; let params: RowIdentifier = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(&params.grid_id)?; let editor = manager.get_grid_editor(&params.grid_id)?;
match editor.get_row(&params.row_id).await? { let row = OptionalRow {
None => Err(FlowyError::record_not_found().context("Can not find the row")), row: editor.get_row(&params.row_id).await?,
Some(row) => data_result(row), };
} data_result(row)
} }
#[tracing::instrument(level = "debug", skip(data, manager), err)] #[tracing::instrument(level = "debug", skip(data, manager), err)]

View File

@ -99,7 +99,7 @@ pub enum GridEvent {
#[event(input = "CreateRowPayload", output = "Row")] #[event(input = "CreateRowPayload", output = "Row")]
CreateRow = 50, CreateRow = 50,
#[event(input = "RowIdentifierPayload", output = "Row")] #[event(input = "RowIdentifierPayload", output = "OptionalRow")]
GetRow = 51, GetRow = 51,
#[event(input = "RowIdentifierPayload")] #[event(input = "RowIdentifierPayload")]

View File

@ -2,7 +2,7 @@ use crate::dart_notification::{send_dart_notification, GridNotification};
use crate::manager::GridUser; use crate::manager::GridUser;
use crate::services::block_revision_editor::GridBlockRevisionEditor; use crate::services::block_revision_editor::GridBlockRevisionEditor;
use crate::services::persistence::block_index::BlockIndexCache; use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::row::{group_row_orders, GridBlockSnapshot}; use crate::services::row::{block_from_row_orders, GridBlockSnapshot};
use dashmap::DashMap; use dashmap::DashMap;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use flowy_grid_data_model::entities::{ use flowy_grid_data_model::entities::{
@ -77,7 +77,7 @@ impl GridBlockManager {
index_row_order.index = row_index; index_row_order.index = row_index;
let _ = self let _ = self
.notify_did_update_block(GridRowsChangeset::insert(block_id, vec![index_row_order])) .notify_did_update_block(block_id, GridRowsChangeset::insert(block_id, vec![index_row_order]))
.await?; .await?;
Ok(row_count) Ok(row_count)
} }
@ -102,7 +102,7 @@ impl GridBlockManager {
changesets.push(GridBlockRevisionChangeset::from_row_count(&block_id, row_count)); changesets.push(GridBlockRevisionChangeset::from_row_count(&block_id, row_count));
let _ = self let _ = self
.notify_did_update_block(GridRowsChangeset::insert(&block_id, inserted_row_orders)) .notify_did_update_block(&block_id, GridRowsChangeset::insert(&block_id, inserted_row_orders))
.await?; .await?;
} }
@ -121,7 +121,9 @@ impl GridBlockManager {
if let Some(row) = row_builder(row_rev.clone()) { if let Some(row) = row_builder(row_rev.clone()) {
let row_order = UpdatedRowOrder::new(&row_rev, row); let row_order = UpdatedRowOrder::new(&row_rev, row);
let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]); let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]);
let _ = self.notify_did_update_block(block_order_changeset).await?; let _ = self
.notify_did_update_block(&editor.block_id, block_order_changeset)
.await?;
} }
} }
} }
@ -137,7 +139,7 @@ impl GridBlockManager {
Some(row_order) => { Some(row_order) => {
let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?; let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
let _ = self let _ = self
.notify_did_update_block(GridRowsChangeset::delete(&block_id, vec![row_order])) .notify_did_update_block(&block_id, GridRowsChangeset::delete(&block_id, vec![row_order]))
.await?; .await?;
} }
} }
@ -147,15 +149,15 @@ impl GridBlockManager {
pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockRevisionChangeset>> { pub(crate) async fn delete_rows(&self, row_orders: Vec<RowOrder>) -> FlowyResult<Vec<GridBlockRevisionChangeset>> {
let mut changesets = vec![]; let mut changesets = vec![];
for block_order in group_row_orders(row_orders) { for block_order in block_from_row_orders(row_orders) {
let editor = self.get_editor(&block_order.block_id).await?; let editor = self.get_editor(&block_order.id).await?;
let row_ids = block_order let row_ids = block_order
.row_orders .row_orders
.into_iter() .into_iter()
.map(|row_order| Cow::Owned(row_order.row_id)) .map(|row_order| Cow::Owned(row_order.row_id))
.collect::<Vec<Cow<String>>>(); .collect::<Vec<Cow<String>>>();
let row_count = editor.delete_rows(row_ids).await?; let row_count = editor.delete_rows(row_ids).await?;
let changeset = GridBlockRevisionChangeset::from_row_count(&block_order.block_id, row_count); let changeset = GridBlockRevisionChangeset::from_row_count(&block_order.id, row_count);
changesets.push(changeset); changesets.push(changeset);
} }
@ -181,7 +183,9 @@ impl GridBlockManager {
updated_rows: vec![], updated_rows: vec![],
}; };
let _ = self.notify_did_update_block(notified_changeset).await?; let _ = self
.notify_did_update_block(&editor.block_id, notified_changeset)
.await?;
} }
} }
@ -241,8 +245,8 @@ impl GridBlockManager {
Ok(block_cell_revs) Ok(block_cell_revs)
} }
async fn notify_did_update_block(&self, changeset: GridRowsChangeset) -> FlowyResult<()> { async fn notify_did_update_block(&self, block_id: &str, changeset: GridRowsChangeset) -> FlowyResult<()> {
send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridRow) send_dart_notification(block_id, GridNotification::DidUpdateGridRow)
.payload(changeset) .payload(changeset)
.send(); .send();
Ok(()) Ok(())

View File

@ -424,8 +424,8 @@ impl GridRevisionEditor {
let mut block_orders = vec![]; let mut block_orders = vec![];
for block_order in pad_read_guard.get_block_revs() { for block_order in pad_read_guard.get_block_revs() {
let row_orders = self.block_manager.get_row_orders(&block_order.block_id).await?; let row_orders = self.block_manager.get_row_orders(&block_order.block_id).await?;
let block_order = GridBlockOrder { let block_order = GridBlock {
block_id: block_order.block_id, id: block_order.block_id,
row_orders, row_orders,
}; };
block_orders.push(block_order); block_orders.push(block_order);
@ -434,7 +434,7 @@ impl GridRevisionEditor {
Ok(Grid { Ok(Grid {
id: self.grid_id.clone(), id: self.grid_id.clone(),
field_orders, field_orders,
block_orders, blocks: block_orders,
}) })
} }

View File

@ -1,6 +1,6 @@
use crate::services::row::decode_cell_data_from_type_option_cell_data; use crate::services::row::decode_cell_data_from_type_option_cell_data;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use flowy_grid_data_model::entities::{Cell, GridBlock, GridBlockOrder, RepeatedGridBlock, Row, RowOrder}; use flowy_grid_data_model::entities::{Cell, GridBlock, RepeatedGridBlock, Row, RowOrder};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, RowRevision}; use flowy_grid_data_model::revision::{CellRevision, FieldRevision, RowRevision};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@ -10,13 +10,13 @@ pub struct GridBlockSnapshot {
pub row_revs: Vec<Arc<RowRevision>>, pub row_revs: Vec<Arc<RowRevision>>,
} }
pub(crate) fn group_row_orders(row_orders: Vec<RowOrder>) -> Vec<GridBlockOrder> { pub(crate) fn block_from_row_orders(row_orders: Vec<RowOrder>) -> Vec<GridBlock> {
let mut map: HashMap<String, GridBlockOrder> = HashMap::new(); let mut map: HashMap<String, GridBlock> = HashMap::new();
row_orders.into_iter().for_each(|row_order| { row_orders.into_iter().for_each(|row_order| {
// Memory Optimization: escape clone block_id // Memory Optimization: escape clone block_id
let block_id = row_order.block_id.clone(); let block_id = row_order.block_id.clone();
map.entry(block_id) map.entry(block_id)
.or_insert_with(|| GridBlockOrder::new(&row_order.block_id)) .or_insert_with(|| GridBlock::new(&row_order.block_id, vec![]))
.row_orders .row_orders
.push(row_order); .push(row_order);
}); });

View File

@ -6,7 +6,7 @@ use flowy_grid_data_model::entities::{
}; };
#[tokio::test] #[tokio::test]
async fn grid_setting_create_text_filter_test() { async fn grid_filter_create_test() {
let test = GridEditorTest::new().await; let test = GridEditorTest::new().await;
let field_rev = test.text_field(); let field_rev = test.text_field();
let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned())); let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
@ -16,7 +16,7 @@ async fn grid_setting_create_text_filter_test() {
#[tokio::test] #[tokio::test]
#[should_panic] #[should_panic]
async fn grid_setting_create_text_filter_panic_test() { async fn grid_filter_invalid_condition_panic_test() {
let test = GridEditorTest::new().await; let test = GridEditorTest::new().await;
let field_rev = test.text_field(); let field_rev = test.text_field();
@ -27,7 +27,7 @@ async fn grid_setting_create_text_filter_panic_test() {
} }
#[tokio::test] #[tokio::test]
async fn grid_setting_delete_text_filter_test() { async fn grid_filter_delete_test() {
let mut test = GridEditorTest::new().await; let mut test = GridEditorTest::new().await;
let field_rev = test.text_field(); let field_rev = test.text_field();
let payload = CreateGridFilterPayload::new(field_rev, 100, Some("abc".to_owned())); let payload = CreateGridFilterPayload::new(field_rev, 100, Some("abc".to_owned()));
@ -43,4 +43,4 @@ async fn grid_setting_delete_text_filter_test() {
} }
#[tokio::test] #[tokio::test]
async fn grid_setting_sort_test() {} async fn grid_filter_get_rows_test() {}

View File

@ -1,4 +1,5 @@
use crate::grid::script::GridEditorTest; use crate::grid::script::GridEditorTest;
use flowy_grid::services::field::DateCellContentChangeset;
use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload}; use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload};
use flowy_grid_data_model::entities::FieldType; use flowy_grid_data_model::entities::FieldType;
use flowy_grid_data_model::revision::FieldRevision; use flowy_grid_data_model::revision::FieldRevision;
@ -17,13 +18,47 @@ impl<'a> GridRowTestBuilder<'a> {
Self { test, inner_builder } Self { test, inner_builder }
} }
pub fn update_text_cell(&mut self) -> Self { pub fn update_text_cell(mut self, data: String) -> Self {
let text_field = self let text_field = self.field_rev_with_type(&FieldType::DateTime);
.test self.inner_builder.add_cell(&text_field.id, data).unwrap();
self
}
pub fn update_number_cell(mut self, data: String) -> Self {
let number_field = self.field_rev_with_type(&FieldType::DateTime);
self.inner_builder.add_cell(&number_field.id, data).unwrap();
self
}
pub fn update_date_cell(mut self, value: i64) -> Self {
let value = serde_json::to_string(&DateCellContentChangeset {
date: Some(value.to_string()),
time: None,
})
.unwrap();
let date_field = self.field_rev_with_type(&FieldType::DateTime);
self.inner_builder.add_cell(&date_field.id, value).unwrap();
self
}
pub fn update_checkbox_cell(mut self, data: bool) -> Self {
let number_field = self.field_rev_with_type(&FieldType::Checkbox);
self.inner_builder.add_cell(&number_field.id, data.to_string()).unwrap();
self
}
pub fn update_url_cell(mut self, data: String) -> Self {
let number_field = self.field_rev_with_type(&FieldType::Checkbox);
self.inner_builder.add_cell(&number_field.id, data).unwrap();
self
}
pub fn field_rev_with_type(&self, field_type: &FieldType) -> &FieldRevision {
self.test
.field_revs .field_revs
.iter() .iter()
.find(|field_rev| field_rev.field_type == FieldType::RichText); .find(|field_rev| field_rev.field_type == &field_type)
// self.inner_builder .unwrap()
} }
pub fn build(self) -> CreateRowRevisionPayload { pub fn build(self) -> CreateRowRevisionPayload {

View File

@ -15,7 +15,7 @@ pub struct Grid {
pub field_orders: Vec<FieldOrder>, pub field_orders: Vec<FieldOrder>,
#[pb(index = 3)] #[pb(index = 3)]
pub block_orders: Vec<GridBlockOrder>, pub blocks: Vec<GridBlock>,
} }
#[derive(Debug, Default, Clone, ProtoBuf)] #[derive(Debug, Default, Clone, ProtoBuf)]
@ -42,6 +42,12 @@ pub struct Row {
pub height: i32, pub height: i32,
} }
#[derive(Debug, Default, ProtoBuf)]
pub struct OptionalRow {
#[pb(index = 1, one_of)]
pub row: Option<Row>,
}
#[derive(Debug, Default, ProtoBuf)] #[derive(Debug, Default, ProtoBuf)]
pub struct RepeatedRow { pub struct RepeatedRow {
#[pb(index = 1)] #[pb(index = 1)]
@ -66,24 +72,6 @@ impl std::convert::From<Vec<GridBlock>> for RepeatedGridBlock {
} }
} }
#[derive(Debug, Clone, Default, ProtoBuf)]
pub struct GridBlockOrder {
#[pb(index = 1)]
pub block_id: String,
#[pb(index = 2)]
pub row_orders: Vec<RowOrder>,
}
impl GridBlockOrder {
pub fn new(block_id: &str) -> Self {
GridBlockOrder {
block_id: block_id.to_owned(),
row_orders: vec![],
}
}
}
#[derive(Debug, Clone, Default, ProtoBuf)] #[derive(Debug, Clone, Default, ProtoBuf)]
pub struct IndexRowOrder { pub struct IndexRowOrder {
#[pb(index = 1)] #[pb(index = 1)]
@ -168,7 +156,7 @@ impl GridRowsChangeset {
} }
} }
#[derive(Debug, Default, ProtoBuf)] #[derive(Debug, Clone, Default, ProtoBuf)]
pub struct GridBlock { pub struct GridBlock {
#[pb(index = 1)] #[pb(index = 1)]
pub id: String, pub id: String,
@ -305,12 +293,12 @@ pub struct QueryGridBlocksPayload {
pub grid_id: String, pub grid_id: String,
#[pb(index = 2)] #[pb(index = 2)]
pub block_orders: Vec<GridBlockOrder>, pub block_ids: Vec<String>,
} }
pub struct QueryGridBlocksParams { pub struct QueryGridBlocksParams {
pub grid_id: String, pub grid_id: String,
pub block_orders: Vec<GridBlockOrder>, pub block_ids: Vec<String>,
} }
impl TryInto<QueryGridBlocksParams> for QueryGridBlocksPayload { impl TryInto<QueryGridBlocksParams> for QueryGridBlocksPayload {
@ -320,7 +308,7 @@ impl TryInto<QueryGridBlocksParams> for QueryGridBlocksPayload {
let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
Ok(QueryGridBlocksParams { Ok(QueryGridBlocksParams {
grid_id: grid_id.0, grid_id: grid_id.0,
block_orders: self.block_orders, block_ids: self.block_ids,
}) })
} }
} }

View File

@ -32,7 +32,7 @@ pub struct GridRevision {
pub fields: Vec<FieldRevision>, pub fields: Vec<FieldRevision>,
pub blocks: Vec<GridBlockRevision>, pub blocks: Vec<GridBlockRevision>,
#[serde(default)] #[serde(default, skip)]
pub setting: GridSettingRevision, pub setting: GridSettingRevision,
} }