diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 8f239f1ece..5896d559a3 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -168,34 +168,34 @@ void _resolveGridDeps(GetIt getIt) { ), ); - getIt.registerFactoryParam( - (cellData, _) => TextCellBloc( - cellData: cellData, + getIt.registerFactoryParam( + (context, _) => TextCellBloc( + cellContext: context, ), ); - getIt.registerFactoryParam( - (cellData, _) => SelectionCellBloc( - cellData: cellData, + getIt.registerFactoryParam( + (context, _) => SelectionCellBloc( + cellContext: context, ), ); - getIt.registerFactoryParam( - (cellData, _) => NumberCellBloc( - cellData: cellData, + getIt.registerFactoryParam( + (context, _) => NumberCellBloc( + cellContext: context, ), ); - getIt.registerFactoryParam( - (cellData, _) => DateCellBloc( - cellData: cellData, + getIt.registerFactoryParam( + (context, _) => DateCellBloc( + cellContext: context, ), ); - getIt.registerFactoryParam( + getIt.registerFactoryParam( (cellData, _) => CheckboxCellBloc( service: CellService(), - cellData: cellData, + cellContext: cellData, ), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_listener.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_listener.dart index 314345017e..05289fee03 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_listener.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_listener.dart @@ -1,5 +1,4 @@ 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'; @@ -7,16 +6,17 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:app_flowy/core/notification_helper.dart'; -typedef UpdateFieldNotifiedValue = Either; +typedef UpdateFieldNotifiedValue = Either; class CellListener { final String rowId; final String fieldId; - PublishNotifier? updateCellNotifier = PublishNotifier(); + PublishNotifier? _updateCellNotifier = PublishNotifier(); GridNotificationListener? _listener; CellListener({required this.rowId, required this.fieldId}); - void start() { + void start({required void Function(UpdateFieldNotifiedValue) onCellChanged}) { + _updateCellNotifier?.addPublishListener(onCellChanged); _listener = GridNotificationListener(objectId: "$rowId:$fieldId", handler: _handler); } @@ -24,8 +24,8 @@ class CellListener { switch (ty) { case GridNotification.DidUpdateCell: result.fold( - (payload) => updateCellNotifier?.value = left(CellNotificationData.fromBuffer(payload)), - (error) => updateCellNotifier?.value = right(error), + (payload) => _updateCellNotifier?.value = left(unit), + (error) => _updateCellNotifier?.value = right(error), ); break; default: @@ -35,7 +35,7 @@ class CellListener { Future stop() async { await _listener?.stop(); - updateCellNotifier?.dispose(); - updateCellNotifier = null; + _updateCellNotifier?.dispose(); + _updateCellNotifier = null; } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart index dce064eae2..1aa090d443 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart @@ -1,12 +1,290 @@ +import 'dart:async'; import 'dart:collection'; -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; -import 'package:flowy_sdk/log.dart'; -import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.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/cell_entities.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; +import 'package:flutter/foundation.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; +import 'package:equatable/equatable.dart'; +part 'cell_service.freezed.dart'; + +typedef GridDefaultCellContext = GridCellContext; +typedef GridSelectOptionCellContext = GridCellContext; + +// ignore: must_be_immutable +class GridCellContext extends Equatable { + final GridCell gridCell; + final GridCellCache cellCache; + final GridCellCacheKey _cacheKey; + final GridCellDataLoader cellDataLoader; + final CellService _cellService = CellService(); + + late final CellListener _cellListener; + late final ValueNotifier _cellDataNotifier; + bool isListening = false; + VoidCallback? _onFieldChangedFn; + Timer? _delayOperation; + + GridCellContext({ + required this.gridCell, + required this.cellCache, + required this.cellDataLoader, + }) : _cacheKey = GridCellCacheKey(objectId: gridCell.rowId, fieldId: gridCell.field.id); + + GridCellContext clone() { + return GridCellContext( + gridCell: gridCell, + cellDataLoader: cellDataLoader, + cellCache: cellCache, + ); + } + + String get gridId => gridCell.gridId; + + String get rowId => gridCell.rowId; + + String get cellId => gridCell.rowId + gridCell.field.id; + + String get fieldId => gridCell.field.id; + + Field get field => gridCell.field; + + FieldType get fieldType => gridCell.field.fieldType; + + GridCellCacheKey get cacheKey => _cacheKey; + + VoidCallback? startListening({required void Function(T) onCellChanged}) { + if (isListening) { + Log.error("Already started. It seems like you should call clone first"); + return null; + } + + isListening = true; + _cellDataNotifier = ValueNotifier(cellCache.get(cacheKey)); + _cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id); + _cellListener.start(onCellChanged: (result) { + result.fold( + (_) => _loadData(), + (err) => Log.error(err), + ); + }); + + if (cellDataLoader.reloadOnFieldChanged) { + _onFieldChangedFn = () { + _loadData(); + }; + cellCache.addListener(cacheKey, _onFieldChangedFn!); + } + + onCellChangedFn() { + final value = _cellDataNotifier.value; + if (value is T) { + onCellChanged(value); + } + + if (cellDataLoader.reloadOnCellChanged) { + _loadData(); + } + } + + _cellDataNotifier.addListener(onCellChangedFn); + return onCellChangedFn; + } + + void removeListener(VoidCallback fn) { + _cellDataNotifier.removeListener(fn); + } + + T? getCellData() { + final data = cellCache.get(cacheKey); + if (data == null) { + _loadData(); + } + return data; + } + + void saveCellData(String data) { + _cellService.updateCell(gridId: gridId, fieldId: field.id, rowId: rowId, data: data).then((result) { + result.fold((l) => null, (err) => Log.error(err)); + }); + } + + void _loadData() { + _delayOperation?.cancel(); + _delayOperation = Timer(const Duration(milliseconds: 10), () { + cellDataLoader.loadData().then((data) { + _cellDataNotifier.value = data; + cellCache.insert(GridCellCacheData(key: cacheKey, object: data)); + }); + }); + } + + void dispose() { + _delayOperation?.cancel(); + + if (_onFieldChangedFn != null) { + cellCache.removeListener(cacheKey, _onFieldChangedFn!); + _onFieldChangedFn = null; + } + } + + @override + List get props => [cellCache.get(cacheKey) ?? "", cellId]; +} + +abstract class GridCellDataLoader { + Future loadData(); + + bool get reloadOnFieldChanged => true; + bool get reloadOnCellChanged => false; +} + +abstract class GridCellDataConfig { + bool get reloadOnFieldChanged => true; + bool get reloadOnCellChanged => false; +} + +class DefaultCellDataLoader extends GridCellDataLoader { + final CellService service = CellService(); + final GridCell gridCell; + @override + final bool reloadOnCellChanged; + + DefaultCellDataLoader({ + required this.gridCell, + this.reloadOnCellChanged = false, + }); + + @override + Future loadData() { + final fut = service.getCell( + gridId: gridCell.gridId, + fieldId: gridCell.field.id, + rowId: gridCell.rowId, + ); + return fut.then((result) { + return result.fold((data) => data, (err) { + Log.error(err); + return null; + }); + }); + } +} + +// key: rowId +typedef GridCellMap = LinkedHashMap; + +class GridCellCacheData { + GridCellCacheKey key; + dynamic object; + GridCellCacheData({ + required this.key, + required this.object, + }); +} + +class GridCellCacheKey { + final String fieldId; + final String objectId; + GridCellCacheKey({ + required this.fieldId, + required this.objectId, + }); +} + +abstract class GridCellFieldDelegate { + void onFieldChanged(void Function(String) callback); + void dispose(); +} + +class GridCellCache { + final String gridId; + final GridCellFieldDelegate fieldDelegate; + + /// fieldId: {objectId: callback} + final Map>> _listenerByFieldId = {}; + + /// fieldId: {cacheKey: cacheData} + final Map> _cellDataByFieldId = {}; + GridCellCache({ + required this.gridId, + required this.fieldDelegate, + }) { + fieldDelegate.onFieldChanged((fieldId) { + _cellDataByFieldId.remove(fieldId); + final map = _listenerByFieldId[fieldId]; + if (map != null) { + for (final callbacks in map.values) { + for (final callback in callbacks) { + callback(); + } + } + } + }); + } + + void addListener(GridCellCacheKey cacheKey, VoidCallback callback) { + var map = _listenerByFieldId[cacheKey.fieldId]; + if (map == null) { + _listenerByFieldId[cacheKey.fieldId] = {}; + map = _listenerByFieldId[cacheKey.fieldId]; + map![cacheKey.objectId] = [callback]; + } else { + var objects = map[cacheKey.objectId]; + if (objects == null) { + map[cacheKey.objectId] = [callback]; + } else { + objects.add(callback); + } + } + } + + void removeListener(GridCellCacheKey cacheKey, VoidCallback fn) { + var callbacks = _listenerByFieldId[cacheKey.fieldId]?[cacheKey.objectId]; + final index = callbacks?.indexWhere((callback) => callback == fn); + if (index != null && index != -1) { + callbacks?.removeAt(index); + } + } + + void insert(T item) { + var map = _cellDataByFieldId[item.key.fieldId]; + if (map == null) { + _cellDataByFieldId[item.key.fieldId] = {}; + map = _cellDataByFieldId[item.key.fieldId]; + } + + map![item.key.objectId] = item.object; + } + + T? get(GridCellCacheKey key) { + final map = _cellDataByFieldId[key.fieldId]; + if (map == null) { + return null; + } else { + final object = map[key.objectId]; + if (object is T) { + return object; + } else { + if (object != null) { + Log.error("Cache data type does not match the cache data type"); + } + + return null; + } + } + } + + Future dispose() async { + fieldDelegate.dispose(); + } +} class CellService { CellService(); @@ -38,38 +316,12 @@ class CellService { } } -class CellCache { - final CellService _cellService; - final HashMap _cellDataMap = HashMap(); - - CellCache() : _cellService = CellService(); - - Future> getCellData(GridCell identifier) async { - final cellId = _cellId(identifier); - final Cell? data = _cellDataMap[cellId]; - if (data != null) { - return Future(() => Some(data)); - } - - final result = await _cellService.getCell( - gridId: identifier.gridId, - fieldId: identifier.field.id, - rowId: identifier.rowId, - ); - - return result.fold( - (cell) { - _cellDataMap[_cellId(identifier)] = cell; - return Some(cell); - }, - (err) { - Log.error(err); - return none(); - }, - ); - } - - String _cellId(GridCell identifier) { - return "${identifier.rowId}/${identifier.field.id}"; - } +@freezed +class GridCell with _$GridCell { + const factory GridCell({ + required String gridId, + required String rowId, + required Field field, + Cell? cell, + }) = _GridCell; } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart index 880267f6c9..4703502cdd 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/checkbox_cell_bloc.dart @@ -1,6 +1,3 @@ -import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; -import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -10,15 +7,13 @@ import 'cell_service.dart'; part 'checkbox_cell_bloc.freezed.dart'; class CheckboxCellBloc extends Bloc { - final CellService _service; - final CellListener _cellListener; + final GridDefaultCellContext cellContext; + void Function()? _onCellChangedFn; CheckboxCellBloc({ required CellService service, - required GridCell cellData, - }) : _service = service, - _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id), - super(CheckboxCellState.initial(cellData)) { + required this.cellContext, + }) : super(CheckboxCellState.initial(cellContext)) { on( (event, emit) async { await event.map( @@ -38,42 +33,25 @@ class CheckboxCellBloc extends Bloc { @override Future close() async { - await _cellListener.stop(); + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + + cellContext.dispose(); return super.close(); } void _startListening() { - _cellListener.updateCellNotifier?.addPublishListener((result) { - result.fold( - (notificationData) async => await _loadCellData(), - (err) => Log.error(err), - ); - }); - _cellListener.start(); - } - - Future _loadCellData() async { - final result = await _service.getCell( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - ); - if (isClosed) { - return; - } - result.fold( - (cell) => add(CheckboxCellEvent.didReceiveCellUpdate(cell)), - (err) => Log.error(err), - ); + _onCellChangedFn = cellContext.startListening(onCellChanged: ((cell) { + if (!isClosed) { + add(CheckboxCellEvent.didReceiveCellUpdate(cell)); + } + })); } void _updateCellData() { - _service.updateCell( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - data: !state.isSelected ? "Yes" : "No", - ); + cellContext.saveCellData(!state.isSelected ? "Yes" : "No"); } } @@ -87,12 +65,11 @@ class CheckboxCellEvent with _$CheckboxCellEvent { @freezed class CheckboxCellState with _$CheckboxCellState { const factory CheckboxCellState({ - required GridCell cellData, required bool isSelected, }) = _CheckboxCellState; - factory CheckboxCellState.initial(GridCell cellData) { - return CheckboxCellState(cellData: cellData, isSelected: _isSelected(cellData.cell)); + factory CheckboxCellState.initial(GridCellContext context) { + return CheckboxCellState(isSelected: _isSelected(context.getCellData())); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart index cbabe6c5fe..f21fe38f2d 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart @@ -1,7 +1,3 @@ -import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; -import 'package:app_flowy/workspace/application/grid/field/field_listener.dart'; -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; -import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell, Field; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -11,15 +7,10 @@ import 'cell_service.dart'; part 'date_cell_bloc.freezed.dart'; class DateCellBloc extends Bloc { - final CellService _service; - final CellListener _cellListener; - final SingleFieldListener _fieldListener; + final GridDefaultCellContext cellContext; + void Function()? _onCellChangedFn; - DateCellBloc({required GridCell cellData}) - : _service = CellService(), - _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id), - _fieldListener = SingleFieldListener(fieldId: cellData.field.id), - super(DateCellState.initial(cellData)) { + DateCellBloc({required this.cellContext}) : super(DateCellState.initial(cellContext)) { on( (event, emit) async { event.map( @@ -31,13 +22,11 @@ class DateCellBloc extends Bloc { }, didReceiveCellUpdate: (_DidReceiveCellUpdate value) { emit(state.copyWith( - cellData: state.cellData.copyWith(cell: value.cell), content: value.cell.content, )); }, didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) { emit(state.copyWith(field: value.field)); - _loadCellData(); }, ); }, @@ -46,52 +35,27 @@ class DateCellBloc extends Bloc { @override Future close() async { - await _cellListener.stop(); - await _fieldListener.stop(); + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + cellContext.dispose(); return super.close(); } void _startListening() { - _cellListener.updateCellNotifier?.addPublishListener((result) { - result.fold( - (notificationData) => _loadCellData(), - (err) => Log.error(err), - ); - }, listenWhen: () => !isClosed); - _cellListener.start(); - - _fieldListener.updateFieldNotifier?.addPublishListener((result) { - result.fold( - (field) => add(DateCellEvent.didReceiveFieldUpdate(field)), - (err) => Log.error(err), - ); - }, listenWhen: () => !isClosed); - _fieldListener.start(); - } - - Future _loadCellData() async { - final result = await _service.getCell( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - ); - if (isClosed) { - return; - } - result.fold( - (cell) => add(DateCellEvent.didReceiveCellUpdate(cell)), - (err) => Log.error(err), + _onCellChangedFn = cellContext.startListening( + onCellChanged: ((cell) { + if (!isClosed) { + add(DateCellEvent.didReceiveCellUpdate(cell)); + } + }), ); } void _updateCellData(DateTime day) { final data = day.millisecondsSinceEpoch ~/ 1000; - _service.updateCell( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - data: data.toString(), - ); + cellContext.saveCellData(data.toString()); } } @@ -106,15 +70,13 @@ class DateCellEvent with _$DateCellEvent { @freezed class DateCellState with _$DateCellState { const factory DateCellState({ - required GridCell cellData, required String content, required Field field, DateTime? selectedDay, }) = _DateCellState; - factory DateCellState.initial(GridCell cellData) => DateCellState( - cellData: cellData, - field: cellData.field, - content: cellData.cell?.content ?? "", + factory DateCellState.initial(GridCellContext context) => DateCellState( + field: context.field, + content: context.getCellData()?.content ?? "", ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart index 27f17257e1..ce801f0385 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/number_cell_bloc.dart @@ -1,7 +1,3 @@ -import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; -import 'package:app_flowy/workspace/application/grid/field/field_listener.dart'; -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; -import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -11,16 +7,12 @@ import 'cell_service.dart'; part 'number_cell_bloc.freezed.dart'; class NumberCellBloc extends Bloc { - final CellService _service; - final CellListener _cellListener; - final SingleFieldListener _fieldListener; + final GridDefaultCellContext cellContext; + void Function()? _onCellChangedFn; NumberCellBloc({ - required GridCell cellData, - }) : _service = CellService(), - _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id), - _fieldListener = SingleFieldListener(fieldId: cellData.field.id), - super(NumberCellState.initial(cellData)) { + required this.cellContext, + }) : super(NumberCellState.initial(cellContext)) { on( (event, emit) async { await event.map( @@ -39,58 +31,26 @@ class NumberCellBloc extends Bloc { } Future _updateCellValue(_UpdateCell value, Emitter emit) async { - final result = await _service.updateCell( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - data: value.text, - ); - result.fold( - (field) => _getCellData(), - (err) => Log.error(err), - ); + cellContext.saveCellData(value.text); } @override Future close() async { - await _cellListener.stop(); - await _fieldListener.stop(); + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + cellContext.dispose(); return super.close(); } void _startListening() { - _cellListener.updateCellNotifier?.addPublishListener((result) { - result.fold( - (notificationData) async { - await _getCellData(); - }, - (err) => Log.error(err), - ); - }); - _cellListener.start(); - - _fieldListener.updateFieldNotifier?.addPublishListener((result) { - result.fold( - (field) => _getCellData(), - (err) => Log.error(err), - ); - }); - _fieldListener.start(); - } - - Future _getCellData() async { - final result = await _service.getCell( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - ); - - if (isClosed) { - return; - } - result.fold( - (cell) => add(NumberCellEvent.didReceiveCellUpdate(cell)), - (err) => Log.error(err), + _onCellChangedFn = cellContext.startListening( + onCellChanged: ((cell) { + if (!isClosed) { + add(NumberCellEvent.didReceiveCellUpdate(cell)); + } + }), ); } } @@ -105,11 +65,11 @@ class NumberCellEvent with _$NumberCellEvent { @freezed class NumberCellState with _$NumberCellState { const factory NumberCellState({ - required GridCell cellData, required String content, }) = _NumberCellState; - factory NumberCellState.initial(GridCell cellData) { - return NumberCellState(cellData: cellData, content: cellData.cell?.content ?? ""); + factory NumberCellState.initial(GridDefaultCellContext context) { + final cell = context.getCellData(); + return NumberCellState(content: cell?.content ?? ""); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart index c76f06adc9..8a10a211d5 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart @@ -1,19 +1,43 @@ -import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; -import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.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/cell_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; -class SelectOptionService { - SelectOptionService(); +import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart'; - Future> create({ - required String gridId, - required String fieldId, - required String rowId, - required String name, - }) { +import 'cell_service.dart'; + +class SelectOptionCellDataLoader extends GridCellDataLoader { + final SelectOptionService service; + final GridCell gridCell; + SelectOptionCellDataLoader({ + required this.gridCell, + }) : service = SelectOptionService(gridCell: gridCell); + @override + Future loadData() async { + return service.getOpitonContext().then((result) { + return result.fold( + (data) => data, + (err) { + Log.error(err); + return null; + }, + ); + }); + } +} + +class SelectOptionService { + final GridCell gridCell; + SelectOptionService({required this.gridCell}); + + String get gridId => gridCell.gridId; + String get fieldId => gridCell.field.id; + String get rowId => gridCell.rowId; + + Future> create({required String name}) { return TypeOptionService(gridId: gridId, fieldId: fieldId).newOption(name: name).then( (result) { return result.fold( @@ -34,9 +58,6 @@ class SelectOptionService { } Future> update({ - required String gridId, - required String fieldId, - required String rowId, required SelectOption option, }) { final cellIdentifier = CellIdentifierPayload.create() @@ -50,9 +71,6 @@ class SelectOptionService { } Future> delete({ - required String gridId, - required String fieldId, - required String rowId, required SelectOption option, }) { final cellIdentifier = CellIdentifierPayload.create() @@ -67,11 +85,7 @@ class SelectOptionService { return GridEventUpdateSelectOption(payload).send(); } - Future> getOpitonContext({ - required String gridId, - required String fieldId, - required String rowId, - }) { + Future> getOpitonContext() { final payload = CellIdentifierPayload.create() ..gridId = gridId ..fieldId = fieldId @@ -80,12 +94,7 @@ class SelectOptionService { return GridEventGetSelectOptionContext(payload).send(); } - Future> select({ - required String gridId, - required String fieldId, - required String rowId, - required String optionId, - }) { + Future> select({required String optionId}) { final payload = SelectOptionCellChangesetPayload.create() ..gridId = gridId ..fieldId = fieldId @@ -94,12 +103,7 @@ class SelectOptionService { return GridEventUpdateCellSelectOption(payload).send(); } - Future> remove({ - required String gridId, - required String fieldId, - required String rowId, - required String optionId, - }) { + Future> unSelect({required String optionId}) { final payload = SelectOptionCellChangesetPayload.create() ..gridId = gridId ..fieldId = fieldId diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart index 2d7435dc7a..2d770aaba5 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/selection_cell_bloc.dart @@ -1,35 +1,29 @@ -import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; -import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart'; -import 'package:app_flowy/workspace/application/grid/field/field_listener.dart'; -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; -import 'package:flowy_sdk/log.dart'; +import 'dart:async'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'dart:async'; +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; part 'selection_cell_bloc.freezed.dart'; class SelectionCellBloc extends Bloc { - final SelectOptionService _service; - final CellListener _cellListener; - final SingleFieldListener _fieldListener; + final GridSelectOptionCellContext cellContext; + void Function()? _onCellChangedFn; SelectionCellBloc({ - required GridCell cellData, - }) : _service = SelectOptionService(), - _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id), - _fieldListener = SingleFieldListener(fieldId: cellData.field.id), - super(SelectionCellState.initial(cellData)) { + required this.cellContext, + }) : super(SelectionCellState.initial(cellContext)) { on( (event, emit) async { await event.map( initial: (_InitialCell value) async { - _loadOptions(); _startListening(); }, didReceiveOptions: (_DidReceiveOptions value) { - emit(state.copyWith(options: value.options, selectedOptions: value.selectedOptions)); + emit(state.copyWith( + options: value.options, + selectedOptions: value.selectedOptions, + )); }, ); }, @@ -38,46 +32,25 @@ class SelectionCellBloc extends Bloc { @override Future close() async { - await _cellListener.stop(); - await _fieldListener.stop(); + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + cellContext.dispose(); return super.close(); } - void _loadOptions() async { - final result = await _service.getOpitonContext( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - ); - if (isClosed) { - return; - } - - result.fold( - (selectOptionContext) => add(SelectionCellEvent.didReceiveOptions( - selectOptionContext.options, - selectOptionContext.selectOptions, - )), - (err) => Log.error(err), - ); - } - void _startListening() { - _cellListener.updateCellNotifier?.addPublishListener((result) { - result.fold( - (notificationData) => _loadOptions(), - (err) => Log.error(err), - ); - }); - _cellListener.start(); - - _fieldListener.updateFieldNotifier?.addPublishListener((result) { - result.fold( - (field) => _loadOptions(), - (err) => Log.error(err), - ); - }); - _fieldListener.start(); + _onCellChangedFn = cellContext.startListening( + onCellChanged: ((selectOptionContext) { + if (!isClosed) { + add(SelectionCellEvent.didReceiveOptions( + selectOptionContext.options, + selectOptionContext.selectOptions, + )); + } + }), + ); } } @@ -93,14 +66,16 @@ class SelectionCellEvent with _$SelectionCellEvent { @freezed class SelectionCellState with _$SelectionCellState { const factory SelectionCellState({ - required GridCell cellData, required List options, required List selectedOptions, }) = _SelectionCellState; - factory SelectionCellState.initial(GridCell cellData) => SelectionCellState( - cellData: cellData, - options: [], - selectedOptions: [], - ); + factory SelectionCellState.initial(GridSelectOptionCellContext context) { + final data = context.getCellData(); + + return SelectionCellState( + options: data?.options ?? [], + selectedOptions: data?.selectOptions ?? [], + ); + } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart index 92676a0c12..0b62945265 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/selection_editor_bloc.dart @@ -1,8 +1,5 @@ -import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart'; -import 'package:app_flowy/workspace/application/grid/field/field_listener.dart'; -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; import 'package:flowy_sdk/log.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -13,28 +10,19 @@ part 'selection_editor_bloc.freezed.dart'; class SelectOptionEditorBloc extends Bloc { final SelectOptionService _selectOptionService; - final SingleFieldListener _fieldListener; - final CellListener _cellListener; - Timer? _delayOperation; + final GridSelectOptionCellContext cellContext; + void Function()? _onCellChangedFn; SelectOptionEditorBloc({ - required GridCell cellData, - required List options, - required List selectedOptions, - }) : _selectOptionService = SelectOptionService(), - _fieldListener = SingleFieldListener(fieldId: cellData.field.id), - _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id), - super(SelectOptionEditorState.initial(cellData, options, selectedOptions)) { + required this.cellContext, + }) : _selectOptionService = SelectOptionService(gridCell: cellContext.gridCell), + super(SelectOptionEditorState.initial(cellContext)) { on( (event, emit) async { await event.map( initial: (_Initial value) async { _startListening(); }, - didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) { - emit(state.copyWith(field: value.field)); - _loadOptions(); - }, didReceiveOptions: (_DidReceiveOptions value) { emit(state.copyWith( options: value.options, @@ -51,7 +39,7 @@ class SelectOptionEditorBloc extends Bloc close() async { - _delayOperation?.cancel(); - await _fieldListener.stop(); - await _cellListener.stop(); + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + cellContext.dispose(); return super.close(); } void _createOption(String name) async { - final result = await _selectOptionService.create( - gridId: state.gridId, - fieldId: state.field.id, - rowId: state.rowId, - name: name, - ); - result.fold((l) => _loadOptions(), (err) => Log.error(err)); + final result = await _selectOptionService.create(name: name); + result.fold((l) => {}, (err) => Log.error(err)); } void _deleteOption(SelectOption option) async { final result = await _selectOptionService.delete( - gridId: state.gridId, - fieldId: state.field.id, - rowId: state.rowId, option: option, ); @@ -89,72 +71,38 @@ class SelectOptionEditorBloc extends Bloc null, (err) => Log.error(err)); } - void _makeOptionAsSelected(String optionId) { - _selectOptionService.select( - gridId: state.gridId, - fieldId: state.field.id, - rowId: state.rowId, - optionId: optionId, - ); - } - - void _loadOptions() async { - _delayOperation?.cancel(); - _delayOperation = Timer( - const Duration(milliseconds: 1), - () async { - final result = await _selectOptionService.getOpitonContext( - gridId: state.gridId, - fieldId: state.field.id, - rowId: state.rowId, - ); - if (isClosed) { - return; - } - - result.fold( - (selectOptionContext) => add(SelectOptionEditorEvent.didReceiveOptions( - selectOptionContext.options, - selectOptionContext.selectOptions, - )), - (err) => Log.error(err), - ); - }, - ); + void _onSelectOption(String optionId) { + final hasSelected = state.selectedOptions.firstWhereOrNull((option) => option.id == optionId); + if (hasSelected != null) { + _selectOptionService.unSelect(optionId: optionId); + } else { + _selectOptionService.select(optionId: optionId); + } } void _startListening() { - _cellListener.updateCellNotifier?.addPublishListener((result) { - result.fold( - (notificationData) => _loadOptions(), - (err) => Log.error(err), - ); - }); - _cellListener.start(); - - _fieldListener.updateFieldNotifier?.addPublishListener((result) { - result.fold( - (field) => add(SelectOptionEditorEvent.didReceiveFieldUpdate(field)), - (err) => Log.error(err), - ); - }, listenWhen: () => !isClosed); - _fieldListener.start(); + _onCellChangedFn = cellContext.startListening( + onCellChanged: ((selectOptionContext) { + if (!isClosed) { + add(SelectOptionEditorEvent.didReceiveOptions( + selectOptionContext.options, + selectOptionContext.selectOptions, + )); + } + }), + ); } } @freezed class SelectOptionEditorEvent with _$SelectOptionEditorEvent { const factory SelectOptionEditorEvent.initial() = _Initial; - const factory SelectOptionEditorEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate; const factory SelectOptionEditorEvent.didReceiveOptions( List options, List selectedOptions) = _DidReceiveOptions; const factory SelectOptionEditorEvent.newOption(String optionName) = _NewOption; @@ -166,24 +114,15 @@ class SelectOptionEditorEvent with _$SelectOptionEditorEvent { @freezed class SelectOptionEditorState with _$SelectOptionEditorState { const factory SelectOptionEditorState({ - required String gridId, - required Field field, - required String rowId, required List options, required List selectedOptions, }) = _SelectOptionEditorState; - factory SelectOptionEditorState.initial( - GridCell cellData, - List options, - List selectedOptions, - ) { + factory SelectOptionEditorState.initial(GridSelectOptionCellContext context) { + final data = context.getCellData(); return SelectOptionEditorState( - gridId: cellData.gridId, - field: cellData.field, - rowId: cellData.rowId, - options: options, - selectedOptions: selectedOptions, + options: data?.options ?? [], + selectedOptions: data?.selectOptions ?? [], ); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart index f32c066c5e..41e2745fb5 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/text_cell_bloc.dart @@ -1,23 +1,17 @@ -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; -import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; -import 'cell_listener.dart'; import 'cell_service.dart'; part 'text_cell_bloc.freezed.dart'; class TextCellBloc extends Bloc { - final CellService _service; - final CellListener _cellListener; - + final GridDefaultCellContext cellContext; + void Function()? _onCellChangedFn; TextCellBloc({ - required GridCell cellData, - }) : _service = CellService(), - _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id), - super(TextCellState.initial(cellData)) { + required this.cellContext, + }) : super(TextCellState.initial(cellContext)) { on( (event, emit) async { await event.map( @@ -25,18 +19,14 @@ class TextCellBloc extends Bloc { _startListening(); }, updateText: (_UpdateText value) { - updateCellContent(value.text); + cellContext.saveCellData(value.text); emit(state.copyWith(content: value.text)); }, didReceiveCellData: (_DidReceiveCellData value) { - emit(state.copyWith( - cellData: value.cellData, - content: value.cellData.cell?.content ?? "", - )); + emit(state.copyWith(content: value.cellData.cell?.content ?? "")); }, didReceiveCellUpdate: (_DidReceiveCellUpdate value) { emit(state.copyWith( - cellData: state.cellData.copyWith(cell: value.cell), content: value.cell.content, )); }, @@ -47,44 +37,21 @@ class TextCellBloc extends Bloc { @override Future close() async { - await _cellListener.stop(); + if (_onCellChangedFn != null) { + cellContext.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + cellContext.dispose(); return super.close(); } - void updateCellContent(String content) { - final fieldId = state.cellData.field.id; - final gridId = state.cellData.gridId; - final rowId = state.cellData.rowId; - _service.updateCell( - data: content, - fieldId: fieldId, - gridId: gridId, - rowId: rowId, - ); - } - void _startListening() { - _cellListener.updateCellNotifier?.addPublishListener((result) { - result.fold( - (notificationData) async => await _loadCellData(), - (err) => Log.error(err), - ); - }); - _cellListener.start(); - } - - Future _loadCellData() async { - final result = await _service.getCell( - gridId: state.cellData.gridId, - fieldId: state.cellData.field.id, - rowId: state.cellData.rowId, - ); - if (isClosed) { - return; - } - result.fold( - (cell) => add(TextCellEvent.didReceiveCellUpdate(cell)), - (err) => Log.error(err), + _onCellChangedFn = cellContext.startListening( + onCellChanged: ((cell) { + if (!isClosed) { + add(TextCellEvent.didReceiveCellUpdate(cell)); + } + }), ); } } @@ -101,11 +68,9 @@ class TextCellEvent with _$TextCellEvent { class TextCellState with _$TextCellState { const factory TextCellState({ required String content, - required GridCell cellData, }) = _TextCellState; - factory TextCellState.initial(GridCell cellData) => TextCellState( - content: cellData.cell?.content ?? "", - cellData: cellData, + factory TextCellState.initial(GridDefaultCellContext context) => TextCellState( + content: context.getCellData()?.content ?? "", ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart index 576e62e3b1..d0d2999b6c 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart @@ -45,13 +45,15 @@ class FieldCellBloc extends Bloc { } void _startListening() { - _fieldListener.updateFieldNotifier?.addPublishListener((result) { + _fieldListener.start(onFieldChanged: (result) { + if (isClosed) { + return; + } result.fold( (field) => add(FieldCellEvent.didReceiveFieldUpdate(field)), (err) => Log.error(err), ); - }, listenWhen: () => !isClosed); - _fieldListener.start(); + }); } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/field_listener.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_listener.dart index b31a9c3ff5..ec4ef77f30 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/field_listener.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_listener.dart @@ -11,12 +11,13 @@ typedef UpdateFieldNotifiedValue = Either; class SingleFieldListener { final String fieldId; - PublishNotifier? updateFieldNotifier = PublishNotifier(); + PublishNotifier? _updateFieldNotifier = PublishNotifier(); GridNotificationListener? _listener; SingleFieldListener({required this.fieldId}); - void start() { + void start({required void Function(UpdateFieldNotifiedValue) onFieldChanged}) { + _updateFieldNotifier?.addPublishListener(onFieldChanged); _listener = GridNotificationListener( objectId: fieldId, handler: _handler, @@ -30,8 +31,8 @@ class SingleFieldListener { switch (ty) { case GridNotification.DidUpdateField: result.fold( - (payload) => updateFieldNotifier?.value = left(Field.fromBuffer(payload)), - (error) => updateFieldNotifier?.value = right(error), + (payload) => _updateFieldNotifier?.value = left(Field.fromBuffer(payload)), + (error) => _updateFieldNotifier?.value = right(error), ); break; default: @@ -41,7 +42,7 @@ class SingleFieldListener { Future stop() async { await _listener?.stop(); - updateFieldNotifier?.dispose(); - updateFieldNotifier = null; + _updateFieldNotifier?.dispose(); + _updateFieldNotifier = null; } } diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart b/frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart index 6774205a5e..e97d24a3e1 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart @@ -15,7 +15,8 @@ class GridFieldsListener { GridNotificationListener? _listener; GridFieldsListener({required this.gridId}); - void start() { + void start({required void Function(UpdateFieldNotifiedValue) onFieldsChanged}) { + updateFieldsNotifier?.addPublishListener(onFieldsChanged); _listener = GridNotificationListener( objectId: gridId, handler: _handler, 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 93036082fb..4915d77087 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -5,6 +5,7 @@ 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 'cell/cell_service.dart'; import 'grid_service.dart'; import 'row/row_service.dart'; @@ -14,6 +15,7 @@ class GridBloc extends Bloc { final GridService _gridService; final GridFieldCache fieldCache; late final GridRowCache rowCache; + late final GridCellCache cellCache; GridBloc({required View view}) : _gridService = GridService(gridId: view.id), @@ -21,7 +23,12 @@ class GridBloc extends Bloc { super(GridState.initial(view.id)) { rowCache = GridRowCache( gridId: view.id, - dataDelegate: GridRowDataDelegateAdaptor(fieldCache), + fieldDelegate: GridRowCacheDelegateImpl(fieldCache), + ); + + cellCache = GridCellCache( + gridId: view.id, + fieldDelegate: GridCellCacheDelegateImpl(fieldCache), ); on( @@ -48,8 +55,9 @@ class GridBloc extends Bloc { @override Future close() async { await _gridService.closeGrid(); - await fieldCache.dispose(); + await cellCache.dispose(); await rowCache.dispose(); + await fieldCache.dispose(); return super.close(); } @@ -81,7 +89,7 @@ class GridBloc extends Bloc { () => result.fold( (fields) { fieldCache.fields = fields.items; - rowCache.updateWithBlock(grid.blockOrders); + rowCache.resetRows(grid.blockOrders); emit(state.copyWith( grid: Some(grid), 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 aee2a82be4..d217d2c6b7 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart @@ -8,6 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'cell/cell_service.dart'; import 'row/row_service.dart'; class GridService { @@ -53,23 +54,29 @@ class FieldsNotifier extends ChangeNotifier { List get fields => _fields; } +typedef ChangesetListener = void Function(GridFieldChangeset); + class GridFieldCache { final String gridId; late final GridFieldsListener _fieldListener; final FieldsNotifier _fieldNotifier = FieldsNotifier(); + final List _changesetListener = []; + GridFieldCache({required this.gridId}) { _fieldListener = GridFieldsListener(gridId: gridId); - _fieldListener.updateFieldsNotifier?.addPublishListener((result) { + _fieldListener.start(onFieldsChanged: (result) { result.fold( (changeset) { _deleteFields(changeset.deletedFields); _insertFields(changeset.insertedFields); _updateFields(changeset.updatedFields); + for (final listener in _changesetListener) { + listener(changeset); + } }, (err) => Log.error(err), ); }); - _fieldListener.start(); } Future dispose() async { @@ -77,8 +84,6 @@ class GridFieldCache { _fieldNotifier.dispose(); } - void applyChangeset(GridFieldChangeset changeset) {} - UnmodifiableListView get unmodifiableFields => UnmodifiableListView(_fieldNotifier.fields); List get clonedFields => [..._fieldNotifier.fields]; @@ -111,6 +116,17 @@ class GridFieldCache { _fieldNotifier.removeListener(f); } + void addChangesetListener(ChangesetListener listener) { + _changesetListener.add(listener); + } + + void removeChangesetListener(ChangesetListener listener) { + final index = _changesetListener.indexWhere((element) => element == listener); + if (index != -1) { + _changesetListener.removeAt(index); + } + } + void _deleteFields(List deletedFields) { if (deletedFields.isEmpty) { return; @@ -155,43 +171,43 @@ class GridFieldCache { } } -class GridRowDataDelegateAdaptor extends GridRowDataDelegate { +class GridRowCacheDelegateImpl extends GridRowFieldDelegate { final GridFieldCache _cache; + GridRowCacheDelegateImpl(GridFieldCache cache) : _cache = cache; - GridRowDataDelegateAdaptor(GridFieldCache cache) : _cache = cache; @override UnmodifiableListView get fields => _cache.unmodifiableFields; - @override - GridRow buildGridRow(RowOrder rowOrder) { - return GridRow( - gridId: _cache.gridId, - fields: _cache.unmodifiableFields, - rowId: rowOrder.rowId, - height: rowOrder.height.toDouble(), - ); - } - @override void onFieldChanged(FieldDidUpdateCallback callback) { _cache.addListener(listener: () { callback(); }); } +} + +class GridCellCacheDelegateImpl extends GridCellFieldDelegate { + final GridFieldCache _cache; + ChangesetListener? _changesetFn; + GridCellCacheDelegateImpl(GridFieldCache cache) : _cache = cache; @override - CellDataMap buildCellDataMap(Row rowData) { - var map = CellDataMap.new(); - for (final field in fields) { - if (field.visibility) { - map[field.id] = GridCell( - rowId: rowData.id, - gridId: _cache.gridId, - cell: rowData.cellByFieldId[field.id], - field: field, - ); + void onFieldChanged(void Function(String) callback) { + changesetFn(GridFieldChangeset changeset) { + for (final updatedField in changeset.updatedFields) { + callback(updatedField.id); } } - return map; + + _cache.addChangesetListener(changesetFn); + _changesetFn = changesetFn; + } + + @override + void dispose() { + if (_changesetFn != null) { + _cache.removeChangesetListener(_changesetFn!); + _changesetFn = null; + } } } 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 b49485c16b..cfd99b3e98 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 @@ -1,9 +1,9 @@ import 'dart:collection'; +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; import 'row_service.dart'; -import 'package:dartz/dartz.dart'; part 'row_bloc.freezed.dart'; @@ -17,19 +17,18 @@ class RowBloc extends Bloc { required GridRowCache rowCache, }) : _rowService = RowService(gridId: rowData.gridId, rowId: rowData.rowId), _rowCache = rowCache, - super(RowState.initial(rowData)) { + super(RowState.initial(rowData, rowCache.loadGridCells(rowData.rowId))) { on( (event, emit) async { await event.map( initial: (_InitialRow value) async { await _startListening(); - await _loadRow(emit); }, createRow: (_CreateRow value) { _rowService.createRow(); }, didReceiveCellDatas: (_DidReceiveCellDatas value) async { - emit(state.copyWith(cellDataMap: Some(value.cellData))); + emit(state.copyWith(cellDataMap: value.cellData)); }, ); }, @@ -41,6 +40,7 @@ class RowBloc extends Bloc { if (_rowListenFn != null) { _rowCache.removeRowListener(_rowListenFn!); } + return super.close(); } @@ -51,33 +51,24 @@ class RowBloc extends Bloc { listenWhen: () => !isClosed, ); } - - Future _loadRow(Emitter emit) async { - final data = _rowCache.loadCellData(state.rowData.rowId); - data.foldRight(null, (cellDatas, _) { - if (!isClosed) { - add(RowEvent.didReceiveCellDatas(cellDatas)); - } - }); - } } @freezed class RowEvent with _$RowEvent { const factory RowEvent.initial() = _InitialRow; const factory RowEvent.createRow() = _CreateRow; - const factory RowEvent.didReceiveCellDatas(CellDataMap cellData) = _DidReceiveCellDatas; + const factory RowEvent.didReceiveCellDatas(GridCellMap cellData) = _DidReceiveCellDatas; } @freezed class RowState with _$RowState { const factory RowState({ required GridRow rowData, - required Option cellDataMap, + required GridCellMap cellDataMap, }) = _RowState; - factory RowState.initial(GridRow rowData) => RowState( + factory RowState.initial(GridRow rowData, GridCellMap cellDataMap) => RowState( rowData: rowData, - cellDataMap: none(), + cellDataMap: cellDataMap, ); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart index 2390dc40a0..4228ddd863 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_detail_bloc.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; @@ -23,7 +24,7 @@ class RowDetailBloc extends Bloc { _loadCellData(); }, didReceiveCellDatas: (_DidReceiveCellDatas value) { - emit(state.copyWith(cellDatas: value.cellDatas)); + emit(state.copyWith(gridCells: value.gridCells)); }, ); }, @@ -47,28 +48,26 @@ class RowDetailBloc extends Bloc { } Future _loadCellData() async { - final data = _rowCache.loadCellData(rowData.rowId); - data.foldRight(null, (cellDataMap, _) { - if (!isClosed) { - add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList())); - } - }); + final cellDataMap = _rowCache.loadGridCells(rowData.rowId); + if (!isClosed) { + add(RowDetailEvent.didReceiveCellDatas(cellDataMap.values.toList())); + } } } @freezed class RowDetailEvent with _$RowDetailEvent { const factory RowDetailEvent.initial() = _Initial; - const factory RowDetailEvent.didReceiveCellDatas(List cellDatas) = _DidReceiveCellDatas; + const factory RowDetailEvent.didReceiveCellDatas(List gridCells) = _DidReceiveCellDatas; } @freezed class RowDetailState with _$RowDetailState { const factory RowDetailState({ - required List cellDatas, + required List gridCells, }) = _RowDetailState; factory RowDetailState.initial() => RowDetailState( - cellDatas: List.empty(), + gridCells: List.empty(), ); } 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 f524e03d0c..82879b647e 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,5 +1,6 @@ import 'dart:collection'; +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/log.dart'; @@ -13,38 +14,43 @@ part 'row_service.freezed.dart'; typedef RowUpdateCallback = void Function(); typedef FieldDidUpdateCallback = void Function(); -typedef CellDataMap = LinkedHashMap; -abstract class GridRowDataDelegate { +abstract class GridRowFieldDelegate { UnmodifiableListView get fields; - GridRow buildGridRow(RowOrder rowOrder); - CellDataMap buildCellDataMap(Row rowData); void onFieldChanged(FieldDidUpdateCallback callback); } class GridRowCache { final String gridId; - final RowsNotifier _rowNotifier; + final RowsNotifier _rowsNotifier; final GridRowListener _rowsListener; - final GridRowDataDelegate _dataDelegate; + final GridRowFieldDelegate _fieldDelegate; + List get clonedRows => _rowsNotifier.clonedRows; - List get clonedRows => _rowNotifier.clonedRows; - - GridRowCache({required this.gridId, required GridRowDataDelegate dataDelegate}) - : _rowNotifier = RowsNotifier(rowBuilder: dataDelegate.buildGridRow), + GridRowCache({required this.gridId, required GridRowFieldDelegate fieldDelegate}) + : _rowsNotifier = RowsNotifier( + rowBuilder: (rowOrder) { + return GridRow( + gridId: gridId, + fields: fieldDelegate.fields, + rowId: rowOrder.rowId, + height: rowOrder.height.toDouble(), + ); + }, + ), _rowsListener = GridRowListener(gridId: gridId), - _dataDelegate = dataDelegate { + _fieldDelegate = fieldDelegate { // - dataDelegate.onFieldChanged(() => _rowNotifier.fieldDidChange()); + fieldDelegate.onFieldChanged(() => _rowsNotifier.fieldDidChange()); // listen on the row update _rowsListener.rowsUpdateNotifier.addPublishListener((result) { result.fold( (changesets) { for (final changeset in changesets) { - _rowNotifier.deleteRows(changeset.deletedRows); - _rowNotifier.insertRows(changeset.insertedRows); - _rowNotifier.updateRows(changeset.updatedRows); + _rowsNotifier.deleteRows(changeset.deletedRows); + _rowsNotifier.insertRows(changeset.insertedRows); + _rowsNotifier.updateRows(changeset.updatedRows); } }, (err) => Log.error(err), @@ -55,14 +61,14 @@ class GridRowCache { Future dispose() async { await _rowsListener.stop(); - _rowNotifier.dispose(); + _rowsNotifier.dispose(); } void addListener({ void Function(List, GridRowChangeReason)? onChanged, bool Function()? listenWhen, }) { - _rowNotifier.addListener(() { + _rowsNotifier.addListener(() { if (onChanged == null) { return; } @@ -71,16 +77,16 @@ class GridRowCache { return; } - onChanged(clonedRows, _rowNotifier._changeReason); + onChanged(clonedRows, _rowsNotifier._changeReason); }); } RowUpdateCallback addRowListener({ required String rowId, - void Function(CellDataMap)? onUpdated, + void Function(GridCellMap)? onUpdated, bool Function()? listenWhen, }) { - listenrHandler() { + listenrHandler() async { if (onUpdated == null) { return; } @@ -90,14 +96,14 @@ class GridRowCache { } notify() { - final row = _rowNotifier.rowDataWithId(rowId); + final row = _rowsNotifier.rowDataWithId(rowId); if (row != null) { - final cellDataMap = _dataDelegate.buildCellDataMap(row); + final GridCellMap cellDataMap = _makeGridCells(rowId, row); onUpdated(cellDataMap); } } - _rowNotifier._changeReason.whenOrNull( + _rowsNotifier._changeReason.whenOrNull( update: (indexs) { if (indexs[rowId] != null) { notify(); @@ -107,36 +113,52 @@ class GridRowCache { ); } - _rowNotifier.addListener(listenrHandler); + _rowsNotifier.addListener(listenrHandler); return listenrHandler; } void removeRowListener(VoidCallback callback) { - _rowNotifier.removeListener(callback); + _rowsNotifier.removeListener(callback); } - Option loadCellData(String rowId) { - final Row? data = _rowNotifier.rowDataWithId(rowId); - if (data != null) { - return Some(_dataDelegate.buildCellDataMap(data)); + GridCellMap loadGridCells(String rowId) { + final Row? data = _rowsNotifier.rowDataWithId(rowId); + if (data == null) { + _loadRow(rowId); } + return _makeGridCells(rowId, data); + } + void resetRows(List blocks) { + final rowOrders = blocks.expand((block) => block.rowOrders).toList(); + _rowsNotifier.reset(rowOrders); + } + + Future _loadRow(String rowId) async { final payload = RowIdentifierPayload.create() ..gridId = gridId ..rowId = rowId; - GridEventGetRow(payload).send().then((result) { - result.fold( - (rowData) => _rowNotifier.rowData = rowData, - (err) => Log.error(err), - ); - }); - return none(); + final result = await GridEventGetRow(payload).send(); + result.fold( + (rowData) => _rowsNotifier.rowData = rowData, + (err) => Log.error(err), + ); } - void updateWithBlock(List blocks) { - final rowOrders = blocks.expand((block) => block.rowOrders).toList(); - _rowNotifier.reset(rowOrders); + GridCellMap _makeGridCells(String rowId, Row? row) { + var cellDataMap = GridCellMap.new(); + for (final field in _fieldDelegate.fields) { + if (field.visibility) { + cellDataMap[field.id] = GridCell( + rowId: rowId, + gridId: gridId, + cell: row?.cellByFieldId[field.id], + field: field, + ); + } + } + return cellDataMap; } } @@ -194,18 +216,18 @@ class RowsNotifier extends ChangeNotifier { _update(newRows, GridRowChangeReason.insert(insertIndexs)); } - void updateRows(List updatedRows) { + void updateRows(List updatedRows) { if (updatedRows.isEmpty) { return; } final UpdatedIndexs updatedIndexs = UpdatedIndexs(); final List newRows = clonedRows; - for (final rowOrder in updatedRows) { + for (final updatedRow in updatedRows) { + final rowOrder = updatedRow.rowOrder; final index = newRows.indexWhere((row) => row.rowId == rowOrder.rowId); if (index != -1) { - // Remove the old row data, the data will be filled if the loadRow method gets called. - _rowDataMap.remove(rowOrder.rowId); + _rowDataMap[rowOrder.rowId] = updatedRow.row; newRows.removeAt(index); newRows.insert(index, rowBuilder(rowOrder)); @@ -323,16 +345,6 @@ class GridRow with _$GridRow { }) = _GridRow; } -@freezed -class GridCell with _$GridCell { - const factory GridCell({ - required String gridId, - required String rowId, - required Field field, - Cell? cell, - }) = _GridCell; -} - typedef InsertedIndexs = List; typedef DeletedIndexs = List; typedef UpdatedIndexs = LinkedHashMap; 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 6e01d56b66..5a913137af 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 @@ -213,7 +213,7 @@ class _GridRowsState extends State<_GridRows> { key: _key, initialItemCount: context.read().state.rows.length, itemBuilder: (BuildContext context, int index, Animation animation) { - final rowData = context.read().state.rows[index]; + final GridRow rowData = context.read().state.rows[index]; return _renderRow(context, rowData, animation); }, ); @@ -227,11 +227,13 @@ class _GridRowsState extends State<_GridRows> { Animation animation, ) { final rowCache = context.read().rowCache; + final cellCache = context.read().cellCache; return SizeTransition( sizeFactor: animation, child: GridRowWidget( rowData: rowData, rowCache: rowCache, + cellCache: cellCache, key: ValueKey(rowData.rowId), ), ); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart index 015fea1c32..df72ba3ae1 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart @@ -1,4 +1,5 @@ -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; +import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType; import 'package:flutter/widgets.dart'; @@ -8,21 +9,52 @@ import 'number_cell.dart'; import 'selection_cell/selection_cell.dart'; import 'text_cell.dart'; -GridCellWidget buildGridCell(GridCell cellData, {GridCellStyle? style}) { - final key = ValueKey(cellData.field.id + cellData.rowId); - switch (cellData.field.fieldType) { +GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) { + final key = ValueKey(gridCell.rowId + gridCell.field.id); + + final cellContext = makeCellContext(gridCell, cellCache); + + switch (gridCell.field.fieldType) { case FieldType.Checkbox: - return CheckboxCell(cellData: cellData, key: key); + return CheckboxCell(cellContext: cellContext, key: key); case FieldType.DateTime: - return DateCell(cellData: cellData, key: key); + return DateCell(cellContext: cellContext, key: key); case FieldType.MultiSelect: - return MultiSelectCell(cellData: cellData, key: key); + return MultiSelectCell(cellContext: cellContext as GridSelectOptionCellContext, style: style, key: key); case FieldType.Number: - return NumberCell(cellData: cellData, key: key); + return NumberCell(cellContext: cellContext, key: key); case FieldType.RichText: - return GridTextCell(cellData: cellData, key: key, style: style); + return GridTextCell(cellContext: cellContext, style: style, key: key); case FieldType.SingleSelect: - return SingleSelectCell(cellData: cellData, key: key); + return SingleSelectCell(cellContext: cellContext as GridSelectOptionCellContext, style: style, key: key); + default: + throw UnimplementedError; + } +} + +GridCellContext makeCellContext(GridCell gridCell, GridCellCache cellCache) { + switch (gridCell.field.fieldType) { + case FieldType.Checkbox: + case FieldType.DateTime: + case FieldType.Number: + return GridDefaultCellContext( + gridCell: gridCell, + cellCache: cellCache, + cellDataLoader: DefaultCellDataLoader(gridCell: gridCell, reloadOnCellChanged: true), + ); + case FieldType.RichText: + return GridDefaultCellContext( + gridCell: gridCell, + cellCache: cellCache, + cellDataLoader: DefaultCellDataLoader(gridCell: gridCell), + ); + case FieldType.MultiSelect: + case FieldType.SingleSelect: + return GridSelectOptionCellContext( + gridCell: gridCell, + cellCache: cellCache, + cellDataLoader: SelectOptionCellDataLoader(gridCell: gridCell), + ); default: throw UnimplementedError; } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart index 608913d41d..991b56a832 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart @@ -7,10 +7,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; class CheckboxCell extends GridCellWidget { - final GridCell cellData; + final GridCellContext cellContext; CheckboxCell({ - required this.cellData, + required this.cellContext, Key? key, }) : super(key: key); @@ -23,7 +23,7 @@ class _CheckboxCellState extends State { @override void initState() { - _cellBloc = getIt(param1: widget.cellData)..add(const CheckboxCellEvent.initial()); + _cellBloc = getIt(param1: widget.cellContext)..add(const CheckboxCellEvent.initial()); super.initState(); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart index 84d60c797a..a3cc807266 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart @@ -14,10 +14,10 @@ abstract class GridCellDelegate { } class DateCell extends GridCellWidget { - final GridCell cellData; + final GridCellContext cellContext; DateCell({ - required this.cellData, + required this.cellContext, Key? key, }) : super(key: key); @@ -30,7 +30,7 @@ class _DateCellState extends State { @override void initState() { - _cellBloc = getIt(param1: widget.cellData)..add(const DateCellEvent.initial()); + _cellBloc = getIt(param1: widget.cellContext)..add(const DateCellEvent.initial()); super.initState(); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart index 2d98aa0bd6..168d2258d3 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart @@ -8,10 +8,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; class NumberCell extends GridCellWidget { - final GridCell cellData; + final GridCellContext cellContext; NumberCell({ - required this.cellData, + required this.cellContext, Key? key, }) : super(key: key); @@ -27,7 +27,7 @@ class _NumberCellState extends State { @override void initState() { - _cellBloc = getIt(param1: widget.cellData)..add(const NumberCellEvent.initial()); + _cellBloc = getIt(param1: widget.cellContext)..add(const NumberCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); _focusNode = FocusNode(); _focusNode.addListener(() { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart index 05dd66e224..939594f19c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart @@ -1,19 +1,39 @@ 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/widgets/cell/cell_builder.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +// ignore: unused_import +import 'package:flowy_sdk/log.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'extension.dart'; import 'selection_editor.dart'; +class SelectOptionCellStyle extends GridCellStyle { + String placeholder; + + SelectOptionCellStyle({ + required this.placeholder, + }); +} + class SingleSelectCell extends GridCellWidget { - final GridCell cellData; + final GridSelectOptionCellContext cellContext; + late final SelectOptionCellStyle? cellStyle; SingleSelectCell({ - required this.cellData, + required this.cellContext, + GridCellStyle? style, Key? key, - }) : super(key: key); + }) : super(key: key) { + if (style != null) { + cellStyle = (style as SelectOptionCellStyle); + } else { + cellStyle = null; + } + } @override State createState() => _SingleSelectCellState(); @@ -24,26 +44,32 @@ class _SingleSelectCellState extends State { @override void initState() { - _cellBloc = getIt(param1: widget.cellData)..add(const SelectionCellEvent.initial()); + // Log.trace("init widget $hashCode"); + _cellBloc = getIt(param1: widget.cellContext)..add(const SelectionCellEvent.initial()); super.initState(); } @override Widget build(BuildContext context) { + final theme = context.watch(); + // Log.trace("build widget $hashCode"); return BlocProvider.value( value: _cellBloc, child: BlocBuilder( builder: (context, state) { - final children = state.selectedOptions.map((option) => SelectOptionTag(option: option)).toList(); + List children = []; + children.addAll(state.selectedOptions.map((option) => SelectOptionTag(option: option)).toList()); + + if (children.isEmpty && widget.cellStyle != null) { + children.add(FlowyText.medium(widget.cellStyle!.placeholder, fontSize: 14, color: theme.shader3)); + } return SizedBox.expand( child: InkWell( onTap: () { widget.onFocus.value = true; SelectOptionCellEditor.show( context, - state.cellData, - state.options, - state.selectedOptions, + widget.cellContext.clone(), () => widget.onFocus.value = false, ); }, @@ -55,8 +81,17 @@ class _SingleSelectCellState extends State { ); } + @override + void didUpdateWidget(covariant SingleSelectCell oldWidget) { + if (oldWidget.cellContext != widget.cellContext) { + // Log.trace("did update widget $hashCode"); + } + super.didUpdateWidget(oldWidget); + } + @override Future dispose() async { + // Log.trace("dispose widget $hashCode"); _cellBloc.close(); super.dispose(); } @@ -64,12 +99,20 @@ class _SingleSelectCellState extends State { //---------------------------------------------------------------- class MultiSelectCell extends GridCellWidget { - final GridCell cellData; + final GridSelectOptionCellContext cellContext; + late final SelectOptionCellStyle? cellStyle; MultiSelectCell({ - required this.cellData, + required this.cellContext, + GridCellStyle? style, Key? key, - }) : super(key: key); + }) : super(key: key) { + if (style != null) { + cellStyle = (style as SelectOptionCellStyle); + } else { + cellStyle = null; + } + } @override State createState() => _MultiSelectCellState(); @@ -80,7 +123,7 @@ class _MultiSelectCellState extends State { @override void initState() { - _cellBloc = getIt(param1: widget.cellData)..add(const SelectionCellEvent.initial()); + _cellBloc = getIt(param1: widget.cellContext)..add(const SelectionCellEvent.initial()); super.initState(); } @@ -90,16 +133,19 @@ class _MultiSelectCellState extends State { value: _cellBloc, child: BlocBuilder( builder: (context, state) { - final children = state.selectedOptions.map((option) => SelectOptionTag(option: option)).toList(); + List children = state.selectedOptions.map((option) => SelectOptionTag(option: option)).toList(); + + if (children.isEmpty && widget.cellStyle != null) { + children.add(FlowyText.medium(widget.cellStyle!.placeholder, fontSize: 14)); + } + return SizedBox.expand( child: InkWell( onTap: () { widget.onFocus.value = true; SelectOptionCellEditor.show( context, - state.cellData, - state.options, - state.selectedOptions, + widget.cellContext, () => widget.onFocus.value = false, ); }, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart index d3fcd9efcf..0379bd55dd 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart @@ -1,6 +1,6 @@ import 'dart:collection'; +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; import 'package:app_flowy/workspace/application/grid/cell/selection_editor_bloc.dart'; -import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/widget.dart'; @@ -25,15 +25,11 @@ import 'text_field.dart'; const double _editorPannelWidth = 300; class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate { - final GridCell cellData; - final List options; - final List selectedOptions; + final GridSelectOptionCellContext cellContext; final VoidCallback onDismissed; const SelectOptionCellEditor({ - required this.cellData, - required this.options, - required this.selectedOptions, + required this.cellContext, required this.onDismissed, Key? key, }) : super(key: key); @@ -42,9 +38,7 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate { Widget build(BuildContext context) { return BlocProvider( create: (context) => SelectOptionEditorBloc( - cellData: cellData, - options: options, - selectedOptions: selectedOptions, + cellContext: cellContext, )..add(const SelectOptionEditorEvent.initial()), child: BlocBuilder( builder: (context, state) { @@ -66,16 +60,12 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate { static void show( BuildContext context, - GridCell cellData, - List options, - List selectedOptions, + GridSelectOptionCellContext cellContext, VoidCallback onDismissed, ) { SelectOptionCellEditor.remove(context); final editor = SelectOptionCellEditor( - cellData: cellData, - options: options, - selectedOptions: selectedOptions, + cellContext: cellContext, onDismissed: onDismissed, ); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart index 4c9c07697e..6d77f5f2cf 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart @@ -14,10 +14,10 @@ class GridTextCellStyle extends GridCellStyle { } class GridTextCell extends GridCellWidget { - final GridCell cellData; + final GridCellContext cellContext; late final GridTextCellStyle? cellStyle; GridTextCell({ - required this.cellData, + required this.cellContext, GridCellStyle? style, Key? key, }) : super(key: key) { @@ -41,7 +41,7 @@ class _GridTextCellState extends State { @override void initState() { - _cellBloc = getIt(param1: widget.cellData); + _cellBloc = getIt(param1: widget.cellContext); _cellBloc.add(const TextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); _focusNode = FocusNode(); 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 1201120f37..62cdc5c53f 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 @@ -8,17 +8,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'row_action_sheet.dart'; -import 'package:dartz/dartz.dart' show Option; import 'row_detail.dart'; class GridRowWidget extends StatefulWidget { final GridRow rowData; final GridRowCache rowCache; + final GridCellCache cellCache; const GridRowWidget({ required this.rowData, required this.rowCache, + required this.cellCache, Key? key, }) : super(key: key); @@ -49,7 +50,7 @@ class _GridRowWidgetState extends State { builder: (context, state) { final children = [ const _RowLeading(), - _RowCells(onExpand: () => onExpandCell(context)), + _RowCells(cellCache: widget.cellCache, onExpand: () => onExpandCell(context)), const _RowTrailing(), ]; @@ -73,7 +74,11 @@ class _GridRowWidgetState extends State { } void onExpandCell(BuildContext context) { - final page = RowDetailPage(rowData: widget.rowData, rowCache: widget.rowCache); + final page = RowDetailPage( + rowData: widget.rowData, + rowCache: widget.rowCache, + cellCache: widget.cellCache, + ); page.show(context); } } @@ -147,13 +152,14 @@ class _DeleteRowButton extends StatelessWidget { } class _RowCells extends StatelessWidget { + final GridCellCache cellCache; final VoidCallback onExpand; - const _RowCells({required this.onExpand, Key? key}) : super(key: key); + const _RowCells({required this.cellCache, required this.onExpand, Key? key}) : super(key: key); @override Widget build(BuildContext context) { return BlocBuilder( - buildWhen: (previous, current) => previous.cellDataMap != current.cellDataMap, + buildWhen: (previous, current) => previous.cellDataMap.length != current.cellDataMap.length, builder: (context, state) { return Row( mainAxisSize: MainAxisSize.min, @@ -164,24 +170,21 @@ class _RowCells extends StatelessWidget { ); } - List _makeCells(Option data) { - return data.fold( - () => [], - (cellDataMap) => cellDataMap.values.map( - (cellData) { - Widget? expander; - if (cellData.field.isPrimary) { - expander = _CellExpander(onExpand: onExpand); - } + List _makeCells(GridCellMap gridCellMap) { + return gridCellMap.values.map( + (gridCell) { + Widget? expander; + if (gridCell.field.isPrimary) { + expander = _CellExpander(onExpand: onExpand); + } - return CellContainer( - width: cellData.field.width.toDouble(), - child: buildGridCell(cellData), - expander: expander, - ); - }, - ).toList(), - ); + return CellContainer( + width: gridCell.field.width.toDouble(), + child: buildGridCellWidget(gridCell, cellCache), + expander: expander, + ); + }, + ).toList(); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart index 4ba31f783b..ae82033320 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart @@ -1,12 +1,15 @@ +import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart'; import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart'; import 'package:app_flowy/workspace/application/grid/row/row_service.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'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; +import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType; import 'package:easy_localization/easy_localization.dart'; @@ -18,10 +21,12 @@ import 'package:window_size/window_size.dart'; class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate { final GridRow rowData; final GridRowCache rowCache; + final GridCellCache cellCache; const RowDetailPage({ required this.rowData, required this.rowCache, + required this.cellCache, Key? key, }) : super(key: key); @@ -59,32 +64,45 @@ class _RowDetailPageState extends State { bloc.add(const RowDetailEvent.initial()); return bloc; }, - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 80, vertical: 40), - child: _PropertyList(), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 80, vertical: 40), + child: _PropertyList(cellCache: widget.cellCache), ), ); } } class _PropertyList extends StatelessWidget { - const _PropertyList({ + final GridCellCache cellCache; + final ScrollController _scrollController; + _PropertyList({ + required this.cellCache, Key? key, - }) : super(key: key); + }) : _scrollController = ScrollController(), + super(key: key); @override Widget build(BuildContext context) { return BlocBuilder( - buildWhen: (previous, current) => previous.cellDatas != current.cellDatas, + buildWhen: (previous, current) => previous.gridCells != current.gridCells, builder: (context, state) { - return ListView.separated( - itemCount: state.cellDatas.length, - itemBuilder: (BuildContext context, int index) { - return _RowDetailCell(cellData: state.cellDatas[index]); - }, - separatorBuilder: (BuildContext context, int index) { - return const VSpace(2); - }, + return ScrollbarListStack( + axis: Axis.vertical, + controller: _scrollController, + barSize: GridSize.scrollBarSize, + child: ListView.separated( + controller: _scrollController, + itemCount: state.gridCells.length, + itemBuilder: (BuildContext context, int index) { + return _RowDetailCell( + gridCell: state.gridCells[index], + cellCache: cellCache, + ); + }, + separatorBuilder: (BuildContext context, int index) { + return const VSpace(2); + }, + ), ); }, ); @@ -92,15 +110,22 @@ class _PropertyList extends StatelessWidget { } class _RowDetailCell extends StatelessWidget { - final GridCell cellData; - const _RowDetailCell({required this.cellData, Key? key}) : super(key: key); + final GridCell gridCell; + final GridCellCache cellCache; + const _RowDetailCell({ + required this.gridCell, + required this.cellCache, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { final theme = context.watch(); - final cell = buildGridCell( - cellData, - style: _buildCellStyle(theme, cellData.field.fieldType), + + final cell = buildGridCellWidget( + gridCell, + cellCache, + style: _buildCellStyle(theme, gridCell.field.fieldType), ); return SizedBox( height: 36, @@ -110,7 +135,7 @@ class _RowDetailCell extends StatelessWidget { children: [ SizedBox( width: 150, - child: FieldCellButton(field: cellData.field, onTap: () => _showFieldEditor(context)), + child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)), ), const HSpace(10), Expanded( @@ -126,10 +151,10 @@ class _RowDetailCell extends StatelessWidget { void _showFieldEditor(BuildContext context) { FieldEditor( - gridId: cellData.gridId, + gridId: gridCell.gridId, fieldContextLoader: FieldContextLoaderAdaptor( - gridId: cellData.gridId, - field: cellData.field, + gridId: gridCell.gridId, + field: gridCell.field, ), ).show(context); } @@ -142,7 +167,9 @@ GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) { case FieldType.DateTime: return null; case FieldType.MultiSelect: - return null; + return SelectOptionCellStyle( + placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), + ); case FieldType.Number: return null; case FieldType.RichText: @@ -150,7 +177,9 @@ GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) { placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), ); case FieldType.SingleSelect: - return null; + return SelectOptionCellStyle( + placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), + ); default: return null; } diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text.dart index 55f29eba93..74cf7e4c31 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text.dart @@ -46,7 +46,7 @@ class FlowyText extends StatelessWidget { style: TextStyle( color: color ?? theme.textColor, fontWeight: fontWeight, - fontSize: fontSize + 2, + fontSize: fontSize, fontFamily: 'Mulish', )); } diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/log.dart b/frontend/app_flowy/packages/flowy_sdk/lib/log.dart index a34faaff0e..ad61b70c4f 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/log.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/log.dart @@ -31,7 +31,7 @@ class Log { } static void trace(dynamic msg) { - Log.shared._logger.d(msg); + Log.shared._logger.v(msg); } static void error(dynamic msg) { diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart index e64431b1f9..30324be776 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart @@ -1081,12 +1081,77 @@ class IndexRowOrder extends $pb.GeneratedMessage { void clearIndex() => clearField(2); } +class UpdatedRowOrder extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdatedRowOrder', createEmptyInstance: create) + ..aOM(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowOrder', subBuilder: RowOrder.create) + ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'row', subBuilder: Row.create) + ..hasRequiredFields = false + ; + + UpdatedRowOrder._() : super(); + factory UpdatedRowOrder({ + RowOrder? rowOrder, + Row? row, + }) { + final _result = create(); + if (rowOrder != null) { + _result.rowOrder = rowOrder; + } + if (row != null) { + _result.row = row; + } + return _result; + } + factory UpdatedRowOrder.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UpdatedRowOrder.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + UpdatedRowOrder clone() => UpdatedRowOrder()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + UpdatedRowOrder copyWith(void Function(UpdatedRowOrder) updates) => super.copyWith((message) => updates(message as UpdatedRowOrder)) as UpdatedRowOrder; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static UpdatedRowOrder create() => UpdatedRowOrder._(); + UpdatedRowOrder createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static UpdatedRowOrder getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static UpdatedRowOrder? _defaultInstance; + + @$pb.TagNumber(1) + RowOrder get rowOrder => $_getN(0); + @$pb.TagNumber(1) + set rowOrder(RowOrder v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasRowOrder() => $_has(0); + @$pb.TagNumber(1) + void clearRowOrder() => clearField(1); + @$pb.TagNumber(1) + RowOrder ensureRowOrder() => $_ensure(0); + + @$pb.TagNumber(2) + Row get row => $_getN(1); + @$pb.TagNumber(2) + set row(Row v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasRow() => $_has(1); + @$pb.TagNumber(2) + void clearRow() => clearField(2); + @$pb.TagNumber(2) + Row ensureRow() => $_ensure(1); +} + class GridRowsChangeset extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridRowsChangeset', createEmptyInstance: create) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId') ..pc(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedRows', $pb.PbFieldType.PM, subBuilder: IndexRowOrder.create) ..pc(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create) - ..pc(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create) + ..pc(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedRows', $pb.PbFieldType.PM, subBuilder: UpdatedRowOrder.create) ..hasRequiredFields = false ; @@ -1095,7 +1160,7 @@ class GridRowsChangeset extends $pb.GeneratedMessage { $core.String? blockId, $core.Iterable? insertedRows, $core.Iterable? deletedRows, - $core.Iterable? updatedRows, + $core.Iterable? updatedRows, }) { final _result = create(); if (blockId != null) { @@ -1149,7 +1214,7 @@ class GridRowsChangeset extends $pb.GeneratedMessage { $core.List get deletedRows => $_getList(2); @$pb.TagNumber(4) - $core.List get updatedRows => $_getList(3); + $core.List get updatedRows => $_getList(3); } class GridBlock extends $pb.GeneratedMessage { @@ -1268,108 +1333,6 @@ class Cell extends $pb.GeneratedMessage { void clearContent() => clearField(2); } -enum CellNotificationData_OneOfContent { - content, - notSet -} - -class CellNotificationData extends $pb.GeneratedMessage { - static const $core.Map<$core.int, CellNotificationData_OneOfContent> _CellNotificationData_OneOfContentByTag = { - 4 : CellNotificationData_OneOfContent.content, - 0 : CellNotificationData_OneOfContent.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CellNotificationData', createEmptyInstance: create) - ..oo(0, [4]) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId') - ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId') - ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId') - ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') - ..hasRequiredFields = false - ; - - CellNotificationData._() : super(); - factory CellNotificationData({ - $core.String? gridId, - $core.String? fieldId, - $core.String? rowId, - $core.String? content, - }) { - final _result = create(); - if (gridId != null) { - _result.gridId = gridId; - } - if (fieldId != null) { - _result.fieldId = fieldId; - } - if (rowId != null) { - _result.rowId = rowId; - } - if (content != null) { - _result.content = content; - } - return _result; - } - factory CellNotificationData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory CellNotificationData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - CellNotificationData clone() => CellNotificationData()..mergeFromMessage(this); - @$core.Deprecated( - 'Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CellNotificationData copyWith(void Function(CellNotificationData) updates) => super.copyWith((message) => updates(message as CellNotificationData)) as CellNotificationData; // ignore: deprecated_member_use - $pb.BuilderInfo get info_ => _i; - @$core.pragma('dart2js:noInline') - static CellNotificationData create() => CellNotificationData._(); - CellNotificationData createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static CellNotificationData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static CellNotificationData? _defaultInstance; - - CellNotificationData_OneOfContent whichOneOfContent() => _CellNotificationData_OneOfContentByTag[$_whichOneof(0)]!; - void clearOneOfContent() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - $core.String get gridId => $_getSZ(0); - @$pb.TagNumber(1) - set gridId($core.String v) { $_setString(0, v); } - @$pb.TagNumber(1) - $core.bool hasGridId() => $_has(0); - @$pb.TagNumber(1) - void clearGridId() => clearField(1); - - @$pb.TagNumber(2) - $core.String get fieldId => $_getSZ(1); - @$pb.TagNumber(2) - set fieldId($core.String v) { $_setString(1, v); } - @$pb.TagNumber(2) - $core.bool hasFieldId() => $_has(1); - @$pb.TagNumber(2) - void clearFieldId() => clearField(2); - - @$pb.TagNumber(3) - $core.String get rowId => $_getSZ(2); - @$pb.TagNumber(3) - set rowId($core.String v) { $_setString(2, v); } - @$pb.TagNumber(3) - $core.bool hasRowId() => $_has(2); - @$pb.TagNumber(3) - void clearRowId() => clearField(3); - - @$pb.TagNumber(4) - $core.String get content => $_getSZ(3); - @$pb.TagNumber(4) - set content($core.String v) { $_setString(3, v); } - @$pb.TagNumber(4) - $core.bool hasContent() => $_has(3); - @$pb.TagNumber(4) - void clearContent() => clearField(4); -} - class RepeatedCell extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedCell', createEmptyInstance: create) ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Cell.create) diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart index 722a8ebcd6..8ec90dddf9 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart @@ -236,6 +236,17 @@ const IndexRowOrder$json = const { /// Descriptor for `IndexRowOrder`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List indexRowOrderDescriptor = $convert.base64Decode('Cg1JbmRleFJvd09yZGVyEiYKCXJvd19vcmRlchgBIAEoCzIJLlJvd09yZGVyUghyb3dPcmRlchIWCgVpbmRleBgCIAEoBUgAUgVpbmRleEIOCgxvbmVfb2ZfaW5kZXg='); +@$core.Deprecated('Use updatedRowOrderDescriptor instead') +const UpdatedRowOrder$json = const { + '1': 'UpdatedRowOrder', + '2': const [ + const {'1': 'row_order', '3': 1, '4': 1, '5': 11, '6': '.RowOrder', '10': 'rowOrder'}, + const {'1': 'row', '3': 2, '4': 1, '5': 11, '6': '.Row', '10': 'row'}, + ], +}; + +/// Descriptor for `UpdatedRowOrder`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List updatedRowOrderDescriptor = $convert.base64Decode('Cg9VcGRhdGVkUm93T3JkZXISJgoJcm93X29yZGVyGAEgASgLMgkuUm93T3JkZXJSCHJvd09yZGVyEhYKA3JvdxgCIAEoCzIELlJvd1IDcm93'); @$core.Deprecated('Use gridRowsChangesetDescriptor instead') const GridRowsChangeset$json = const { '1': 'GridRowsChangeset', @@ -243,12 +254,12 @@ const GridRowsChangeset$json = const { const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'}, const {'1': 'inserted_rows', '3': 2, '4': 3, '5': 11, '6': '.IndexRowOrder', '10': 'insertedRows'}, const {'1': 'deleted_rows', '3': 3, '4': 3, '5': 11, '6': '.RowOrder', '10': 'deletedRows'}, - const {'1': 'updated_rows', '3': 4, '4': 3, '5': 11, '6': '.RowOrder', '10': 'updatedRows'}, + const {'1': 'updated_rows', '3': 4, '4': 3, '5': 11, '6': '.UpdatedRowOrder', '10': 'updatedRows'}, ], }; /// Descriptor for `GridRowsChangeset`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List gridRowsChangesetDescriptor = $convert.base64Decode('ChFHcmlkUm93c0NoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIzCg1pbnNlcnRlZF9yb3dzGAIgAygLMg4uSW5kZXhSb3dPcmRlclIMaW5zZXJ0ZWRSb3dzEiwKDGRlbGV0ZWRfcm93cxgDIAMoCzIJLlJvd09yZGVyUgtkZWxldGVkUm93cxIsCgx1cGRhdGVkX3Jvd3MYBCADKAsyCS5Sb3dPcmRlclILdXBkYXRlZFJvd3M='); +final $typed_data.Uint8List gridRowsChangesetDescriptor = $convert.base64Decode('ChFHcmlkUm93c0NoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIzCg1pbnNlcnRlZF9yb3dzGAIgAygLMg4uSW5kZXhSb3dPcmRlclIMaW5zZXJ0ZWRSb3dzEiwKDGRlbGV0ZWRfcm93cxgDIAMoCzIJLlJvd09yZGVyUgtkZWxldGVkUm93cxIzCgx1cGRhdGVkX3Jvd3MYBCADKAsyEC5VcGRhdGVkUm93T3JkZXJSC3VwZGF0ZWRSb3dz'); @$core.Deprecated('Use gridBlockDescriptor instead') const GridBlock$json = const { '1': 'GridBlock', @@ -271,22 +282,6 @@ const Cell$json = const { /// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQ='); -@$core.Deprecated('Use cellNotificationDataDescriptor instead') -const CellNotificationData$json = const { - '1': 'CellNotificationData', - '2': const [ - const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'}, - const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '10': 'fieldId'}, - const {'1': 'row_id', '3': 3, '4': 1, '5': 9, '10': 'rowId'}, - const {'1': 'content', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'content'}, - ], - '8': const [ - const {'1': 'one_of_content'}, - ], -}; - -/// Descriptor for `CellNotificationData`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List cellNotificationDataDescriptor = $convert.base64Decode('ChRDZWxsTm90aWZpY2F0aW9uRGF0YRIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSGQoIZmllbGRfaWQYAiABKAlSB2ZpZWxkSWQSFQoGcm93X2lkGAMgASgJUgVyb3dJZBIaCgdjb250ZW50GAQgASgJSABSB2NvbnRlbnRCEAoOb25lX29mX2NvbnRlbnQ='); @$core.Deprecated('Use repeatedCellDescriptor instead') const RepeatedCell$json = const { '1': 'RepeatedCell', diff --git a/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs b/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs index b4d8bccbe5..ce7c5c8e8d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs @@ -96,6 +96,12 @@ impl ClientGridBlockMetaEditor { Ok(()) } + pub async fn get_row_meta(&self, row_id: &str) -> FlowyResult>> { + let row_ids = vec![Cow::Borrowed(row_id)]; + let row_meta = self.get_row_metas(Some(row_ids)).await?.pop(); + Ok(row_meta) + } + pub async fn get_row_metas(&self, row_ids: Option>>) -> FlowyResult>> where T: AsRef + ToOwned + ?Sized, @@ -113,6 +119,11 @@ impl ClientGridBlockMetaEditor { Ok(cell_metas) } + pub async fn get_row_order(&self, row_id: &str) -> FlowyResult> { + let row_ids = Some(vec![Cow::Borrowed(row_id)]); + Ok(self.get_row_orders(row_ids).await?.pop()) + } + pub async fn get_row_orders(&self, row_ids: Option>>) -> FlowyResult> where T: AsRef + ToOwned + ?Sized, diff --git a/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs index 5e0927795b..229afe75a2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs @@ -3,16 +3,15 @@ use crate::manager::GridUser; use crate::services::block_meta_editor::ClientGridBlockMetaEditor; use crate::services::persistence::block_index::BlockIndexPersistence; use crate::services::row::{group_row_orders, GridBlockSnapshot}; -use std::borrow::Cow; - use dashmap::DashMap; use flowy_error::FlowyResult; use flowy_grid_data_model::entities::{ - CellChangeset, CellMeta, CellNotificationData, GridBlockMeta, GridBlockMetaChangeset, GridRowsChangeset, - IndexRowOrder, RowMeta, RowMetaChangeset, RowOrder, + CellChangeset, CellMeta, GridBlockMeta, GridBlockMetaChangeset, GridRowsChangeset, IndexRowOrder, Row, RowMeta, + RowMetaChangeset, RowOrder, UpdatedRowOrder, }; use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence; use flowy_revision::{RevisionManager, RevisionPersistence}; +use std::borrow::Cow; use std::collections::HashMap; use std::sync::Arc; @@ -108,10 +107,22 @@ impl GridBlockMetaEditorManager { Ok(changesets) } - pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> { + pub async fn update_row(&self, changeset: RowMetaChangeset, row_builder: F) -> FlowyResult<()> + where + F: FnOnce(Arc) -> Option, + { let editor = self.get_editor_from_row_id(&changeset.row_id).await?; let _ = editor.update_row(changeset.clone()).await?; - let _ = self.notify_did_update_block_row(&changeset.row_id).await?; + match editor.get_row_meta(&changeset.row_id).await? { + None => tracing::error!("Internal error: can't find the row with id: {}", changeset.row_id), + Some(row_meta) => { + if let Some(row) = row_builder(row_meta.clone()) { + let row_order = UpdatedRowOrder::new(&row_meta, row); + let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]); + let _ = self.notify_did_update_block(block_order_changeset).await?; + } + } + } Ok(()) } @@ -119,11 +130,15 @@ impl GridBlockMetaEditorManager { let row_id = row_id.to_owned(); let block_id = self.persistence.get_block_id(&row_id)?; let editor = self.get_editor(&block_id).await?; - let row_orders = editor.get_row_orders(Some(vec![Cow::Borrowed(&row_id)])).await?; - let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?; - let _ = self - .notify_did_update_block(GridRowsChangeset::delete(&block_id, row_orders)) - .await?; + match editor.get_row_order(&row_id).await? { + None => {} + Some(row_order) => { + let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?; + let _ = self + .notify_did_update_block(GridRowsChangeset::delete(&block_id, vec![row_order])) + .await?; + } + } Ok(()) } @@ -171,18 +186,13 @@ impl GridBlockMetaEditorManager { Ok(()) } - pub async fn update_cell(&self, changeset: CellChangeset) -> FlowyResult<()> { + pub async fn update_cell(&self, changeset: CellChangeset, row_builder: F) -> FlowyResult<()> + where + F: FnOnce(Arc) -> Option, + { let row_changeset: RowMetaChangeset = changeset.clone().into(); - let _ = self.update_row(row_changeset).await?; - - let cell_notification_data = CellNotificationData { - grid_id: changeset.grid_id, - field_id: changeset.field_id, - row_id: changeset.row_id, - content: changeset.data, - }; - self.notify_did_update_cell(cell_notification_data).await?; - + let _ = self.update_row(row_changeset, row_builder).await?; + self.notify_did_update_cell(changeset).await?; Ok(()) } @@ -229,20 +239,6 @@ impl GridBlockMetaEditorManager { Ok(block_cell_metas) } - async fn notify_did_update_block_row(&self, row_id: &str) -> FlowyResult<()> { - let editor = self.get_editor_from_row_id(row_id).await?; - let row_ids = Some(vec![Cow::Borrowed(&row_id)]); - match editor.get_row_orders(row_ids).await?.pop() { - None => {} - Some(row_order) => { - let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]); - let _ = self.notify_did_update_block(block_order_changeset).await?; - } - } - - Ok(()) - } - async fn notify_did_update_block(&self, changeset: GridRowsChangeset) -> FlowyResult<()> { send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridRow) .payload(changeset) @@ -250,11 +246,9 @@ impl GridBlockMetaEditorManager { Ok(()) } - async fn notify_did_update_cell(&self, data: CellNotificationData) -> FlowyResult<()> { - let id = format!("{}:{}", data.row_id, data.field_id); - send_dart_notification(&id, GridNotification::DidUpdateCell) - .payload(data) - .send(); + async fn notify_did_update_cell(&self, changeset: CellChangeset) -> FlowyResult<()> { + let id = format!("{}:{}", changeset.row_id, changeset.field_id); + send_dart_notification(&id, GridNotification::DidUpdateCell).send(); Ok(()) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 8f30c0a7cd..d8d909cabe 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -247,7 +247,10 @@ impl ClientGridEditor { } pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> { - self.block_meta_manager.update_row(changeset).await + let field_metas = self.get_field_metas::(None).await?; + self.block_meta_manager + .update_row(changeset, |row_meta| make_row_from_row_meta(&field_metas, row_meta)) + .await } pub async fn get_rows(&self, block_id: &str) -> FlowyResult { @@ -322,7 +325,11 @@ impl ClientGridEditor { Some((_, field_meta)) => { // Update the changeset.data property with the return value. changeset.data = Some(apply_cell_data_changeset(cell_data_changeset, cell_meta, field_meta)?); - let _ = self.block_meta_manager.update_cell(changeset).await?; + let field_metas = self.get_field_metas::(None).await?; + let _ = self + .block_meta_manager + .update_cell(changeset, |row_meta| make_row_from_row_meta(&field_metas, row_meta)) + .await?; Ok(()) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs b/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs index 770ba758e9..93b40f4d51 100644 --- a/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs +++ b/frontend/rust-lib/flowy-grid/src/services/row/row_loader.rs @@ -47,6 +47,10 @@ pub(crate) fn make_row_orders_from_row_metas(row_metas: &[Arc]) -> Vec< row_metas.iter().map(RowOrder::from).collect::>() } +pub(crate) fn make_row_from_row_meta(fields: &[FieldMeta], row_meta: Arc) -> Option { + make_rows_from_row_metas(fields, &[row_meta]).pop() +} + pub(crate) fn make_rows_from_row_metas(fields: &[FieldMeta], row_metas: &[Arc]) -> Vec { let field_meta_map = fields .iter() diff --git a/shared-lib/flowy-grid-data-model/src/entities/grid.rs b/shared-lib/flowy-grid-data-model/src/entities/grid.rs index d246fd490b..c2f6d3841b 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -352,7 +352,25 @@ pub struct IndexRowOrder { pub index: Option, } -#[derive(Debug, Clone, Default, ProtoBuf)] +#[derive(Debug, Default, ProtoBuf)] +pub struct UpdatedRowOrder { + #[pb(index = 1)] + pub row_order: RowOrder, + + #[pb(index = 2)] + pub row: Row, +} + +impl UpdatedRowOrder { + pub fn new(row_meta: &RowMeta, row: Row) -> Self { + Self { + row_order: RowOrder::from(row_meta), + row, + } + } +} + +#[derive(Debug, Default, ProtoBuf)] pub struct GridRowsChangeset { #[pb(index = 1)] pub block_id: String, @@ -364,7 +382,7 @@ pub struct GridRowsChangeset { pub deleted_rows: Vec, #[pb(index = 4)] - pub updated_rows: Vec, + pub updated_rows: Vec, } impl std::convert::From for IndexRowOrder { @@ -399,7 +417,7 @@ impl GridRowsChangeset { } } - pub fn update(block_id: &str, updated_rows: Vec) -> Self { + pub fn update(block_id: &str, updated_rows: Vec) -> Self { Self { block_id: block_id.to_owned(), inserted_rows: vec![], @@ -445,21 +463,6 @@ impl Cell { } } -#[derive(Debug, Clone, Default, ProtoBuf)] -pub struct CellNotificationData { - #[pb(index = 1)] - pub grid_id: String, - - #[pb(index = 2)] - pub field_id: String, - - #[pb(index = 3)] - pub row_id: String, - - #[pb(index = 4, one_of)] - pub content: Option, -} - #[derive(Debug, Default, ProtoBuf)] pub struct RepeatedCell { #[pb(index = 1)] diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index 754fc11386..ccf489b8fd 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -3701,13 +3701,244 @@ impl ::protobuf::reflect::ProtobufValue for IndexRowOrder { } } +#[derive(PartialEq,Clone,Default)] +pub struct UpdatedRowOrder { + // message fields + pub row_order: ::protobuf::SingularPtrField, + pub row: ::protobuf::SingularPtrField, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a UpdatedRowOrder { + fn default() -> &'a UpdatedRowOrder { + ::default_instance() + } +} + +impl UpdatedRowOrder { + pub fn new() -> UpdatedRowOrder { + ::std::default::Default::default() + } + + // .RowOrder row_order = 1; + + + pub fn get_row_order(&self) -> &RowOrder { + self.row_order.as_ref().unwrap_or_else(|| ::default_instance()) + } + pub fn clear_row_order(&mut self) { + self.row_order.clear(); + } + + pub fn has_row_order(&self) -> bool { + self.row_order.is_some() + } + + // Param is passed by value, moved + pub fn set_row_order(&mut self, v: RowOrder) { + self.row_order = ::protobuf::SingularPtrField::some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_row_order(&mut self) -> &mut RowOrder { + if self.row_order.is_none() { + self.row_order.set_default(); + } + self.row_order.as_mut().unwrap() + } + + // Take field + pub fn take_row_order(&mut self) -> RowOrder { + self.row_order.take().unwrap_or_else(|| RowOrder::new()) + } + + // .Row row = 2; + + + pub fn get_row(&self) -> &Row { + self.row.as_ref().unwrap_or_else(|| ::default_instance()) + } + pub fn clear_row(&mut self) { + self.row.clear(); + } + + pub fn has_row(&self) -> bool { + self.row.is_some() + } + + // Param is passed by value, moved + pub fn set_row(&mut self, v: Row) { + self.row = ::protobuf::SingularPtrField::some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_row(&mut self) -> &mut Row { + if self.row.is_none() { + self.row.set_default(); + } + self.row.as_mut().unwrap() + } + + // Take field + pub fn take_row(&mut self) -> Row { + self.row.take().unwrap_or_else(|| Row::new()) + } +} + +impl ::protobuf::Message for UpdatedRowOrder { + fn is_initialized(&self) -> bool { + for v in &self.row_order { + if !v.is_initialized() { + return false; + } + }; + for v in &self.row { + if !v.is_initialized() { + return false; + } + }; + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.row_order)?; + }, + 2 => { + ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.row)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if let Some(ref v) = self.row_order.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; + } + if let Some(ref v) = self.row.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if let Some(ref v) = self.row_order.as_ref() { + os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?; + os.write_raw_varint32(v.get_cached_size())?; + v.write_to_with_cached_sizes(os)?; + } + if let Some(ref v) = self.row.as_ref() { + os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?; + os.write_raw_varint32(v.get_cached_size())?; + v.write_to_with_cached_sizes(os)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: ::std::boxed::Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> UpdatedRowOrder { + UpdatedRowOrder::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( + "row_order", + |m: &UpdatedRowOrder| { &m.row_order }, + |m: &mut UpdatedRowOrder| { &mut m.row_order }, + )); + fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( + "row", + |m: &UpdatedRowOrder| { &m.row }, + |m: &mut UpdatedRowOrder| { &mut m.row }, + )); + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "UpdatedRowOrder", + fields, + file_descriptor_proto() + ) + }) + } + + fn default_instance() -> &'static UpdatedRowOrder { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(UpdatedRowOrder::new) + } +} + +impl ::protobuf::Clear for UpdatedRowOrder { + fn clear(&mut self) { + self.row_order.clear(); + self.row.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for UpdatedRowOrder { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for UpdatedRowOrder { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Message(self) + } +} + #[derive(PartialEq,Clone,Default)] pub struct GridRowsChangeset { // message fields pub block_id: ::std::string::String, pub inserted_rows: ::protobuf::RepeatedField, pub deleted_rows: ::protobuf::RepeatedField, - pub updated_rows: ::protobuf::RepeatedField, + pub updated_rows: ::protobuf::RepeatedField, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -3800,10 +4031,10 @@ impl GridRowsChangeset { ::std::mem::replace(&mut self.deleted_rows, ::protobuf::RepeatedField::new()) } - // repeated .RowOrder updated_rows = 4; + // repeated .UpdatedRowOrder updated_rows = 4; - pub fn get_updated_rows(&self) -> &[RowOrder] { + pub fn get_updated_rows(&self) -> &[UpdatedRowOrder] { &self.updated_rows } pub fn clear_updated_rows(&mut self) { @@ -3811,17 +4042,17 @@ impl GridRowsChangeset { } // Param is passed by value, moved - pub fn set_updated_rows(&mut self, v: ::protobuf::RepeatedField) { + pub fn set_updated_rows(&mut self, v: ::protobuf::RepeatedField) { self.updated_rows = v; } // Mutable pointer to the field. - pub fn mut_updated_rows(&mut self) -> &mut ::protobuf::RepeatedField { + pub fn mut_updated_rows(&mut self) -> &mut ::protobuf::RepeatedField { &mut self.updated_rows } // Take field - pub fn take_updated_rows(&mut self) -> ::protobuf::RepeatedField { + pub fn take_updated_rows(&mut self) -> ::protobuf::RepeatedField { ::std::mem::replace(&mut self.updated_rows, ::protobuf::RepeatedField::new()) } } @@ -3966,7 +4197,7 @@ impl ::protobuf::Message for GridRowsChangeset { |m: &GridRowsChangeset| { &m.deleted_rows }, |m: &mut GridRowsChangeset| { &mut m.deleted_rows }, )); - fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( + fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "updated_rows", |m: &GridRowsChangeset| { &m.updated_rows }, |m: &mut GridRowsChangeset| { &mut m.updated_rows }, @@ -4416,331 +4647,6 @@ impl ::protobuf::reflect::ProtobufValue for Cell { } } -#[derive(PartialEq,Clone,Default)] -pub struct CellNotificationData { - // message fields - pub grid_id: ::std::string::String, - pub field_id: ::std::string::String, - pub row_id: ::std::string::String, - // message oneof groups - pub one_of_content: ::std::option::Option, - // special fields - pub unknown_fields: ::protobuf::UnknownFields, - pub cached_size: ::protobuf::CachedSize, -} - -impl<'a> ::std::default::Default for &'a CellNotificationData { - fn default() -> &'a CellNotificationData { - ::default_instance() - } -} - -#[derive(Clone,PartialEq,Debug)] -pub enum CellNotificationData_oneof_one_of_content { - content(::std::string::String), -} - -impl CellNotificationData { - pub fn new() -> CellNotificationData { - ::std::default::Default::default() - } - - // string grid_id = 1; - - - pub fn get_grid_id(&self) -> &str { - &self.grid_id - } - pub fn clear_grid_id(&mut self) { - self.grid_id.clear(); - } - - // Param is passed by value, moved - pub fn set_grid_id(&mut self, v: ::std::string::String) { - self.grid_id = v; - } - - // Mutable pointer to the field. - // If field is not initialized, it is initialized with default value first. - pub fn mut_grid_id(&mut self) -> &mut ::std::string::String { - &mut self.grid_id - } - - // Take field - pub fn take_grid_id(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.grid_id, ::std::string::String::new()) - } - - // string field_id = 2; - - - pub fn get_field_id(&self) -> &str { - &self.field_id - } - pub fn clear_field_id(&mut self) { - self.field_id.clear(); - } - - // Param is passed by value, moved - pub fn set_field_id(&mut self, v: ::std::string::String) { - self.field_id = v; - } - - // Mutable pointer to the field. - // If field is not initialized, it is initialized with default value first. - pub fn mut_field_id(&mut self) -> &mut ::std::string::String { - &mut self.field_id - } - - // Take field - pub fn take_field_id(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.field_id, ::std::string::String::new()) - } - - // string row_id = 3; - - - pub fn get_row_id(&self) -> &str { - &self.row_id - } - pub fn clear_row_id(&mut self) { - self.row_id.clear(); - } - - // Param is passed by value, moved - pub fn set_row_id(&mut self, v: ::std::string::String) { - self.row_id = v; - } - - // Mutable pointer to the field. - // If field is not initialized, it is initialized with default value first. - pub fn mut_row_id(&mut self) -> &mut ::std::string::String { - &mut self.row_id - } - - // Take field - pub fn take_row_id(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.row_id, ::std::string::String::new()) - } - - // string content = 4; - - - pub fn get_content(&self) -> &str { - match self.one_of_content { - ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(ref v)) => v, - _ => "", - } - } - pub fn clear_content(&mut self) { - self.one_of_content = ::std::option::Option::None; - } - - pub fn has_content(&self) -> bool { - match self.one_of_content { - ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(..)) => true, - _ => false, - } - } - - // Param is passed by value, moved - pub fn set_content(&mut self, v: ::std::string::String) { - self.one_of_content = ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(v)) - } - - // Mutable pointer to the field. - pub fn mut_content(&mut self) -> &mut ::std::string::String { - if let ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(_)) = self.one_of_content { - } else { - self.one_of_content = ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(::std::string::String::new())); - } - match self.one_of_content { - ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(ref mut v)) => v, - _ => panic!(), - } - } - - // Take field - pub fn take_content(&mut self) -> ::std::string::String { - if self.has_content() { - match self.one_of_content.take() { - ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(v)) => v, - _ => panic!(), - } - } else { - ::std::string::String::new() - } - } -} - -impl ::protobuf::Message for CellNotificationData { - fn is_initialized(&self) -> bool { - true - } - - fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { - while !is.eof()? { - let (field_number, wire_type) = is.read_tag_unpack()?; - match field_number { - 1 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?; - }, - 2 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; - }, - 3 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?; - }, - 4 => { - if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited { - return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); - } - self.one_of_content = ::std::option::Option::Some(CellNotificationData_oneof_one_of_content::content(is.read_string()?)); - }, - _ => { - ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; - }, - }; - } - ::std::result::Result::Ok(()) - } - - // Compute sizes of nested messages - #[allow(unused_variables)] - fn compute_size(&self) -> u32 { - let mut my_size = 0; - if !self.grid_id.is_empty() { - my_size += ::protobuf::rt::string_size(1, &self.grid_id); - } - if !self.field_id.is_empty() { - my_size += ::protobuf::rt::string_size(2, &self.field_id); - } - if !self.row_id.is_empty() { - my_size += ::protobuf::rt::string_size(3, &self.row_id); - } - if let ::std::option::Option::Some(ref v) = self.one_of_content { - match v { - &CellNotificationData_oneof_one_of_content::content(ref v) => { - my_size += ::protobuf::rt::string_size(4, &v); - }, - }; - } - my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); - self.cached_size.set(my_size); - my_size - } - - fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { - if !self.grid_id.is_empty() { - os.write_string(1, &self.grid_id)?; - } - if !self.field_id.is_empty() { - os.write_string(2, &self.field_id)?; - } - if !self.row_id.is_empty() { - os.write_string(3, &self.row_id)?; - } - if let ::std::option::Option::Some(ref v) = self.one_of_content { - match v { - &CellNotificationData_oneof_one_of_content::content(ref v) => { - os.write_string(4, v)?; - }, - }; - } - os.write_unknown_fields(self.get_unknown_fields())?; - ::std::result::Result::Ok(()) - } - - fn get_cached_size(&self) -> u32 { - self.cached_size.get() - } - - fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { - &self.unknown_fields - } - - fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { - &mut self.unknown_fields - } - - fn as_any(&self) -> &dyn (::std::any::Any) { - self as &dyn (::std::any::Any) - } - fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { - self as &mut dyn (::std::any::Any) - } - fn into_any(self: ::std::boxed::Box) -> ::std::boxed::Box { - self - } - - fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { - Self::descriptor_static() - } - - fn new() -> CellNotificationData { - CellNotificationData::new() - } - - fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; - descriptor.get(|| { - let mut fields = ::std::vec::Vec::new(); - fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( - "grid_id", - |m: &CellNotificationData| { &m.grid_id }, - |m: &mut CellNotificationData| { &mut m.grid_id }, - )); - fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( - "field_id", - |m: &CellNotificationData| { &m.field_id }, - |m: &mut CellNotificationData| { &mut m.field_id }, - )); - fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( - "row_id", - |m: &CellNotificationData| { &m.row_id }, - |m: &mut CellNotificationData| { &mut m.row_id }, - )); - fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>( - "content", - CellNotificationData::has_content, - CellNotificationData::get_content, - )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "CellNotificationData", - fields, - file_descriptor_proto() - ) - }) - } - - fn default_instance() -> &'static CellNotificationData { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(CellNotificationData::new) - } -} - -impl ::protobuf::Clear for CellNotificationData { - fn clear(&mut self) { - self.grid_id.clear(); - self.field_id.clear(); - self.row_id.clear(); - self.one_of_content = ::std::option::Option::None; - self.unknown_fields.clear(); - } -} - -impl ::std::fmt::Debug for CellNotificationData { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - ::protobuf::text_format::fmt(self, f) - } -} - -impl ::protobuf::reflect::ProtobufValue for CellNotificationData { - fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { - ::protobuf::reflect::ReflectValueRef::Message(self) - } -} - #[derive(PartialEq,Clone,Default)] pub struct RepeatedCell { // message fields @@ -7843,56 +7749,54 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x12(\n\nrow_orders\x18\ \x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\"_\n\rIndexRowOrder\x12&\n\tro\ w_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\n\x05index\ - \x18\x02\x20\x01(\x05H\0R\x05indexB\x0e\n\x0cone_of_index\"\xbf\x01\n\ - \x11GridRowsChangeset\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07block\ - Id\x123\n\rinserted_rows\x18\x02\x20\x03(\x0b2\x0e.IndexRowOrderR\x0cins\ - ertedRows\x12,\n\x0cdeleted_rows\x18\x03\x20\x03(\x0b2\t.RowOrderR\x0bde\ - letedRows\x12,\n\x0cupdated_rows\x18\x04\x20\x03(\x0b2\t.RowOrderR\x0bup\ - datedRows\"E\n\tGridBlock\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\ - \n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\x04Cell\ - \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07conte\ - nt\x18\x02\x20\x01(\tR\x07content\"\x8f\x01\n\x14CellNotificationData\ - \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_i\ - d\x18\x02\x20\x01(\tR\x07fieldId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\ - \x05rowId\x12\x1a\n\x07content\x18\x04\x20\x01(\tH\0R\x07contentB\x10\n\ - \x0eone_of_content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\ - \x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04nam\ - e\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\ - \x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\ - \x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\ - \x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0\ - R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12InsertFieldPa\ - yload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05fi\ - eld\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type_option_data\ - \x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0estart_field_id\x18\ - \x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_field_id\"d\n\ - \x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\ - \x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\ - \x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\ - \x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\ - \x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\x15FieldChangesetPayload\ - \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_\ - id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04name\x18\x03\x20\x01(\tH\0\ - R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x01R\x04desc\x12+\n\nfie\ - ld_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\tfieldType\x12\x18\n\x06\ - frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\x20\n\nvisibility\x18\ - \x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05width\x18\x08\x20\x01(\ - \x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\x01(\x0cH\x06R\ - \x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x13\n\x11one\ - _of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_visibilityB\x0e\n\ - \x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\x01\n\x0fMoveIt\ - emPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x17\n\ - \x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_index\x18\x03\ - \x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\x01(\x05R\ - \x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTypeR\x02ty\ - \"\x7f\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06grid\ - Id\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_i\ - d\x18\x03\x20\x01(\tR\x07fieldId\x12\x14\n\x04data\x18\x04\x20\x01(\tH\0\ - R\x04dataB\r\n\x0bone_of_data**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\ - \0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\ - \0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0c\ - SingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Check\ - box\x10\x05b\x06proto3\ + \x18\x02\x20\x01(\x05H\0R\x05indexB\x0e\n\x0cone_of_index\"Q\n\x0fUpdate\ + dRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\x08rowOrde\ + r\x12\x16\n\x03row\x18\x02\x20\x01(\x0b2\x04.RowR\x03row\"\xc6\x01\n\x11\ + GridRowsChangeset\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\ + \x123\n\rinserted_rows\x18\x02\x20\x03(\x0b2\x0e.IndexRowOrderR\x0cinser\ + tedRows\x12,\n\x0cdeleted_rows\x18\x03\x20\x03(\x0b2\t.RowOrderR\x0bdele\ + tedRows\x123\n\x0cupdated_rows\x18\x04\x20\x03(\x0b2\x10.UpdatedRowOrder\ + R\x0bupdatedRows\"E\n\tGridBlock\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02\ + id\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\ + \x04Cell\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\ + \x07content\x18\x02\x20\x01(\tR\x07content\"+\n\x0cRepeatedCell\x12\x1b\ + \n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridP\ + ayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\ + \x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"#\n\x0bGridBlockId\x12\ + \x14\n\x05value\x18\x01\x20\x01(\tR\x05value\"f\n\x10CreateRowPayload\ + \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\"\n\x0cstart_row\ + _id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\x13one_of_start_row_id\"\ + \xb6\x01\n\x12InsertFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\t\ + R\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\ + \x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\ + \n\x0estart_field_id\x18\x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15on\ + e_of_start_field_id\"d\n\x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\ + \x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\ + \x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\ + \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orde\ + rs\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\x0bblockOrders\"\xa8\x03\n\ + \x15FieldChangesetPayload\x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07f\ + ieldId\x12\x17\n\x07grid_id\x18\x02\x20\x01(\tR\x06gridId\x12\x14\n\x04n\ + ame\x18\x03\x20\x01(\tH\0R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\ + \x01R\x04desc\x12+\n\nfield_type\x18\x05\x20\x01(\x0e2\n.FieldTypeH\x02R\ + \tfieldType\x12\x18\n\x06frozen\x18\x06\x20\x01(\x08H\x03R\x06frozen\x12\ + \x20\n\nvisibility\x18\x07\x20\x01(\x08H\x04R\nvisibility\x12\x16\n\x05w\ + idth\x18\x08\x20\x01(\x05H\x05R\x05width\x12*\n\x10type_option_data\x18\ + \t\x20\x01(\x0cH\x06R\x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_\ + of_descB\x13\n\x11one_of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_\ + of_visibilityB\x0e\n\x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\ + \x9c\x01\n\x0fMoveItemPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\ + \x06gridId\x12\x17\n\x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\ + \nfrom_index\x18\x03\x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\ + \x04\x20\x01(\x05R\x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.Mo\ + veItemTypeR\x02ty\"\x7f\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\ + \x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\ + \x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x12\x14\n\x04data\ + \x18\x04\x20\x01(\tH\0R\x04dataB\r\n\x0bone_of_data**\n\x0cMoveItemType\ + \x12\r\n\tMoveField\x10\0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\ + \x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08Date\ + Time\x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\ + \x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index 2d6dbecfe4..5d72f77302 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -73,11 +73,15 @@ message IndexRowOrder { RowOrder row_order = 1; oneof one_of_index { int32 index = 2; }; } +message UpdatedRowOrder { + RowOrder row_order = 1; + Row row = 2; +} message GridRowsChangeset { string block_id = 1; repeated IndexRowOrder inserted_rows = 2; repeated RowOrder deleted_rows = 3; - repeated RowOrder updated_rows = 4; + repeated UpdatedRowOrder updated_rows = 4; } message GridBlock { string id = 1; @@ -87,12 +91,6 @@ message Cell { string field_id = 1; string content = 2; } -message CellNotificationData { - string grid_id = 1; - string field_id = 2; - string row_id = 3; - oneof one_of_content { string content = 4; }; -} message RepeatedCell { repeated Cell items = 1; }