Просмотр исходного кода

chore: replace row when row updated

appflowy 3 лет назад
Родитель
Сommit
0ac17fa6df

+ 8 - 8
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<CellNotificationData, FlowyError>;
+typedef UpdateFieldNotifiedValue = Either<Unit, FlowyError>;
 
 class CellListener {
   final String rowId;
   final String fieldId;
-  PublishNotifier<UpdateFieldNotifiedValue>? updateCellNotifier = PublishNotifier();
+  PublishNotifier<UpdateFieldNotifiedValue>? _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<void> stop() async {
     await _listener?.stop();
-    updateCellNotifier?.dispose();
-    updateCellNotifier = null;
+    _updateCellNotifier?.dispose();
+    _updateCellNotifier = null;
   }
 }

+ 18 - 16
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service.dart

@@ -22,20 +22,18 @@ class GridCellContext<T> {
   final GridCellCache cellCache;
   final GridCellCacheKey _cacheKey;
   final GridCellDataLoader<T> cellDataLoader;
-
-  final CellListener _cellListener;
   final CellService _cellService = CellService();
+
+  late final CellListener _cellListener;
   late final ValueNotifier<T?> _cellDataNotifier;
+  bool isListening = false;
   Timer? _delayOperation;
 
   GridCellContext({
     required this.gridCell,
     required this.cellCache,
     required this.cellDataLoader,
-  })  : _cellListener = CellListener(rowId: gridCell.rowId, fieldId: gridCell.field.id),
-        _cacheKey = GridCellCacheKey(objectId: gridCell.rowId, fieldId: gridCell.field.id) {
-    _cellDataNotifier = ValueNotifier(cellCache.get(cacheKey));
-  }
+  }) : _cacheKey = GridCellCacheKey(objectId: gridCell.rowId, fieldId: gridCell.field.id);
 
   String get gridId => gridCell.gridId;
 
@@ -52,16 +50,20 @@ class GridCellContext<T> {
   GridCellCacheKey get cacheKey => _cacheKey;
 
   void startListening({required void Function(T) onCellChanged}) {
-    _cellListener.updateCellNotifier?.addPublishListener((result) {
-      result.fold(
-        (notification) => _loadData(),
-        (err) => Log.error(err),
-      );
-    });
-    _cellListener.start();
+    if (!isListening) {
+      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) {
-      cellCache.addListener(cacheKey, () => reloadCellData());
+      if (cellDataLoader.reloadOnFieldChanged) {
+        cellCache.addListener(cacheKey, () => reloadCellData());
+      }
     }
 
     _cellDataNotifier.addListener(() {
@@ -171,7 +173,7 @@ class GridCellCache {
   final String gridId;
   final GridCellFieldDelegate fieldDelegate;
 
-  /// fieldId: {rowId: callback}
+  /// fieldId: {objectId: callback}
   final Map<String, Map<String, VoidCallback>> _cellListenerByFieldId = {};
 
   /// fieldId: {cacheKey: cacheData}

+ 5 - 3
frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart

@@ -45,13 +45,15 @@ class FieldCellBloc extends Bloc<FieldCellEvent, FieldCellState> {
   }
 
   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();
+    });
   }
 }
 

+ 7 - 6
frontend/app_flowy/lib/workspace/application/grid/field/field_listener.dart

@@ -11,12 +11,13 @@ typedef UpdateFieldNotifiedValue = Either<Field, FlowyError>;
 
 class SingleFieldListener {
   final String fieldId;
-  PublishNotifier<UpdateFieldNotifiedValue>? updateFieldNotifier = PublishNotifier();
+  PublishNotifier<UpdateFieldNotifiedValue>? _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<void> stop() async {
     await _listener?.stop();
-    updateFieldNotifier?.dispose();
-    updateFieldNotifier = null;
+    _updateFieldNotifier?.dispose();
+    _updateFieldNotifier = null;
   }
 }

+ 2 - 1
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,

+ 1 - 2
frontend/app_flowy/lib/workspace/application/grid/grid_service.dart

@@ -60,7 +60,7 @@ class GridFieldCache {
   final FieldsNotifier _fieldNotifier = FieldsNotifier();
   GridFieldCache({required this.gridId}) {
     _fieldListener = GridFieldsListener(gridId: gridId);
-    _fieldListener.updateFieldsNotifier?.addPublishListener((result) {
+    _fieldListener.start(onFieldsChanged: (result) {
       result.fold(
         (changeset) {
           _deleteFields(changeset.deletedFields);
@@ -70,7 +70,6 @@ class GridFieldCache {
         (err) => Log.error(err),
       );
     });
-    _fieldListener.start();
   }
 
   Future<void> dispose() async {

+ 4 - 4
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -216,18 +216,18 @@ class RowsNotifier extends ChangeNotifier {
     _update(newRows, GridRowChangeReason.insert(insertIndexs));
   }
 
-  void updateRows(List<RowOrder> updatedRows) {
+  void updateRows(List<UpdatedRowOrder> updatedRows) {
     if (updatedRows.isEmpty) {
       return;
     }
 
     final UpdatedIndexs updatedIndexs = UpdatedIndexs();
     final List<GridRow> 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));

+ 0 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart

@@ -3,7 +3,6 @@ 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';
-import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 68 - 105
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<RowOrder>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowOrder', subBuilder: RowOrder.create)
+    ..aOM<Row>(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<UpdatedRowOrder> createRepeated() => $pb.PbList<UpdatedRowOrder>();
+  @$core.pragma('dart2js:noInline')
+  static UpdatedRowOrder getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UpdatedRowOrder>(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<IndexRowOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedRows', $pb.PbFieldType.PM, subBuilder: IndexRowOrder.create)
     ..pc<RowOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
-    ..pc<RowOrder>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
+    ..pc<UpdatedRowOrder>(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<IndexRowOrder>? insertedRows,
     $core.Iterable<RowOrder>? deletedRows,
-    $core.Iterable<RowOrder>? updatedRows,
+    $core.Iterable<UpdatedRowOrder>? updatedRows,
   }) {
     final _result = create();
     if (blockId != null) {
@@ -1149,7 +1214,7 @@ class GridRowsChangeset extends $pb.GeneratedMessage {
   $core.List<RowOrder> get deletedRows => $_getList(2);
 
   @$pb.TagNumber(4)
-  $core.List<RowOrder> get updatedRows => $_getList(3);
+  $core.List<UpdatedRowOrder> 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<CellNotificationData> createRepeated() => $pb.PbList<CellNotificationData>();
-  @$core.pragma('dart2js:noInline')
-  static CellNotificationData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CellNotificationData>(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<Cell>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Cell.create)

+ 13 - 18
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',

+ 27 - 34
frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs

@@ -6,13 +6,14 @@ use crate::services::row::{group_row_orders, GridBlockSnapshot};
 use std::borrow::Cow;
 
 use dashmap::DashMap;
-use flowy_error::FlowyResult;
+use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{
-    CellChangeset, CellMeta, CellNotificationData, GridBlockMeta, GridBlockMetaChangeset, GridRowsChangeset,
-    IndexRowOrder, RowMeta, RowMetaChangeset, RowOrder,
+    Cell, CellChangeset, CellMeta, GridBlockMeta, GridBlockMetaChangeset, GridRowsChangeset, IndexRowOrder, Row,
+    RowMeta, RowMetaChangeset, RowOrder, UpdatedRowOrder,
 };
 use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
 use flowy_revision::{RevisionManager, RevisionPersistence};
+use lib_infra::future::FutureResult;
 use std::collections::HashMap;
 use std::sync::Arc;
 
@@ -108,10 +109,22 @@ impl GridBlockMetaEditorManager {
         Ok(changesets)
     }
 
-    pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
+    pub async fn update_row<F>(&self, changeset: RowMetaChangeset, row_builder: F) -> FlowyResult<()>
+    where
+        F: FnOnce(Arc<RowMeta>) -> Option<Row>,
+    {
         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(())
     }
 
@@ -175,18 +188,13 @@ impl GridBlockMetaEditorManager {
         Ok(())
     }
 
-    pub async fn update_cell(&self, changeset: CellChangeset) -> FlowyResult<()> {
+    pub async fn update_cell<F>(&self, changeset: CellChangeset, row_builder: F) -> FlowyResult<()>
+    where
+        F: FnOnce(Arc<RowMeta>) -> Option<Row>,
+    {
         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(())
     }
 
@@ -233,19 +241,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?;
-        match editor.get_row_order(row_id).await? {
-            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)
@@ -253,11 +248,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(())
     }
 }

+ 14 - 2
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::<FieldOrder>(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<RepeatedRow> {
@@ -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::<FieldOrder>(None).await?;
+                let _ = self
+                    .block_meta_manager
+                    .update_cell(changeset, |row_meta| make_row_from_row_meta(&field_metas, row_meta))
+                    .await?;
                 Ok(())
             }
         }
@@ -423,6 +430,11 @@ impl ClientGridEditor {
         self.grid_pad.read().await.delta_bytes()
     }
 
+    async fn row_builder(&self, row_meta: Arc<RowMeta>) -> FlowyResult<Option<Row>> {
+        let field_metas = self.get_field_metas::<FieldOrder>(None).await?;
+        Ok(make_rows_from_row_metas(&field_metas, &[row_meta]).pop())
+    }
+
     async fn modify<F>(&self, f: F) -> FlowyResult<()>
     where
         F: for<'a> FnOnce(&'a mut GridMetaPad) -> FlowyResult<Option<GridChangeset>>,

+ 4 - 0
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<RowMeta>]) -> Vec<
     row_metas.iter().map(RowOrder::from).collect::<Vec<_>>()
 }
 
+pub(crate) fn make_row_from_row_meta(fields: &[FieldMeta], row_meta: Arc<RowMeta>) -> Option<Row> {
+    make_rows_from_row_metas(fields, &vec![row_meta]).pop()
+}
+
 pub(crate) fn make_rows_from_row_metas(fields: &[FieldMeta], row_metas: &[Arc<RowMeta>]) -> Vec<Row> {
     let field_meta_map = fields
         .iter()

+ 21 - 18
shared-lib/flowy-grid-data-model/src/entities/grid.rs

@@ -352,7 +352,25 @@ pub struct IndexRowOrder {
     pub index: Option<i32>,
 }
 
-#[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<RowOrder>,
 
     #[pb(index = 4)]
-    pub updated_rows: Vec<RowOrder>,
+    pub updated_rows: Vec<UpdatedRowOrder>,
 }
 
 impl std::convert::From<RowOrder> for IndexRowOrder {
@@ -399,7 +417,7 @@ impl GridRowsChangeset {
         }
     }
 
-    pub fn update(block_id: &str, updated_rows: Vec<RowOrder>) -> Self {
+    pub fn update(block_id: &str, updated_rows: Vec<UpdatedRowOrder>) -> 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<String>,
-}
-
 #[derive(Debug, Default, ProtoBuf)]
 pub struct RepeatedCell {
     #[pb(index = 1)]

+ 286 - 382
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<RowOrder>,
+    pub row: ::protobuf::SingularPtrField<Row>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a UpdatedRowOrder {
+    fn default() -> &'a UpdatedRowOrder {
+        <UpdatedRowOrder as ::protobuf::Message>::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(|| <RowOrder as ::protobuf::Message>::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(|| <Row as ::protobuf::Message>::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<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        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<RowOrder>>(
+                "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>>(
+                "row",
+                |m: &UpdatedRowOrder| { &m.row },
+                |m: &mut UpdatedRowOrder| { &mut m.row },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdatedRowOrder>(
+                "UpdatedRowOrder",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static UpdatedRowOrder {
+        static instance: ::protobuf::rt::LazyV2<UpdatedRowOrder> = ::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<IndexRowOrder>,
     pub deleted_rows: ::protobuf::RepeatedField<RowOrder>,
-    pub updated_rows: ::protobuf::RepeatedField<RowOrder>,
+    pub updated_rows: ::protobuf::RepeatedField<UpdatedRowOrder>,
     // 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<RowOrder>) {
+    pub fn set_updated_rows(&mut self, v: ::protobuf::RepeatedField<UpdatedRowOrder>) {
         self.updated_rows = v;
     }
 
     // Mutable pointer to the field.
-    pub fn mut_updated_rows(&mut self) -> &mut ::protobuf::RepeatedField<RowOrder> {
+    pub fn mut_updated_rows(&mut self) -> &mut ::protobuf::RepeatedField<UpdatedRowOrder> {
         &mut self.updated_rows
     }
 
     // Take field
-    pub fn take_updated_rows(&mut self) -> ::protobuf::RepeatedField<RowOrder> {
+    pub fn take_updated_rows(&mut self) -> ::protobuf::RepeatedField<UpdatedRowOrder> {
         ::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<RowOrder>>(
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<UpdatedRowOrder>>(
                 "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<CellNotificationData_oneof_one_of_content>,
-    // special fields
-    pub unknown_fields: ::protobuf::UnknownFields,
-    pub cached_size: ::protobuf::CachedSize,
-}
-
-impl<'a> ::std::default::Default for &'a CellNotificationData {
-    fn default() -> &'a CellNotificationData {
-        <CellNotificationData as ::protobuf::Message>::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<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
-        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>(
-                "CellNotificationData",
-                fields,
-                file_descriptor_proto()
-            )
-        })
-    }
-
-    fn default_instance() -> &'static CellNotificationData {
-        static instance: ::protobuf::rt::LazyV2<CellNotificationData> = ::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;

+ 5 - 7
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;
 }