Bläddra i källkod

Merge pull request #554 from AppFlowy-IO/refactor/grid_rev

Refactor grid revision struct
Nathan.fooo 2 år sedan
förälder
incheckning
b4a5d007be
100 ändrade filer med 3119 tillägg och 1893 borttagningar
  1. 1 2
      frontend/app_flowy/analysis_options.yaml
  2. 3 0
      frontend/app_flowy/lib/plugin/plugin.dart
  3. 2 0
      frontend/app_flowy/lib/startup/tasks/load_plugin.dart
  4. 0 0
      frontend/app_flowy/lib/workspace/application/board/board_bloc.dart
  5. 1 0
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart
  6. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart
  7. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart
  8. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/field_cell_bloc.dart
  9. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart
  10. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/field_listener.dart
  11. 1 0
      frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart
  12. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart
  13. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart
  14. 1 1
      frontend/app_flowy/lib/workspace/application/grid/grid_header_bloc.dart
  15. 1 0
      frontend/app_flowy/lib/workspace/application/grid/grid_service.dart
  16. 1 1
      frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart
  17. 1 0
      frontend/app_flowy/lib/workspace/application/grid/row/row_listener.dart
  18. 1 0
      frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart
  19. 1 1
      frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart
  20. 1 4
      frontend/app_flowy/lib/workspace/application/view/view_ext.dart
  21. 4 1
      frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart
  22. 66 0
      frontend/app_flowy/lib/workspace/presentation/plugins/board/board.dart
  23. 17 0
      frontend/app_flowy/lib/workspace/presentation/plugins/board/src/board_page.dart
  24. 1 2
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart
  25. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart
  26. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart
  27. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart
  28. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_extension.dart
  29. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart
  30. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart
  31. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart
  32. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart
  33. 1 0
      frontend/app_flowy/packages/flowy_sdk/analysis_options.yaml
  34. 33 16
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-folder/dart_event.dart
  35. 2 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbenum.dart
  36. 2 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbjson.dart
  37. 1 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/protobuf.dart
  38. 61 133
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pb.dart
  39. 15 20
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart
  40. 148 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pb.dart
  41. 7 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pbenum.dart
  42. 26 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pbjson.dart
  43. 9 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pbserver.dart
  44. 7 5
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder/event_map.pbenum.dart
  45. 6 5
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder/event_map.pbjson.dart
  46. 1246 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pb.dart
  47. 36 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pbenum.dart
  48. 215 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pbjson.dart
  49. 9 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pbserver.dart
  50. 7 986
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart
  51. 0 25
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbenum.dart
  52. 0 205
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart
  53. 433 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pb.dart
  54. 7 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pbenum.dart
  55. 81 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pbjson.dart
  56. 9 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pbserver.dart
  57. 2 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/protobuf.dart
  58. 90 21
      frontend/rust-lib/Cargo.lock
  59. 2 0
      frontend/rust-lib/dart-ffi/Cargo.toml
  60. 0 0
      frontend/rust-lib/flowy-database/migrations/2021-07-09-063045_user/down.sql
  61. 0 0
      frontend/rust-lib/flowy-database/migrations/2021-07-09-063045_user/up.sql
  62. 0 0
      frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/down.sql
  63. 0 0
      frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/up.sql
  64. 0 2
      frontend/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/down.sql
  65. 0 7
      frontend/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql
  66. 0 0
      frontend/rust-lib/flowy-database/migrations/2021-09-22-074638_revision/down.sql
  67. 0 0
      frontend/rust-lib/flowy-database/migrations/2021-09-22-074638_revision/up.sql
  68. 0 2
      frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql
  69. 0 5
      frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql
  70. 1 0
      frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_grid/down.sql
  71. 4 0
      frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_grid/up.sql
  72. 0 0
      frontend/rust-lib/flowy-database/migrations/2022-04-05-015536_grid-block-index/down.sql
  73. 0 0
      frontend/rust-lib/flowy-database/migrations/2022-04-05-015536_grid-block-index/up.sql
  74. 1 0
      frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/down.sql
  75. 2 0
      frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/up.sql
  76. 1 9
      frontend/rust-lib/flowy-database/src/schema.rs
  77. 30 17
      frontend/rust-lib/flowy-folder/src/event_map.rs
  78. 15 9
      frontend/rust-lib/flowy-folder/src/manager.rs
  79. 19 16
      frontend/rust-lib/flowy-folder/src/protobuf/model/event_map.rs
  80. 5 4
      frontend/rust-lib/flowy-folder/src/protobuf/proto/event_map.proto
  81. 23 16
      frontend/rust-lib/flowy-folder/src/services/app/controller.rs
  82. 7 9
      frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs
  83. 5 7
      frontend/rust-lib/flowy-folder/src/services/folder_editor.rs
  84. 8 11
      frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs
  85. 12 17
      frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs
  86. 14 14
      frontend/rust-lib/flowy-folder/src/services/persistence/version_1/app_sql.rs
  87. 27 11
      frontend/rust-lib/flowy-folder/src/services/persistence/version_1/trash_sql.rs
  88. 41 47
      frontend/rust-lib/flowy-folder/src/services/persistence/version_1/v1_impl.rs
  89. 21 17
      frontend/rust-lib/flowy-folder/src/services/persistence/version_1/view_sql.rs
  90. 14 18
      frontend/rust-lib/flowy-folder/src/services/persistence/version_1/workspace_sql.rs
  91. 31 36
      frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs
  92. 33 25
      frontend/rust-lib/flowy-folder/src/services/trash/controller.rs
  93. 97 57
      frontend/rust-lib/flowy-folder/src/services/view/controller.rs
  94. 18 8
      frontend/rust-lib/flowy-folder/src/services/view/event_handler.rs
  95. 27 15
      frontend/rust-lib/flowy-folder/src/services/workspace/controller.rs
  96. 39 20
      frontend/rust-lib/flowy-folder/src/services/workspace/event_handler.rs
  97. 2 6
      frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs
  98. 17 14
      frontend/rust-lib/flowy-folder/tests/workspace/script.rs
  99. 2 1
      frontend/rust-lib/flowy-grid/Cargo.toml
  100. 30 29
      frontend/rust-lib/flowy-grid/src/event_handler.rs

+ 1 - 2
frontend/app_flowy/analysis_options.yaml

@@ -17,7 +17,6 @@ analyzer:
     - "packages/flowy_editor/**"
     - "packages/editor/**"
     # - "packages/flowy_infra_ui/**"
-    
 linter:
   # The lint rules applied to this project can be customized in the
   # section below to disable rules from the `package:flutter_lints/flutter.yaml`
@@ -38,4 +37,4 @@ linter:
 # https://dart.dev/guides/language/analysis-options
 
 errors:
-  invalid_annotation_target: ignore
+  invalid_annotation_target: ignore

+ 3 - 0
frontend/app_flowy/lib/plugin/plugin.dart

@@ -14,6 +14,7 @@ enum DefaultPlugin {
   blank,
   trash,
   grid,
+  board,
 }
 
 extension FlowyDefaultPluginExt on DefaultPlugin {
@@ -27,6 +28,8 @@ extension FlowyDefaultPluginExt on DefaultPlugin {
         return 2;
       case DefaultPlugin.grid:
         return 3;
+      case DefaultPlugin.board:
+        return 4;
     }
   }
 }

+ 2 - 0
frontend/app_flowy/lib/startup/tasks/load_plugin.dart

@@ -1,6 +1,7 @@
 import 'package:app_flowy/plugin/plugin.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/presentation/plugins/blank/blank.dart';
+import 'package:app_flowy/workspace/presentation/plugins/board/board.dart';
 import 'package:app_flowy/workspace/presentation/plugins/doc/document.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/grid.dart';
 import 'package:app_flowy/workspace/presentation/plugins/trash/trash.dart';
@@ -15,5 +16,6 @@ class PluginLoadTask extends LaunchTask {
     registerPlugin(builder: TrashPluginBuilder(), config: TrashPluginConfig());
     registerPlugin(builder: DocumentPluginBuilder());
     registerPlugin(builder: GridPluginBuilder(), config: GridPluginConfig());
+    registerPlugin(builder: BoardPluginBuilder(), config: BoardPluginConfig());
   }
 }

+ 0 - 0
frontend/app_flowy/lib/workspace/application/board/board_bloc.dart


+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart

@@ -6,6 +6,7 @@ import 'package:equatable/equatable.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/field.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/date_type_option.pb.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart

@@ -1,4 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart

@@ -1,5 +1,5 @@
 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-data-model/field.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

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

@@ -1,7 +1,7 @@
 import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'package:flowy_sdk/log.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart

@@ -1,4 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

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

@@ -1,6 +1,6 @@
 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-data-model/field.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
 import 'package:flowy_infra/notifier.dart';
 import 'dart:async';

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -2,6 +2,7 @@ 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/field.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/foundation.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart

@@ -1,5 +1,5 @@
 import 'package:dartz/dartz.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.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';

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart

@@ -4,7 +4,7 @@ import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.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-data-model/field.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';

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

@@ -1,6 +1,6 @@
 import 'package:app_flowy/workspace/application/grid/field/field_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-data-model/field.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

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

@@ -6,6 +6,7 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'cell/cell_service/cell_service.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart

@@ -1,7 +1,7 @@
 import 'dart:collection';
 import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
 import 'package:equatable/equatable.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/row/row_listener.dart

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
 import 'package:flowy_infra/notifier.dart';

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

@@ -5,6 +5,7 @@ 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/field.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 import 'package:flutter/foundation.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart

@@ -1,7 +1,7 @@
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'package:app_flowy/workspace/application/grid/grid_service.dart';
 import 'package:flowy_sdk/log.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';

+ 1 - 4
frontend/app_flowy/lib/workspace/application/view/view_ext.dart

@@ -34,10 +34,7 @@ extension FlowyPluginExtension on FlowyPlugin {
 
 extension ViewExtension on View {
   Widget renderThumbnail({Color? iconColor}) {
-    String thumbnail = this.thumbnail;
-    if (thumbnail.isEmpty) {
-      thumbnail = "file_icon";
-    }
+    String thumbnail = "file_icon";
 
     final Widget widget = svgWidget(thumbnail, color: iconColor);
     return widget;

+ 4 - 1
frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart

@@ -108,7 +108,10 @@ class _HomeScreenState extends State<HomeScreen> {
     final workspaceSetting = state.workspaceSetting;
     if (initialView == null && workspaceSetting.hasLatestView()) {
       initialView = workspaceSetting.latestView;
-      final plugin = makePlugin(pluginType: initialView!.pluginType, data: initialView);
+      final plugin = makePlugin(
+        pluginType: initialView!.pluginType,
+        data: initialView,
+      );
       getIt<HomeStackManager>().setPlugin(plugin);
     }
 

+ 66 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/board/board.dart

@@ -0,0 +1,66 @@
+import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
+import 'package:app_flowy/workspace/presentation/plugins/widgets/left_bar_item.dart';
+import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
+import 'package:app_flowy/plugin/plugin.dart';
+import 'package:flutter/material.dart';
+
+import 'src/board_page.dart';
+
+class BoardPluginBuilder implements PluginBuilder {
+  @override
+  Plugin build(dynamic data) {
+    if (data is View) {
+      return BoardPlugin(pluginType: pluginType, view: data);
+    } else {
+      throw FlowyPluginException.invalidData;
+    }
+  }
+
+  @override
+  String get menuName => "Board";
+
+  @override
+  PluginType get pluginType => DefaultPlugin.board.type();
+
+  @override
+  ViewDataType get dataType => ViewDataType.Grid;
+}
+
+class BoardPluginConfig implements PluginConfig {
+  @override
+  bool get creatable => false;
+}
+
+class BoardPlugin extends Plugin {
+  final View _view;
+  final PluginType _pluginType;
+
+  BoardPlugin({
+    required View view,
+    required PluginType pluginType,
+  })  : _pluginType = pluginType,
+        _view = view;
+
+  @override
+  PluginDisplay get display => GridPluginDisplay(view: _view);
+
+  @override
+  PluginId get id => _view.id;
+
+  @override
+  PluginType get ty => _pluginType;
+}
+
+class GridPluginDisplay extends PluginDisplay {
+  final View _view;
+  GridPluginDisplay({required View view, Key? key}) : _view = view;
+
+  @override
+  Widget get leftBarItem => ViewLeftBarItem(view: _view);
+
+  @override
+  Widget buildWidget() => BoardPage(view: _view);
+
+  @override
+  List<NavigationItem> get navigationItems => [this];
+}

+ 17 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/board/src/board_page.dart

@@ -0,0 +1,17 @@
+// ignore_for_file: unused_field
+
+import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
+import 'package:flutter/material.dart';
+
+class BoardPage extends StatelessWidget {
+  final View _view;
+
+  const BoardPage({required View view, Key? key})
+      : _view = view,
+        super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}

+ 1 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart

@@ -1,5 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
-
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'sizes.dart';
 
 class GridLayout {

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

@@ -1,5 +1,5 @@
 import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter/material.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart

@@ -6,7 +6,7 @@ import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'field_type_extension.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart

@@ -10,7 +10,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.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-data-model/field.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:app_flowy/workspace/application/grid/prelude.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_extension.dart

@@ -1,4 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:easy_localization/easy_localization.dart';
 

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart

@@ -6,7 +6,7 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
 import 'package:flowy_infra_ui/style_widget/text.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:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter/material.dart';
 import 'field_type_extension.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart

@@ -5,7 +5,7 @@ import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:reorderables/reorderables.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart

@@ -14,7 +14,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.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:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:flutter/material.dart';

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart

@@ -12,7 +12,7 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Field;
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/field.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:styled_widget/styled_widget.dart';

+ 1 - 0
frontend/app_flowy/packages/flowy_sdk/analysis_options.yaml

@@ -1,4 +1,5 @@
 analyzer:
   exclude:
     - "**/*.g.dart"
+    - "**/*.pb.dart"
     - "**/*.freezed.dart"

+ 33 - 16
frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-folder/dart_event.dart

@@ -253,44 +253,61 @@ class FolderEventDuplicateView {
     }
 }
 
-class FolderEventCopyLink {
-    FolderEventCopyLink();
+class FolderEventCloseView {
+     ViewId request;
+     FolderEventCloseView(this.request);
 
     Future<Either<Unit, FlowyError>> send() {
-     final request = FFIRequest.create()
-        ..event = FolderEvent.CopyLink.toString();
+    final request = FFIRequest.create()
+          ..event = FolderEvent.CloseView.toString()
+          ..payload = requestToBytes(this.request);
 
-     return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
-        (bytes) => left(unit),
-        (errBytes) => right(FlowyError.fromBuffer(errBytes)),
-      ));
+    return Dispatch.asyncRequest(request)
+        .then((bytesResult) => bytesResult.fold(
+           (bytes) => left(unit),
+           (errBytes) => right(FlowyError.fromBuffer(errBytes)),
+        ));
     }
 }
 
-class FolderEventSetLatestView {
+class FolderEventReadViewInfo {
      ViewId request;
-     FolderEventSetLatestView(this.request);
+     FolderEventReadViewInfo(this.request);
 
-    Future<Either<Unit, FlowyError>> send() {
+    Future<Either<ViewInfo, FlowyError>> send() {
     final request = FFIRequest.create()
-          ..event = FolderEvent.SetLatestView.toString()
+          ..event = FolderEvent.ReadViewInfo.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)
         .then((bytesResult) => bytesResult.fold(
-           (bytes) => left(unit),
+           (okBytes) => left(ViewInfo.fromBuffer(okBytes)),
            (errBytes) => right(FlowyError.fromBuffer(errBytes)),
         ));
     }
 }
 
-class FolderEventCloseView {
+class FolderEventCopyLink {
+    FolderEventCopyLink();
+
+    Future<Either<Unit, FlowyError>> send() {
+     final request = FFIRequest.create()
+        ..event = FolderEvent.CopyLink.toString();
+
+     return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
+        (bytes) => left(unit),
+        (errBytes) => right(FlowyError.fromBuffer(errBytes)),
+      ));
+    }
+}
+
+class FolderEventSetLatestView {
      ViewId request;
-     FolderEventCloseView(this.request);
+     FolderEventSetLatestView(this.request);
 
     Future<Either<Unit, FlowyError>> send() {
     final request = FFIRequest.create()
-          ..event = FolderEvent.CloseView.toString()
+          ..event = FolderEvent.SetLatestView.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)

+ 2 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbenum.dart

@@ -53,6 +53,7 @@ class ErrorCode extends $pb.ProtobufEnum {
   static const ErrorCode FieldInvalidOperation = ErrorCode._(444, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldInvalidOperation');
   static const ErrorCode TypeOptionDataIsEmpty = ErrorCode._(450, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TypeOptionDataIsEmpty');
   static const ErrorCode InvalidDateTimeFormat = ErrorCode._(500, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidDateTimeFormat');
+  static const ErrorCode UnexpectedEmptyString = ErrorCode._(999, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UnexpectedEmptyString');
   static const ErrorCode InvalidData = ErrorCode._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidData');
 
   static const $core.List<ErrorCode> values = <ErrorCode> [
@@ -99,6 +100,7 @@ class ErrorCode extends $pb.ProtobufEnum {
     FieldInvalidOperation,
     TypeOptionDataIsEmpty,
     InvalidDateTimeFormat,
+    UnexpectedEmptyString,
     InvalidData,
   ];
 

+ 2 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbjson.dart

@@ -55,9 +55,10 @@ const ErrorCode$json = const {
     const {'1': 'FieldInvalidOperation', '2': 444},
     const {'1': 'TypeOptionDataIsEmpty', '2': 450},
     const {'1': 'InvalidDateTimeFormat', '2': 500},
+    const {'1': 'UnexpectedEmptyString', '2': 999},
     const {'1': 'InvalidData', '2': 1000},
   ],
 };
 
 /// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIRCg1Vc2VySWRJc0VtcHR5EAQSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQZBIWChJXb3Jrc3BhY2VJZEludmFsaWQQZRIYChRBcHBDb2xvclN0eWxlSW52YWxpZBBmEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEGcSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQaBIQCgxBcHBJZEludmFsaWQQbhISCg5BcHBOYW1lSW52YWxpZBBvEhMKD1ZpZXdOYW1lSW52YWxpZBB4EhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEHkSEQoNVmlld0lkSW52YWxpZBB6EhMKD1ZpZXdEZXNjVG9vTG9uZxB7EhMKD1ZpZXdEYXRhSW52YWxpZBB8EhMKD1ZpZXdOYW1lVG9vTG9uZxB9EhEKDENvbm5lY3RFcnJvchDIARIRCgxFbWFpbElzRW1wdHkQrAISFwoSRW1haWxGb3JtYXRJbnZhbGlkEK0CEhcKEkVtYWlsQWxyZWFkeUV4aXN0cxCuAhIUCg9QYXNzd29yZElzRW1wdHkQrwISFAoPUGFzc3dvcmRUb29Mb25nELACEiUKIFBhc3N3b3JkQ29udGFpbnNGb3JiaWRDaGFyYWN0ZXJzELECEhoKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBCyAhIVChBQYXNzd29yZE5vdE1hdGNoELMCEhQKD1VzZXJOYW1lVG9vTG9uZxC0AhInCiJVc2VyTmFtZUNvbnRhaW5Gb3JiaWRkZW5DaGFyYWN0ZXJzELUCEhQKD1VzZXJOYW1lSXNFbXB0eRC2AhISCg1Vc2VySWRJbnZhbGlkELcCEhEKDFVzZXJOb3RFeGlzdBC4AhIQCgtUZXh0VG9vTG9uZxCQAxISCg1HcmlkSWRJc0VtcHR5EJoDEhMKDkJsb2NrSWRJc0VtcHR5EKQDEhEKDFJvd0lkSXNFbXB0eRCuAxIUCg9PcHRpb25JZElzRW1wdHkQrwMSEwoORmllbGRJZElzRW1wdHkQuAMSFgoRRmllbGREb2VzTm90RXhpc3QQuQMSHAoXU2VsZWN0T3B0aW9uTmFtZUlzRW1wdHkQugMSEwoORmllbGROb3RFeGlzdHMQuwMSGgoVRmllbGRJbnZhbGlkT3BlcmF0aW9uELwDEhoKFVR5cGVPcHRpb25EYXRhSXNFbXB0eRDCAxIaChVJbnZhbGlkRGF0ZVRpbWVGb3JtYXQQ9AMSEAoLSW52YWxpZERhdGEQ6Ac=');
+final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIRCg1Vc2VySWRJc0VtcHR5EAQSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQZBIWChJXb3Jrc3BhY2VJZEludmFsaWQQZRIYChRBcHBDb2xvclN0eWxlSW52YWxpZBBmEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEGcSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQaBIQCgxBcHBJZEludmFsaWQQbhISCg5BcHBOYW1lSW52YWxpZBBvEhMKD1ZpZXdOYW1lSW52YWxpZBB4EhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEHkSEQoNVmlld0lkSW52YWxpZBB6EhMKD1ZpZXdEZXNjVG9vTG9uZxB7EhMKD1ZpZXdEYXRhSW52YWxpZBB8EhMKD1ZpZXdOYW1lVG9vTG9uZxB9EhEKDENvbm5lY3RFcnJvchDIARIRCgxFbWFpbElzRW1wdHkQrAISFwoSRW1haWxGb3JtYXRJbnZhbGlkEK0CEhcKEkVtYWlsQWxyZWFkeUV4aXN0cxCuAhIUCg9QYXNzd29yZElzRW1wdHkQrwISFAoPUGFzc3dvcmRUb29Mb25nELACEiUKIFBhc3N3b3JkQ29udGFpbnNGb3JiaWRDaGFyYWN0ZXJzELECEhoKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBCyAhIVChBQYXNzd29yZE5vdE1hdGNoELMCEhQKD1VzZXJOYW1lVG9vTG9uZxC0AhInCiJVc2VyTmFtZUNvbnRhaW5Gb3JiaWRkZW5DaGFyYWN0ZXJzELUCEhQKD1VzZXJOYW1lSXNFbXB0eRC2AhISCg1Vc2VySWRJbnZhbGlkELcCEhEKDFVzZXJOb3RFeGlzdBC4AhIQCgtUZXh0VG9vTG9uZxCQAxISCg1HcmlkSWRJc0VtcHR5EJoDEhMKDkJsb2NrSWRJc0VtcHR5EKQDEhEKDFJvd0lkSXNFbXB0eRCuAxIUCg9PcHRpb25JZElzRW1wdHkQrwMSEwoORmllbGRJZElzRW1wdHkQuAMSFgoRRmllbGREb2VzTm90RXhpc3QQuQMSHAoXU2VsZWN0T3B0aW9uTmFtZUlzRW1wdHkQugMSEwoORmllbGROb3RFeGlzdHMQuwMSGgoVRmllbGRJbnZhbGlkT3BlcmF0aW9uELwDEhoKFVR5cGVPcHRpb25EYXRhSXNFbXB0eRDCAxIaChVJbnZhbGlkRGF0ZVRpbWVGb3JtYXQQ9AMSGgoVVW5leHBlY3RlZEVtcHR5U3RyaW5nEOcHEhAKC0ludmFsaWREYXRhEOgH');

+ 1 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/protobuf.dart

@@ -1,5 +1,6 @@
 // Auto-generated, do not edit 
 export './app.pb.dart';
+export './view_info.pb.dart';
 export './view.pb.dart';
 export './trash.pb.dart';
 export './workspace.pb.dart';

+ 61 - 133
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pb.dart

@@ -19,15 +19,10 @@ class View extends $pb.GeneratedMessage {
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongToId')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
-    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
-    ..e<ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.TextBlock, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
-    ..aInt64(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'version')
-    ..aOM<RepeatedView>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongings', subBuilder: RepeatedView.create)
-    ..aInt64(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'modifiedTime')
-    ..aInt64(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'createTime')
-    ..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extData')
-    ..aOS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
-    ..a<$core.int>(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pluginType', $pb.PbFieldType.O3)
+    ..e<ViewDataType>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: ViewDataType.TextBlock, valueOf: ViewDataType.valueOf, enumValues: ViewDataType.values)
+    ..aInt64(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'modifiedTime')
+    ..aInt64(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'createTime')
+    ..a<$core.int>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pluginType', $pb.PbFieldType.O3)
     ..hasRequiredFields = false
   ;
 
@@ -36,14 +31,9 @@ class View extends $pb.GeneratedMessage {
     $core.String? id,
     $core.String? belongToId,
     $core.String? name,
-    $core.String? desc,
     ViewDataType? dataType,
-    $fixnum.Int64? version,
-    RepeatedView? belongings,
     $fixnum.Int64? modifiedTime,
     $fixnum.Int64? createTime,
-    $core.String? extData,
-    $core.String? thumbnail,
     $core.int? pluginType,
   }) {
     final _result = create();
@@ -56,30 +46,15 @@ class View extends $pb.GeneratedMessage {
     if (name != null) {
       _result.name = name;
     }
-    if (desc != null) {
-      _result.desc = desc;
-    }
     if (dataType != null) {
       _result.dataType = dataType;
     }
-    if (version != null) {
-      _result.version = version;
-    }
-    if (belongings != null) {
-      _result.belongings = belongings;
-    }
     if (modifiedTime != null) {
       _result.modifiedTime = modifiedTime;
     }
     if (createTime != null) {
       _result.createTime = createTime;
     }
-    if (extData != null) {
-      _result.extData = extData;
-    }
-    if (thumbnail != null) {
-      _result.thumbnail = thumbnail;
-    }
     if (pluginType != null) {
       _result.pluginType = pluginType;
     }
@@ -134,87 +109,40 @@ class View extends $pb.GeneratedMessage {
   void clearName() => clearField(3);
 
   @$pb.TagNumber(4)
-  $core.String get desc => $_getSZ(3);
+  ViewDataType get dataType => $_getN(3);
   @$pb.TagNumber(4)
-  set desc($core.String v) { $_setString(3, v); }
+  set dataType(ViewDataType v) { setField(4, v); }
   @$pb.TagNumber(4)
-  $core.bool hasDesc() => $_has(3);
+  $core.bool hasDataType() => $_has(3);
   @$pb.TagNumber(4)
-  void clearDesc() => clearField(4);
+  void clearDataType() => clearField(4);
 
   @$pb.TagNumber(5)
-  ViewDataType get dataType => $_getN(4);
+  $fixnum.Int64 get modifiedTime => $_getI64(4);
   @$pb.TagNumber(5)
-  set dataType(ViewDataType v) { setField(5, v); }
+  set modifiedTime($fixnum.Int64 v) { $_setInt64(4, v); }
   @$pb.TagNumber(5)
-  $core.bool hasDataType() => $_has(4);
+  $core.bool hasModifiedTime() => $_has(4);
   @$pb.TagNumber(5)
-  void clearDataType() => clearField(5);
+  void clearModifiedTime() => clearField(5);
 
   @$pb.TagNumber(6)
-  $fixnum.Int64 get version => $_getI64(5);
+  $fixnum.Int64 get createTime => $_getI64(5);
   @$pb.TagNumber(6)
-  set version($fixnum.Int64 v) { $_setInt64(5, v); }
+  set createTime($fixnum.Int64 v) { $_setInt64(5, v); }
   @$pb.TagNumber(6)
-  $core.bool hasVersion() => $_has(5);
+  $core.bool hasCreateTime() => $_has(5);
   @$pb.TagNumber(6)
-  void clearVersion() => clearField(6);
+  void clearCreateTime() => clearField(6);
 
   @$pb.TagNumber(7)
-  RepeatedView get belongings => $_getN(6);
-  @$pb.TagNumber(7)
-  set belongings(RepeatedView v) { setField(7, v); }
+  $core.int get pluginType => $_getIZ(6);
   @$pb.TagNumber(7)
-  $core.bool hasBelongings() => $_has(6);
+  set pluginType($core.int v) { $_setSignedInt32(6, v); }
   @$pb.TagNumber(7)
-  void clearBelongings() => clearField(7);
+  $core.bool hasPluginType() => $_has(6);
   @$pb.TagNumber(7)
-  RepeatedView ensureBelongings() => $_ensure(6);
-
-  @$pb.TagNumber(8)
-  $fixnum.Int64 get modifiedTime => $_getI64(7);
-  @$pb.TagNumber(8)
-  set modifiedTime($fixnum.Int64 v) { $_setInt64(7, v); }
-  @$pb.TagNumber(8)
-  $core.bool hasModifiedTime() => $_has(7);
-  @$pb.TagNumber(8)
-  void clearModifiedTime() => clearField(8);
-
-  @$pb.TagNumber(9)
-  $fixnum.Int64 get createTime => $_getI64(8);
-  @$pb.TagNumber(9)
-  set createTime($fixnum.Int64 v) { $_setInt64(8, v); }
-  @$pb.TagNumber(9)
-  $core.bool hasCreateTime() => $_has(8);
-  @$pb.TagNumber(9)
-  void clearCreateTime() => clearField(9);
-
-  @$pb.TagNumber(10)
-  $core.String get extData => $_getSZ(9);
-  @$pb.TagNumber(10)
-  set extData($core.String v) { $_setString(9, v); }
-  @$pb.TagNumber(10)
-  $core.bool hasExtData() => $_has(9);
-  @$pb.TagNumber(10)
-  void clearExtData() => clearField(10);
-
-  @$pb.TagNumber(11)
-  $core.String get thumbnail => $_getSZ(10);
-  @$pb.TagNumber(11)
-  set thumbnail($core.String v) { $_setString(10, v); }
-  @$pb.TagNumber(11)
-  $core.bool hasThumbnail() => $_has(10);
-  @$pb.TagNumber(11)
-  void clearThumbnail() => clearField(11);
-
-  @$pb.TagNumber(12)
-  $core.int get pluginType => $_getIZ(11);
-  @$pb.TagNumber(12)
-  set pluginType($core.int v) { $_setSignedInt32(11, v); }
-  @$pb.TagNumber(12)
-  $core.bool hasPluginType() => $_has(11);
-  @$pb.TagNumber(12)
-  void clearPluginType() => clearField(12);
+  void clearPluginType() => clearField(7);
 }
 
 class RepeatedView extends $pb.GeneratedMessage {
@@ -258,6 +186,47 @@ class RepeatedView extends $pb.GeneratedMessage {
   $core.List<View> get items => $_getList(0);
 }
 
+class RepeatedViewId extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedViewId', createEmptyInstance: create)
+    ..pPS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items')
+    ..hasRequiredFields = false
+  ;
+
+  RepeatedViewId._() : super();
+  factory RepeatedViewId({
+    $core.Iterable<$core.String>? items,
+  }) {
+    final _result = create();
+    if (items != null) {
+      _result.items.addAll(items);
+    }
+    return _result;
+  }
+  factory RepeatedViewId.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RepeatedViewId.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')
+  RepeatedViewId clone() => RepeatedViewId()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  RepeatedViewId copyWith(void Function(RepeatedViewId) updates) => super.copyWith((message) => updates(message as RepeatedViewId)) as RepeatedViewId; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static RepeatedViewId create() => RepeatedViewId._();
+  RepeatedViewId createEmptyInstance() => create();
+  static $pb.PbList<RepeatedViewId> createRepeated() => $pb.PbList<RepeatedViewId>();
+  @$core.pragma('dart2js:noInline')
+  static RepeatedViewId getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedViewId>(create);
+  static RepeatedViewId? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.List<$core.String> get items => $_getList(0);
+}
+
 enum CreateViewPayload_OneOfThumbnail {
   thumbnail, 
   notSet
@@ -594,47 +563,6 @@ class ViewId extends $pb.GeneratedMessage {
   void clearValue() => clearField(1);
 }
 
-class RepeatedViewId extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedViewId', createEmptyInstance: create)
-    ..pPS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items')
-    ..hasRequiredFields = false
-  ;
-
-  RepeatedViewId._() : super();
-  factory RepeatedViewId({
-    $core.Iterable<$core.String>? items,
-  }) {
-    final _result = create();
-    if (items != null) {
-      _result.items.addAll(items);
-    }
-    return _result;
-  }
-  factory RepeatedViewId.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory RepeatedViewId.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')
-  RepeatedViewId clone() => RepeatedViewId()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  RepeatedViewId copyWith(void Function(RepeatedViewId) updates) => super.copyWith((message) => updates(message as RepeatedViewId)) as RepeatedViewId; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static RepeatedViewId create() => RepeatedViewId._();
-  RepeatedViewId createEmptyInstance() => create();
-  static $pb.PbList<RepeatedViewId> createRepeated() => $pb.PbList<RepeatedViewId>();
-  @$core.pragma('dart2js:noInline')
-  static RepeatedViewId getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedViewId>(create);
-  static RepeatedViewId? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.List<$core.String> get items => $_getList(0);
-}
-
 enum UpdateViewPayload_OneOfName {
   name, 
   notSet

+ 15 - 20
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view.pbjson.dart

@@ -37,20 +37,15 @@ const View$json = const {
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'belong_to_id', '3': 2, '4': 1, '5': 9, '10': 'belongToId'},
     const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'},
-    const {'1': 'desc', '3': 4, '4': 1, '5': 9, '10': 'desc'},
-    const {'1': 'data_type', '3': 5, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
-    const {'1': 'version', '3': 6, '4': 1, '5': 3, '10': 'version'},
-    const {'1': 'belongings', '3': 7, '4': 1, '5': 11, '6': '.RepeatedView', '10': 'belongings'},
-    const {'1': 'modified_time', '3': 8, '4': 1, '5': 3, '10': 'modifiedTime'},
-    const {'1': 'create_time', '3': 9, '4': 1, '5': 3, '10': 'createTime'},
-    const {'1': 'ext_data', '3': 10, '4': 1, '5': 9, '10': 'extData'},
-    const {'1': 'thumbnail', '3': 11, '4': 1, '5': 9, '10': 'thumbnail'},
-    const {'1': 'plugin_type', '3': 12, '4': 1, '5': 5, '10': 'pluginType'},
+    const {'1': 'data_type', '3': 4, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
+    const {'1': 'modified_time', '3': 5, '4': 1, '5': 3, '10': 'modifiedTime'},
+    const {'1': 'create_time', '3': 6, '4': 1, '5': 3, '10': 'createTime'},
+    const {'1': 'plugin_type', '3': 7, '4': 1, '5': 5, '10': 'pluginType'},
   ],
 };
 
 /// Descriptor for `View`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List viewDescriptor = $convert.base64Decode('CgRWaWV3Eg4KAmlkGAEgASgJUgJpZBIgCgxiZWxvbmdfdG9faWQYAiABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgDIAEoCVIEbmFtZRISCgRkZXNjGAQgASgJUgRkZXNjEioKCWRhdGFfdHlwZRgFIAEoDjINLlZpZXdEYXRhVHlwZVIIZGF0YVR5cGUSGAoHdmVyc2lvbhgGIAEoA1IHdmVyc2lvbhItCgpiZWxvbmdpbmdzGAcgASgLMg0uUmVwZWF0ZWRWaWV3UgpiZWxvbmdpbmdzEiMKDW1vZGlmaWVkX3RpbWUYCCABKANSDG1vZGlmaWVkVGltZRIfCgtjcmVhdGVfdGltZRgJIAEoA1IKY3JlYXRlVGltZRIZCghleHRfZGF0YRgKIAEoCVIHZXh0RGF0YRIcCgl0aHVtYm5haWwYCyABKAlSCXRodW1ibmFpbBIfCgtwbHVnaW5fdHlwZRgMIAEoBVIKcGx1Z2luVHlwZQ==');
+final $typed_data.Uint8List viewDescriptor = $convert.base64Decode('CgRWaWV3Eg4KAmlkGAEgASgJUgJpZBIgCgxiZWxvbmdfdG9faWQYAiABKAlSCmJlbG9uZ1RvSWQSEgoEbmFtZRgDIAEoCVIEbmFtZRIqCglkYXRhX3R5cGUYBCABKA4yDS5WaWV3RGF0YVR5cGVSCGRhdGFUeXBlEiMKDW1vZGlmaWVkX3RpbWUYBSABKANSDG1vZGlmaWVkVGltZRIfCgtjcmVhdGVfdGltZRgGIAEoA1IKY3JlYXRlVGltZRIfCgtwbHVnaW5fdHlwZRgHIAEoBVIKcGx1Z2luVHlwZQ==');
 @$core.Deprecated('Use repeatedViewDescriptor instead')
 const RepeatedView$json = const {
   '1': 'RepeatedView',
@@ -61,6 +56,16 @@ const RepeatedView$json = const {
 
 /// Descriptor for `RepeatedView`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List repeatedViewDescriptor = $convert.base64Decode('CgxSZXBlYXRlZFZpZXcSGwoFaXRlbXMYASADKAsyBS5WaWV3UgVpdGVtcw==');
+@$core.Deprecated('Use repeatedViewIdDescriptor instead')
+const RepeatedViewId$json = const {
+  '1': 'RepeatedViewId',
+  '2': const [
+    const {'1': 'items', '3': 1, '4': 3, '5': 9, '10': 'items'},
+  ],
+};
+
+/// Descriptor for `RepeatedViewId`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List repeatedViewIdDescriptor = $convert.base64Decode('Cg5SZXBlYXRlZFZpZXdJZBIUCgVpdGVtcxgBIAMoCVIFaXRlbXM=');
 @$core.Deprecated('Use createViewPayloadDescriptor instead')
 const CreateViewPayload$json = const {
   '1': 'CreateViewPayload',
@@ -107,16 +112,6 @@ const ViewId$json = const {
 
 /// Descriptor for `ViewId`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List viewIdDescriptor = $convert.base64Decode('CgZWaWV3SWQSFAoFdmFsdWUYASABKAlSBXZhbHVl');
-@$core.Deprecated('Use repeatedViewIdDescriptor instead')
-const RepeatedViewId$json = const {
-  '1': 'RepeatedViewId',
-  '2': const [
-    const {'1': 'items', '3': 1, '4': 3, '5': 9, '10': 'items'},
-  ],
-};
-
-/// Descriptor for `RepeatedViewId`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List repeatedViewIdDescriptor = $convert.base64Decode('Cg5SZXBlYXRlZFZpZXdJZBIUCgVpdGVtcxgBIAMoCVIFaXRlbXM=');
 @$core.Deprecated('Use updateViewPayloadDescriptor instead')
 const UpdateViewPayload$json = const {
   '1': 'UpdateViewPayload',

+ 148 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pb.dart

@@ -0,0 +1,148 @@
+///
+//  Generated code. Do not modify.
+//  source: view_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+import 'view.pb.dart' as $0;
+
+import 'view.pbenum.dart' as $0;
+
+class ViewInfo extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ViewInfo', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongToId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
+    ..e<$0.ViewDataType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dataType', $pb.PbFieldType.OE, defaultOrMaker: $0.ViewDataType.TextBlock, valueOf: $0.ViewDataType.valueOf, enumValues: $0.ViewDataType.values)
+    ..aOM<$0.RepeatedView>(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'belongings', subBuilder: $0.RepeatedView.create)
+    ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'extData')
+    ..hasRequiredFields = false
+  ;
+
+  ViewInfo._() : super();
+  factory ViewInfo({
+    $core.String? id,
+    $core.String? belongToId,
+    $core.String? name,
+    $core.String? desc,
+    $0.ViewDataType? dataType,
+    $0.RepeatedView? belongings,
+    $core.String? extData,
+  }) {
+    final _result = create();
+    if (id != null) {
+      _result.id = id;
+    }
+    if (belongToId != null) {
+      _result.belongToId = belongToId;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (desc != null) {
+      _result.desc = desc;
+    }
+    if (dataType != null) {
+      _result.dataType = dataType;
+    }
+    if (belongings != null) {
+      _result.belongings = belongings;
+    }
+    if (extData != null) {
+      _result.extData = extData;
+    }
+    return _result;
+  }
+  factory ViewInfo.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ViewInfo.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')
+  ViewInfo clone() => ViewInfo()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  ViewInfo copyWith(void Function(ViewInfo) updates) => super.copyWith((message) => updates(message as ViewInfo)) as ViewInfo; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static ViewInfo create() => ViewInfo._();
+  ViewInfo createEmptyInstance() => create();
+  static $pb.PbList<ViewInfo> createRepeated() => $pb.PbList<ViewInfo>();
+  @$core.pragma('dart2js:noInline')
+  static ViewInfo getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ViewInfo>(create);
+  static ViewInfo? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get id => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set id($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get belongToId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set belongToId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasBelongToId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearBelongToId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get name => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set name($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasName() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearName() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $core.String get desc => $_getSZ(3);
+  @$pb.TagNumber(4)
+  set desc($core.String v) { $_setString(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasDesc() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearDesc() => clearField(4);
+
+  @$pb.TagNumber(5)
+  $0.ViewDataType get dataType => $_getN(4);
+  @$pb.TagNumber(5)
+  set dataType($0.ViewDataType v) { setField(5, v); }
+  @$pb.TagNumber(5)
+  $core.bool hasDataType() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearDataType() => clearField(5);
+
+  @$pb.TagNumber(6)
+  $0.RepeatedView get belongings => $_getN(5);
+  @$pb.TagNumber(6)
+  set belongings($0.RepeatedView v) { setField(6, v); }
+  @$pb.TagNumber(6)
+  $core.bool hasBelongings() => $_has(5);
+  @$pb.TagNumber(6)
+  void clearBelongings() => clearField(6);
+  @$pb.TagNumber(6)
+  $0.RepeatedView ensureBelongings() => $_ensure(5);
+
+  @$pb.TagNumber(7)
+  $core.String get extData => $_getSZ(6);
+  @$pb.TagNumber(7)
+  set extData($core.String v) { $_setString(6, v); }
+  @$pb.TagNumber(7)
+  $core.bool hasExtData() => $_has(6);
+  @$pb.TagNumber(7)
+  void clearExtData() => clearField(7);
+}
+

+ 7 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: view_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 26 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pbjson.dart

@@ -0,0 +1,26 @@
+///
+//  Generated code. Do not modify.
+//  source: view_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use viewInfoDescriptor instead')
+const ViewInfo$json = const {
+  '1': 'ViewInfo',
+  '2': const [
+    const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
+    const {'1': 'belong_to_id', '3': 2, '4': 1, '5': 9, '10': 'belongToId'},
+    const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'},
+    const {'1': 'desc', '3': 4, '4': 1, '5': 9, '10': 'desc'},
+    const {'1': 'data_type', '3': 5, '4': 1, '5': 14, '6': '.ViewDataType', '10': 'dataType'},
+    const {'1': 'belongings', '3': 6, '4': 1, '5': 11, '6': '.RepeatedView', '10': 'belongings'},
+    const {'1': 'ext_data', '3': 7, '4': 1, '5': 9, '10': 'extData'},
+  ],
+};
+
+/// Descriptor for `ViewInfo`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List viewInfoDescriptor = $convert.base64Decode('CghWaWV3SW5mbxIOCgJpZBgBIAEoCVICaWQSIAoMYmVsb25nX3RvX2lkGAIgASgJUgpiZWxvbmdUb0lkEhIKBG5hbWUYAyABKAlSBG5hbWUSEgoEZGVzYxgEIAEoCVIEZGVzYxIqCglkYXRhX3R5cGUYBSABKA4yDS5WaWV3RGF0YVR5cGVSCGRhdGFUeXBlEi0KCmJlbG9uZ2luZ3MYBiABKAsyDS5SZXBlYXRlZFZpZXdSCmJlbG9uZ2luZ3MSGQoIZXh0X2RhdGEYByABKAlSB2V4dERhdGE=');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder-data-model/view_info.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: view_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'view_info.pb.dart';
+

+ 7 - 5
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder/event_map.pbenum.dart

@@ -25,10 +25,11 @@ class FolderEvent extends $pb.ProtobufEnum {
   static const FolderEvent UpdateView = FolderEvent._(203, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateView');
   static const FolderEvent DeleteView = FolderEvent._(204, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteView');
   static const FolderEvent DuplicateView = FolderEvent._(205, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateView');
-  static const FolderEvent CopyLink = FolderEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CopyLink');
-  static const FolderEvent SetLatestView = FolderEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SetLatestView');
-  static const FolderEvent CloseView = FolderEvent._(208, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CloseView');
-  static const FolderEvent MoveFolderItem = FolderEvent._(209, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveFolderItem');
+  static const FolderEvent CloseView = FolderEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CloseView');
+  static const FolderEvent ReadViewInfo = FolderEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadViewInfo');
+  static const FolderEvent CopyLink = FolderEvent._(220, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CopyLink');
+  static const FolderEvent SetLatestView = FolderEvent._(221, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SetLatestView');
+  static const FolderEvent MoveFolderItem = FolderEvent._(230, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveFolderItem');
   static const FolderEvent ReadTrash = FolderEvent._(300, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadTrash');
   static const FolderEvent PutbackTrash = FolderEvent._(301, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PutbackTrash');
   static const FolderEvent DeleteTrash = FolderEvent._(302, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteTrash');
@@ -51,9 +52,10 @@ class FolderEvent extends $pb.ProtobufEnum {
     UpdateView,
     DeleteView,
     DuplicateView,
+    CloseView,
+    ReadViewInfo,
     CopyLink,
     SetLatestView,
-    CloseView,
     MoveFolderItem,
     ReadTrash,
     PutbackTrash,

+ 6 - 5
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-folder/event_map.pbjson.dart

@@ -27,10 +27,11 @@ const FolderEvent$json = const {
     const {'1': 'UpdateView', '2': 203},
     const {'1': 'DeleteView', '2': 204},
     const {'1': 'DuplicateView', '2': 205},
-    const {'1': 'CopyLink', '2': 206},
-    const {'1': 'SetLatestView', '2': 207},
-    const {'1': 'CloseView', '2': 208},
-    const {'1': 'MoveFolderItem', '2': 209},
+    const {'1': 'CloseView', '2': 206},
+    const {'1': 'ReadViewInfo', '2': 207},
+    const {'1': 'CopyLink', '2': 220},
+    const {'1': 'SetLatestView', '2': 221},
+    const {'1': 'MoveFolderItem', '2': 230},
     const {'1': 'ReadTrash', '2': 300},
     const {'1': 'PutbackTrash', '2': 301},
     const {'1': 'DeleteTrash', '2': 302},
@@ -40,4 +41,4 @@ const FolderEvent$json = const {
 };
 
 /// Descriptor for `FolderEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List folderEventDescriptor = $convert.base64Decode('CgtGb2xkZXJFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESEgoNRHVwbGljYXRlVmlldxDNARINCghDb3B5TGluaxDOARISCg1TZXRMYXRlc3RWaWV3EM8BEg4KCUNsb3NlVmlldxDQARITCg5Nb3ZlRm9sZGVySXRlbRDRARIOCglSZWFkVHJhc2gQrAISEQoMUHV0YmFja1RyYXNoEK0CEhAKC0RlbGV0ZVRyYXNoEK4CEhQKD1Jlc3RvcmVBbGxUcmFzaBCvAhITCg5EZWxldGVBbGxUcmFzaBCwAg==');
+final $typed_data.Uint8List folderEventDescriptor = $convert.base64Decode('CgtGb2xkZXJFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESEgoNRHVwbGljYXRlVmlldxDNARIOCglDbG9zZVZpZXcQzgESEQoMUmVhZFZpZXdJbmZvEM8BEg0KCENvcHlMaW5rENwBEhIKDVNldExhdGVzdFZpZXcQ3QESEwoOTW92ZUZvbGRlckl0ZW0Q5gESDgoJUmVhZFRyYXNoEKwCEhEKDFB1dGJhY2tUcmFzaBCtAhIQCgtEZWxldGVUcmFzaBCuAhIUCg9SZXN0b3JlQWxsVHJhc2gQrwISEwoORGVsZXRlQWxsVHJhc2gQsAI=');

+ 1246 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pb.dart

@@ -0,0 +1,1246 @@
+///
+//  Generated code. Do not modify.
+//  source: field.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+import 'field.pbenum.dart';
+
+export 'field.pbenum.dart';
+
+class Field extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Field', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
+    ..e<FieldType>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: FieldType.RichText, valueOf: FieldType.valueOf, enumValues: FieldType.values)
+    ..aOB(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'frozen')
+    ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'visibility')
+    ..a<$core.int>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'width', $pb.PbFieldType.O3)
+    ..aOB(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isPrimary')
+    ..hasRequiredFields = false
+  ;
+
+  Field._() : super();
+  factory Field({
+    $core.String? id,
+    $core.String? name,
+    $core.String? desc,
+    FieldType? fieldType,
+    $core.bool? frozen,
+    $core.bool? visibility,
+    $core.int? width,
+    $core.bool? isPrimary,
+  }) {
+    final _result = create();
+    if (id != null) {
+      _result.id = id;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (desc != null) {
+      _result.desc = desc;
+    }
+    if (fieldType != null) {
+      _result.fieldType = fieldType;
+    }
+    if (frozen != null) {
+      _result.frozen = frozen;
+    }
+    if (visibility != null) {
+      _result.visibility = visibility;
+    }
+    if (width != null) {
+      _result.width = width;
+    }
+    if (isPrimary != null) {
+      _result.isPrimary = isPrimary;
+    }
+    return _result;
+  }
+  factory Field.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory Field.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')
+  Field clone() => Field()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  Field copyWith(void Function(Field) updates) => super.copyWith((message) => updates(message as Field)) as Field; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static Field create() => Field._();
+  Field createEmptyInstance() => create();
+  static $pb.PbList<Field> createRepeated() => $pb.PbList<Field>();
+  @$core.pragma('dart2js:noInline')
+  static Field getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Field>(create);
+  static Field? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get id => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set id($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get name => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set name($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasName() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearName() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get desc => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set desc($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasDesc() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearDesc() => clearField(3);
+
+  @$pb.TagNumber(4)
+  FieldType get fieldType => $_getN(3);
+  @$pb.TagNumber(4)
+  set fieldType(FieldType v) { setField(4, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasFieldType() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearFieldType() => clearField(4);
+
+  @$pb.TagNumber(5)
+  $core.bool get frozen => $_getBF(4);
+  @$pb.TagNumber(5)
+  set frozen($core.bool v) { $_setBool(4, v); }
+  @$pb.TagNumber(5)
+  $core.bool hasFrozen() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearFrozen() => clearField(5);
+
+  @$pb.TagNumber(6)
+  $core.bool get visibility => $_getBF(5);
+  @$pb.TagNumber(6)
+  set visibility($core.bool v) { $_setBool(5, v); }
+  @$pb.TagNumber(6)
+  $core.bool hasVisibility() => $_has(5);
+  @$pb.TagNumber(6)
+  void clearVisibility() => clearField(6);
+
+  @$pb.TagNumber(7)
+  $core.int get width => $_getIZ(6);
+  @$pb.TagNumber(7)
+  set width($core.int v) { $_setSignedInt32(6, v); }
+  @$pb.TagNumber(7)
+  $core.bool hasWidth() => $_has(6);
+  @$pb.TagNumber(7)
+  void clearWidth() => clearField(7);
+
+  @$pb.TagNumber(8)
+  $core.bool get isPrimary => $_getBF(7);
+  @$pb.TagNumber(8)
+  set isPrimary($core.bool v) { $_setBool(7, v); }
+  @$pb.TagNumber(8)
+  $core.bool hasIsPrimary() => $_has(7);
+  @$pb.TagNumber(8)
+  void clearIsPrimary() => clearField(8);
+}
+
+class FieldOrder extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldOrder', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..hasRequiredFields = false
+  ;
+
+  FieldOrder._() : super();
+  factory FieldOrder({
+    $core.String? fieldId,
+  }) {
+    final _result = create();
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    return _result;
+  }
+  factory FieldOrder.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory FieldOrder.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')
+  FieldOrder clone() => FieldOrder()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  FieldOrder copyWith(void Function(FieldOrder) updates) => super.copyWith((message) => updates(message as FieldOrder)) as FieldOrder; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static FieldOrder create() => FieldOrder._();
+  FieldOrder createEmptyInstance() => create();
+  static $pb.PbList<FieldOrder> createRepeated() => $pb.PbList<FieldOrder>();
+  @$core.pragma('dart2js:noInline')
+  static FieldOrder getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FieldOrder>(create);
+  static FieldOrder? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get fieldId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set fieldId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasFieldId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearFieldId() => clearField(1);
+}
+
+class GridFieldChangeset extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridFieldChangeset', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..pc<IndexField>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedFields', $pb.PbFieldType.PM, subBuilder: IndexField.create)
+    ..pc<FieldOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedFields', $pb.PbFieldType.PM, subBuilder: FieldOrder.create)
+    ..pc<Field>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'updatedFields', $pb.PbFieldType.PM, subBuilder: Field.create)
+    ..hasRequiredFields = false
+  ;
+
+  GridFieldChangeset._() : super();
+  factory GridFieldChangeset({
+    $core.String? gridId,
+    $core.Iterable<IndexField>? insertedFields,
+    $core.Iterable<FieldOrder>? deletedFields,
+    $core.Iterable<Field>? updatedFields,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (insertedFields != null) {
+      _result.insertedFields.addAll(insertedFields);
+    }
+    if (deletedFields != null) {
+      _result.deletedFields.addAll(deletedFields);
+    }
+    if (updatedFields != null) {
+      _result.updatedFields.addAll(updatedFields);
+    }
+    return _result;
+  }
+  factory GridFieldChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GridFieldChangeset.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')
+  GridFieldChangeset clone() => GridFieldChangeset()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GridFieldChangeset copyWith(void Function(GridFieldChangeset) updates) => super.copyWith((message) => updates(message as GridFieldChangeset)) as GridFieldChangeset; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GridFieldChangeset create() => GridFieldChangeset._();
+  GridFieldChangeset createEmptyInstance() => create();
+  static $pb.PbList<GridFieldChangeset> createRepeated() => $pb.PbList<GridFieldChangeset>();
+  @$core.pragma('dart2js:noInline')
+  static GridFieldChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridFieldChangeset>(create);
+  static GridFieldChangeset? _defaultInstance;
+
+  @$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.List<IndexField> get insertedFields => $_getList(1);
+
+  @$pb.TagNumber(3)
+  $core.List<FieldOrder> get deletedFields => $_getList(2);
+
+  @$pb.TagNumber(4)
+  $core.List<Field> get updatedFields => $_getList(3);
+}
+
+class IndexField extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'IndexField', createEmptyInstance: create)
+    ..aOM<Field>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'field', subBuilder: Field.create)
+    ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'index', $pb.PbFieldType.O3)
+    ..hasRequiredFields = false
+  ;
+
+  IndexField._() : super();
+  factory IndexField({
+    Field? field_1,
+    $core.int? index,
+  }) {
+    final _result = create();
+    if (field_1 != null) {
+      _result.field_1 = field_1;
+    }
+    if (index != null) {
+      _result.index = index;
+    }
+    return _result;
+  }
+  factory IndexField.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory IndexField.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')
+  IndexField clone() => IndexField()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  IndexField copyWith(void Function(IndexField) updates) => super.copyWith((message) => updates(message as IndexField)) as IndexField; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static IndexField create() => IndexField._();
+  IndexField createEmptyInstance() => create();
+  static $pb.PbList<IndexField> createRepeated() => $pb.PbList<IndexField>();
+  @$core.pragma('dart2js:noInline')
+  static IndexField getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<IndexField>(create);
+  static IndexField? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  Field get field_1 => $_getN(0);
+  @$pb.TagNumber(1)
+  set field_1(Field v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasField_1() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearField_1() => clearField(1);
+  @$pb.TagNumber(1)
+  Field ensureField_1() => $_ensure(0);
+
+  @$pb.TagNumber(2)
+  $core.int get index => $_getIZ(1);
+  @$pb.TagNumber(2)
+  set index($core.int v) { $_setSignedInt32(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasIndex() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearIndex() => clearField(2);
+}
+
+enum GetEditFieldContextPayload_OneOfFieldId {
+  fieldId, 
+  notSet
+}
+
+class GetEditFieldContextPayload extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, GetEditFieldContextPayload_OneOfFieldId> _GetEditFieldContextPayload_OneOfFieldIdByTag = {
+    2 : GetEditFieldContextPayload_OneOfFieldId.fieldId,
+    0 : GetEditFieldContextPayload_OneOfFieldId.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetEditFieldContextPayload', createEmptyInstance: create)
+    ..oo(0, [2])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..e<FieldType>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: FieldType.RichText, valueOf: FieldType.valueOf, enumValues: FieldType.values)
+    ..hasRequiredFields = false
+  ;
+
+  GetEditFieldContextPayload._() : super();
+  factory GetEditFieldContextPayload({
+    $core.String? gridId,
+    $core.String? fieldId,
+    FieldType? fieldType,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    if (fieldType != null) {
+      _result.fieldType = fieldType;
+    }
+    return _result;
+  }
+  factory GetEditFieldContextPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GetEditFieldContextPayload.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')
+  GetEditFieldContextPayload clone() => GetEditFieldContextPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GetEditFieldContextPayload copyWith(void Function(GetEditFieldContextPayload) updates) => super.copyWith((message) => updates(message as GetEditFieldContextPayload)) as GetEditFieldContextPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GetEditFieldContextPayload create() => GetEditFieldContextPayload._();
+  GetEditFieldContextPayload createEmptyInstance() => create();
+  static $pb.PbList<GetEditFieldContextPayload> createRepeated() => $pb.PbList<GetEditFieldContextPayload>();
+  @$core.pragma('dart2js:noInline')
+  static GetEditFieldContextPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetEditFieldContextPayload>(create);
+  static GetEditFieldContextPayload? _defaultInstance;
+
+  GetEditFieldContextPayload_OneOfFieldId whichOneOfFieldId() => _GetEditFieldContextPayload_OneOfFieldIdByTag[$_whichOneof(0)]!;
+  void clearOneOfFieldId() => 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)
+  FieldType get fieldType => $_getN(2);
+  @$pb.TagNumber(3)
+  set fieldType(FieldType v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasFieldType() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearFieldType() => clearField(3);
+}
+
+class EditFieldPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'EditFieldPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..e<FieldType>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: FieldType.RichText, valueOf: FieldType.valueOf, enumValues: FieldType.values)
+    ..aOB(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'createIfNotExist')
+    ..hasRequiredFields = false
+  ;
+
+  EditFieldPayload._() : super();
+  factory EditFieldPayload({
+    $core.String? gridId,
+    $core.String? fieldId,
+    FieldType? fieldType,
+    $core.bool? createIfNotExist,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    if (fieldType != null) {
+      _result.fieldType = fieldType;
+    }
+    if (createIfNotExist != null) {
+      _result.createIfNotExist = createIfNotExist;
+    }
+    return _result;
+  }
+  factory EditFieldPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory EditFieldPayload.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')
+  EditFieldPayload clone() => EditFieldPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  EditFieldPayload copyWith(void Function(EditFieldPayload) updates) => super.copyWith((message) => updates(message as EditFieldPayload)) as EditFieldPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static EditFieldPayload create() => EditFieldPayload._();
+  EditFieldPayload createEmptyInstance() => create();
+  static $pb.PbList<EditFieldPayload> createRepeated() => $pb.PbList<EditFieldPayload>();
+  @$core.pragma('dart2js:noInline')
+  static EditFieldPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EditFieldPayload>(create);
+  static EditFieldPayload? _defaultInstance;
+
+  @$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)
+  FieldType get fieldType => $_getN(2);
+  @$pb.TagNumber(3)
+  set fieldType(FieldType v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasFieldType() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearFieldType() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $core.bool get createIfNotExist => $_getBF(3);
+  @$pb.TagNumber(4)
+  set createIfNotExist($core.bool v) { $_setBool(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasCreateIfNotExist() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearCreateIfNotExist() => clearField(4);
+}
+
+class FieldTypeOptionContext extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldTypeOptionContext', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOM<Field>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridField', subBuilder: Field.create)
+    ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY)
+    ..hasRequiredFields = false
+  ;
+
+  FieldTypeOptionContext._() : super();
+  factory FieldTypeOptionContext({
+    $core.String? gridId,
+    Field? gridField,
+    $core.List<$core.int>? typeOptionData,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (gridField != null) {
+      _result.gridField = gridField;
+    }
+    if (typeOptionData != null) {
+      _result.typeOptionData = typeOptionData;
+    }
+    return _result;
+  }
+  factory FieldTypeOptionContext.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory FieldTypeOptionContext.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')
+  FieldTypeOptionContext clone() => FieldTypeOptionContext()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  FieldTypeOptionContext copyWith(void Function(FieldTypeOptionContext) updates) => super.copyWith((message) => updates(message as FieldTypeOptionContext)) as FieldTypeOptionContext; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static FieldTypeOptionContext create() => FieldTypeOptionContext._();
+  FieldTypeOptionContext createEmptyInstance() => create();
+  static $pb.PbList<FieldTypeOptionContext> createRepeated() => $pb.PbList<FieldTypeOptionContext>();
+  @$core.pragma('dart2js:noInline')
+  static FieldTypeOptionContext getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FieldTypeOptionContext>(create);
+  static FieldTypeOptionContext? _defaultInstance;
+
+  @$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)
+  Field get gridField => $_getN(1);
+  @$pb.TagNumber(2)
+  set gridField(Field v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasGridField() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearGridField() => clearField(2);
+  @$pb.TagNumber(2)
+  Field ensureGridField() => $_ensure(1);
+
+  @$pb.TagNumber(3)
+  $core.List<$core.int> get typeOptionData => $_getN(2);
+  @$pb.TagNumber(3)
+  set typeOptionData($core.List<$core.int> v) { $_setBytes(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasTypeOptionData() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearTypeOptionData() => clearField(3);
+}
+
+class FieldTypeOptionData extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldTypeOptionData', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOM<Field>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'field', subBuilder: Field.create)
+    ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY)
+    ..hasRequiredFields = false
+  ;
+
+  FieldTypeOptionData._() : super();
+  factory FieldTypeOptionData({
+    $core.String? gridId,
+    Field? field_2,
+    $core.List<$core.int>? typeOptionData,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (field_2 != null) {
+      _result.field_2 = field_2;
+    }
+    if (typeOptionData != null) {
+      _result.typeOptionData = typeOptionData;
+    }
+    return _result;
+  }
+  factory FieldTypeOptionData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory FieldTypeOptionData.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')
+  FieldTypeOptionData clone() => FieldTypeOptionData()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  FieldTypeOptionData copyWith(void Function(FieldTypeOptionData) updates) => super.copyWith((message) => updates(message as FieldTypeOptionData)) as FieldTypeOptionData; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static FieldTypeOptionData create() => FieldTypeOptionData._();
+  FieldTypeOptionData createEmptyInstance() => create();
+  static $pb.PbList<FieldTypeOptionData> createRepeated() => $pb.PbList<FieldTypeOptionData>();
+  @$core.pragma('dart2js:noInline')
+  static FieldTypeOptionData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FieldTypeOptionData>(create);
+  static FieldTypeOptionData? _defaultInstance;
+
+  @$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)
+  Field get field_2 => $_getN(1);
+  @$pb.TagNumber(2)
+  set field_2(Field v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasField_2() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearField_2() => clearField(2);
+  @$pb.TagNumber(2)
+  Field ensureField_2() => $_ensure(1);
+
+  @$pb.TagNumber(3)
+  $core.List<$core.int> get typeOptionData => $_getN(2);
+  @$pb.TagNumber(3)
+  set typeOptionData($core.List<$core.int> v) { $_setBytes(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasTypeOptionData() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearTypeOptionData() => clearField(3);
+}
+
+class RepeatedField extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedField', createEmptyInstance: create)
+    ..pc<Field>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Field.create)
+    ..hasRequiredFields = false
+  ;
+
+  RepeatedField._() : super();
+  factory RepeatedField({
+    $core.Iterable<Field>? items,
+  }) {
+    final _result = create();
+    if (items != null) {
+      _result.items.addAll(items);
+    }
+    return _result;
+  }
+  factory RepeatedField.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RepeatedField.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')
+  RepeatedField clone() => RepeatedField()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  RepeatedField copyWith(void Function(RepeatedField) updates) => super.copyWith((message) => updates(message as RepeatedField)) as RepeatedField; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static RepeatedField create() => RepeatedField._();
+  RepeatedField createEmptyInstance() => create();
+  static $pb.PbList<RepeatedField> createRepeated() => $pb.PbList<RepeatedField>();
+  @$core.pragma('dart2js:noInline')
+  static RepeatedField getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedField>(create);
+  static RepeatedField? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.List<Field> get items => $_getList(0);
+}
+
+class RepeatedFieldOrder extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedFieldOrder', createEmptyInstance: create)
+    ..pc<FieldOrder>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: FieldOrder.create)
+    ..hasRequiredFields = false
+  ;
+
+  RepeatedFieldOrder._() : super();
+  factory RepeatedFieldOrder({
+    $core.Iterable<FieldOrder>? items,
+  }) {
+    final _result = create();
+    if (items != null) {
+      _result.items.addAll(items);
+    }
+    return _result;
+  }
+  factory RepeatedFieldOrder.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RepeatedFieldOrder.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')
+  RepeatedFieldOrder clone() => RepeatedFieldOrder()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  RepeatedFieldOrder copyWith(void Function(RepeatedFieldOrder) updates) => super.copyWith((message) => updates(message as RepeatedFieldOrder)) as RepeatedFieldOrder; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static RepeatedFieldOrder create() => RepeatedFieldOrder._();
+  RepeatedFieldOrder createEmptyInstance() => create();
+  static $pb.PbList<RepeatedFieldOrder> createRepeated() => $pb.PbList<RepeatedFieldOrder>();
+  @$core.pragma('dart2js:noInline')
+  static RepeatedFieldOrder getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedFieldOrder>(create);
+  static RepeatedFieldOrder? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.List<FieldOrder> get items => $_getList(0);
+}
+
+enum InsertFieldPayload_OneOfStartFieldId {
+  startFieldId, 
+  notSet
+}
+
+class InsertFieldPayload extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, InsertFieldPayload_OneOfStartFieldId> _InsertFieldPayload_OneOfStartFieldIdByTag = {
+    4 : InsertFieldPayload_OneOfStartFieldId.startFieldId,
+    0 : InsertFieldPayload_OneOfStartFieldId.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'InsertFieldPayload', createEmptyInstance: create)
+    ..oo(0, [4])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOM<Field>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'field', subBuilder: Field.create)
+    ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY)
+    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'startFieldId')
+    ..hasRequiredFields = false
+  ;
+
+  InsertFieldPayload._() : super();
+  factory InsertFieldPayload({
+    $core.String? gridId,
+    Field? field_2,
+    $core.List<$core.int>? typeOptionData,
+    $core.String? startFieldId,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (field_2 != null) {
+      _result.field_2 = field_2;
+    }
+    if (typeOptionData != null) {
+      _result.typeOptionData = typeOptionData;
+    }
+    if (startFieldId != null) {
+      _result.startFieldId = startFieldId;
+    }
+    return _result;
+  }
+  factory InsertFieldPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory InsertFieldPayload.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')
+  InsertFieldPayload clone() => InsertFieldPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  InsertFieldPayload copyWith(void Function(InsertFieldPayload) updates) => super.copyWith((message) => updates(message as InsertFieldPayload)) as InsertFieldPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static InsertFieldPayload create() => InsertFieldPayload._();
+  InsertFieldPayload createEmptyInstance() => create();
+  static $pb.PbList<InsertFieldPayload> createRepeated() => $pb.PbList<InsertFieldPayload>();
+  @$core.pragma('dart2js:noInline')
+  static InsertFieldPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<InsertFieldPayload>(create);
+  static InsertFieldPayload? _defaultInstance;
+
+  InsertFieldPayload_OneOfStartFieldId whichOneOfStartFieldId() => _InsertFieldPayload_OneOfStartFieldIdByTag[$_whichOneof(0)]!;
+  void clearOneOfStartFieldId() => 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)
+  Field get field_2 => $_getN(1);
+  @$pb.TagNumber(2)
+  set field_2(Field v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasField_2() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearField_2() => clearField(2);
+  @$pb.TagNumber(2)
+  Field ensureField_2() => $_ensure(1);
+
+  @$pb.TagNumber(3)
+  $core.List<$core.int> get typeOptionData => $_getN(2);
+  @$pb.TagNumber(3)
+  set typeOptionData($core.List<$core.int> v) { $_setBytes(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasTypeOptionData() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearTypeOptionData() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $core.String get startFieldId => $_getSZ(3);
+  @$pb.TagNumber(4)
+  set startFieldId($core.String v) { $_setString(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasStartFieldId() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearStartFieldId() => clearField(4);
+}
+
+class UpdateFieldTypeOptionPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateFieldTypeOptionPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY)
+    ..hasRequiredFields = false
+  ;
+
+  UpdateFieldTypeOptionPayload._() : super();
+  factory UpdateFieldTypeOptionPayload({
+    $core.String? gridId,
+    $core.String? fieldId,
+    $core.List<$core.int>? typeOptionData,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    if (typeOptionData != null) {
+      _result.typeOptionData = typeOptionData;
+    }
+    return _result;
+  }
+  factory UpdateFieldTypeOptionPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory UpdateFieldTypeOptionPayload.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')
+  UpdateFieldTypeOptionPayload clone() => UpdateFieldTypeOptionPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  UpdateFieldTypeOptionPayload copyWith(void Function(UpdateFieldTypeOptionPayload) updates) => super.copyWith((message) => updates(message as UpdateFieldTypeOptionPayload)) as UpdateFieldTypeOptionPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static UpdateFieldTypeOptionPayload create() => UpdateFieldTypeOptionPayload._();
+  UpdateFieldTypeOptionPayload createEmptyInstance() => create();
+  static $pb.PbList<UpdateFieldTypeOptionPayload> createRepeated() => $pb.PbList<UpdateFieldTypeOptionPayload>();
+  @$core.pragma('dart2js:noInline')
+  static UpdateFieldTypeOptionPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UpdateFieldTypeOptionPayload>(create);
+  static UpdateFieldTypeOptionPayload? _defaultInstance;
+
+  @$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.List<$core.int> get typeOptionData => $_getN(2);
+  @$pb.TagNumber(3)
+  set typeOptionData($core.List<$core.int> v) { $_setBytes(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasTypeOptionData() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearTypeOptionData() => clearField(3);
+}
+
+class QueryFieldPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryFieldPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOM<RepeatedFieldOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldOrders', subBuilder: RepeatedFieldOrder.create)
+    ..hasRequiredFields = false
+  ;
+
+  QueryFieldPayload._() : super();
+  factory QueryFieldPayload({
+    $core.String? gridId,
+    RepeatedFieldOrder? fieldOrders,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (fieldOrders != null) {
+      _result.fieldOrders = fieldOrders;
+    }
+    return _result;
+  }
+  factory QueryFieldPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory QueryFieldPayload.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')
+  QueryFieldPayload clone() => QueryFieldPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  QueryFieldPayload copyWith(void Function(QueryFieldPayload) updates) => super.copyWith((message) => updates(message as QueryFieldPayload)) as QueryFieldPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static QueryFieldPayload create() => QueryFieldPayload._();
+  QueryFieldPayload createEmptyInstance() => create();
+  static $pb.PbList<QueryFieldPayload> createRepeated() => $pb.PbList<QueryFieldPayload>();
+  @$core.pragma('dart2js:noInline')
+  static QueryFieldPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<QueryFieldPayload>(create);
+  static QueryFieldPayload? _defaultInstance;
+
+  @$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)
+  RepeatedFieldOrder get fieldOrders => $_getN(1);
+  @$pb.TagNumber(2)
+  set fieldOrders(RepeatedFieldOrder v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasFieldOrders() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearFieldOrders() => clearField(2);
+  @$pb.TagNumber(2)
+  RepeatedFieldOrder ensureFieldOrders() => $_ensure(1);
+}
+
+enum FieldChangesetPayload_OneOfName {
+  name, 
+  notSet
+}
+
+enum FieldChangesetPayload_OneOfDesc {
+  desc, 
+  notSet
+}
+
+enum FieldChangesetPayload_OneOfFieldType {
+  fieldType, 
+  notSet
+}
+
+enum FieldChangesetPayload_OneOfFrozen {
+  frozen, 
+  notSet
+}
+
+enum FieldChangesetPayload_OneOfVisibility {
+  visibility, 
+  notSet
+}
+
+enum FieldChangesetPayload_OneOfWidth {
+  width, 
+  notSet
+}
+
+enum FieldChangesetPayload_OneOfTypeOptionData {
+  typeOptionData, 
+  notSet
+}
+
+class FieldChangesetPayload extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, FieldChangesetPayload_OneOfName> _FieldChangesetPayload_OneOfNameByTag = {
+    3 : FieldChangesetPayload_OneOfName.name,
+    0 : FieldChangesetPayload_OneOfName.notSet
+  };
+  static const $core.Map<$core.int, FieldChangesetPayload_OneOfDesc> _FieldChangesetPayload_OneOfDescByTag = {
+    4 : FieldChangesetPayload_OneOfDesc.desc,
+    0 : FieldChangesetPayload_OneOfDesc.notSet
+  };
+  static const $core.Map<$core.int, FieldChangesetPayload_OneOfFieldType> _FieldChangesetPayload_OneOfFieldTypeByTag = {
+    5 : FieldChangesetPayload_OneOfFieldType.fieldType,
+    0 : FieldChangesetPayload_OneOfFieldType.notSet
+  };
+  static const $core.Map<$core.int, FieldChangesetPayload_OneOfFrozen> _FieldChangesetPayload_OneOfFrozenByTag = {
+    6 : FieldChangesetPayload_OneOfFrozen.frozen,
+    0 : FieldChangesetPayload_OneOfFrozen.notSet
+  };
+  static const $core.Map<$core.int, FieldChangesetPayload_OneOfVisibility> _FieldChangesetPayload_OneOfVisibilityByTag = {
+    7 : FieldChangesetPayload_OneOfVisibility.visibility,
+    0 : FieldChangesetPayload_OneOfVisibility.notSet
+  };
+  static const $core.Map<$core.int, FieldChangesetPayload_OneOfWidth> _FieldChangesetPayload_OneOfWidthByTag = {
+    8 : FieldChangesetPayload_OneOfWidth.width,
+    0 : FieldChangesetPayload_OneOfWidth.notSet
+  };
+  static const $core.Map<$core.int, FieldChangesetPayload_OneOfTypeOptionData> _FieldChangesetPayload_OneOfTypeOptionDataByTag = {
+    9 : FieldChangesetPayload_OneOfTypeOptionData.typeOptionData,
+    0 : FieldChangesetPayload_OneOfTypeOptionData.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldChangesetPayload', createEmptyInstance: create)
+    ..oo(0, [3])
+    ..oo(1, [4])
+    ..oo(2, [5])
+    ..oo(3, [6])
+    ..oo(4, [7])
+    ..oo(5, [8])
+    ..oo(6, [9])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
+    ..e<FieldType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: FieldType.RichText, valueOf: FieldType.valueOf, enumValues: FieldType.values)
+    ..aOB(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'frozen')
+    ..aOB(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'visibility')
+    ..a<$core.int>(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'width', $pb.PbFieldType.O3)
+    ..a<$core.List<$core.int>>(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'typeOptionData', $pb.PbFieldType.OY)
+    ..hasRequiredFields = false
+  ;
+
+  FieldChangesetPayload._() : super();
+  factory FieldChangesetPayload({
+    $core.String? fieldId,
+    $core.String? gridId,
+    $core.String? name,
+    $core.String? desc,
+    FieldType? fieldType,
+    $core.bool? frozen,
+    $core.bool? visibility,
+    $core.int? width,
+    $core.List<$core.int>? typeOptionData,
+  }) {
+    final _result = create();
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (desc != null) {
+      _result.desc = desc;
+    }
+    if (fieldType != null) {
+      _result.fieldType = fieldType;
+    }
+    if (frozen != null) {
+      _result.frozen = frozen;
+    }
+    if (visibility != null) {
+      _result.visibility = visibility;
+    }
+    if (width != null) {
+      _result.width = width;
+    }
+    if (typeOptionData != null) {
+      _result.typeOptionData = typeOptionData;
+    }
+    return _result;
+  }
+  factory FieldChangesetPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory FieldChangesetPayload.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')
+  FieldChangesetPayload clone() => FieldChangesetPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  FieldChangesetPayload copyWith(void Function(FieldChangesetPayload) updates) => super.copyWith((message) => updates(message as FieldChangesetPayload)) as FieldChangesetPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static FieldChangesetPayload create() => FieldChangesetPayload._();
+  FieldChangesetPayload createEmptyInstance() => create();
+  static $pb.PbList<FieldChangesetPayload> createRepeated() => $pb.PbList<FieldChangesetPayload>();
+  @$core.pragma('dart2js:noInline')
+  static FieldChangesetPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FieldChangesetPayload>(create);
+  static FieldChangesetPayload? _defaultInstance;
+
+  FieldChangesetPayload_OneOfName whichOneOfName() => _FieldChangesetPayload_OneOfNameByTag[$_whichOneof(0)]!;
+  void clearOneOfName() => clearField($_whichOneof(0));
+
+  FieldChangesetPayload_OneOfDesc whichOneOfDesc() => _FieldChangesetPayload_OneOfDescByTag[$_whichOneof(1)]!;
+  void clearOneOfDesc() => clearField($_whichOneof(1));
+
+  FieldChangesetPayload_OneOfFieldType whichOneOfFieldType() => _FieldChangesetPayload_OneOfFieldTypeByTag[$_whichOneof(2)]!;
+  void clearOneOfFieldType() => clearField($_whichOneof(2));
+
+  FieldChangesetPayload_OneOfFrozen whichOneOfFrozen() => _FieldChangesetPayload_OneOfFrozenByTag[$_whichOneof(3)]!;
+  void clearOneOfFrozen() => clearField($_whichOneof(3));
+
+  FieldChangesetPayload_OneOfVisibility whichOneOfVisibility() => _FieldChangesetPayload_OneOfVisibilityByTag[$_whichOneof(4)]!;
+  void clearOneOfVisibility() => clearField($_whichOneof(4));
+
+  FieldChangesetPayload_OneOfWidth whichOneOfWidth() => _FieldChangesetPayload_OneOfWidthByTag[$_whichOneof(5)]!;
+  void clearOneOfWidth() => clearField($_whichOneof(5));
+
+  FieldChangesetPayload_OneOfTypeOptionData whichOneOfTypeOptionData() => _FieldChangesetPayload_OneOfTypeOptionDataByTag[$_whichOneof(6)]!;
+  void clearOneOfTypeOptionData() => clearField($_whichOneof(6));
+
+  @$pb.TagNumber(1)
+  $core.String get fieldId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set fieldId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasFieldId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearFieldId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get gridId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set gridId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasGridId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearGridId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get name => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set name($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasName() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearName() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $core.String get desc => $_getSZ(3);
+  @$pb.TagNumber(4)
+  set desc($core.String v) { $_setString(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasDesc() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearDesc() => clearField(4);
+
+  @$pb.TagNumber(5)
+  FieldType get fieldType => $_getN(4);
+  @$pb.TagNumber(5)
+  set fieldType(FieldType v) { setField(5, v); }
+  @$pb.TagNumber(5)
+  $core.bool hasFieldType() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearFieldType() => clearField(5);
+
+  @$pb.TagNumber(6)
+  $core.bool get frozen => $_getBF(5);
+  @$pb.TagNumber(6)
+  set frozen($core.bool v) { $_setBool(5, v); }
+  @$pb.TagNumber(6)
+  $core.bool hasFrozen() => $_has(5);
+  @$pb.TagNumber(6)
+  void clearFrozen() => clearField(6);
+
+  @$pb.TagNumber(7)
+  $core.bool get visibility => $_getBF(6);
+  @$pb.TagNumber(7)
+  set visibility($core.bool v) { $_setBool(6, v); }
+  @$pb.TagNumber(7)
+  $core.bool hasVisibility() => $_has(6);
+  @$pb.TagNumber(7)
+  void clearVisibility() => clearField(7);
+
+  @$pb.TagNumber(8)
+  $core.int get width => $_getIZ(7);
+  @$pb.TagNumber(8)
+  set width($core.int v) { $_setSignedInt32(7, v); }
+  @$pb.TagNumber(8)
+  $core.bool hasWidth() => $_has(7);
+  @$pb.TagNumber(8)
+  void clearWidth() => clearField(8);
+
+  @$pb.TagNumber(9)
+  $core.List<$core.int> get typeOptionData => $_getN(8);
+  @$pb.TagNumber(9)
+  set typeOptionData($core.List<$core.int> v) { $_setBytes(8, v); }
+  @$pb.TagNumber(9)
+  $core.bool hasTypeOptionData() => $_has(8);
+  @$pb.TagNumber(9)
+  void clearTypeOptionData() => clearField(9);
+}
+

+ 36 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pbenum.dart

@@ -0,0 +1,36 @@
+///
+//  Generated code. Do not modify.
+//  source: field.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME
+import 'dart:core' as $core;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class FieldType extends $pb.ProtobufEnum {
+  static const FieldType RichText = FieldType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
+  static const FieldType Number = FieldType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Number');
+  static const FieldType DateTime = FieldType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DateTime');
+  static const FieldType SingleSelect = FieldType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SingleSelect');
+  static const FieldType MultiSelect = FieldType._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MultiSelect');
+  static const FieldType Checkbox = FieldType._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Checkbox');
+  static const FieldType URL = FieldType._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'URL');
+
+  static const $core.List<FieldType> values = <FieldType> [
+    RichText,
+    Number,
+    DateTime,
+    SingleSelect,
+    MultiSelect,
+    Checkbox,
+    URL,
+  ];
+
+  static final $core.Map<$core.int, FieldType> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static FieldType? valueOf($core.int value) => _byValue[value];
+
+  const FieldType._($core.int v, $core.String n) : super(v, n);
+}
+

+ 215 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pbjson.dart

@@ -0,0 +1,215 @@
+///
+//  Generated code. Do not modify.
+//  source: field.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use fieldTypeDescriptor instead')
+const FieldType$json = const {
+  '1': 'FieldType',
+  '2': const [
+    const {'1': 'RichText', '2': 0},
+    const {'1': 'Number', '2': 1},
+    const {'1': 'DateTime', '2': 2},
+    const {'1': 'SingleSelect', '2': 3},
+    const {'1': 'MultiSelect', '2': 4},
+    const {'1': 'Checkbox', '2': 5},
+    const {'1': 'URL', '2': 6},
+  ],
+};
+
+/// Descriptor for `FieldType`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List fieldTypeDescriptor = $convert.base64Decode('CglGaWVsZFR5cGUSDAoIUmljaFRleHQQABIKCgZOdW1iZXIQARIMCghEYXRlVGltZRACEhAKDFNpbmdsZVNlbGVjdBADEg8KC011bHRpU2VsZWN0EAQSDAoIQ2hlY2tib3gQBRIHCgNVUkwQBg==');
+@$core.Deprecated('Use fieldDescriptor instead')
+const Field$json = const {
+  '1': 'Field',
+  '2': const [
+    const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
+    const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
+    const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
+    const {'1': 'field_type', '3': 4, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
+    const {'1': 'frozen', '3': 5, '4': 1, '5': 8, '10': 'frozen'},
+    const {'1': 'visibility', '3': 6, '4': 1, '5': 8, '10': 'visibility'},
+    const {'1': 'width', '3': 7, '4': 1, '5': 5, '10': 'width'},
+    const {'1': 'is_primary', '3': 8, '4': 1, '5': 8, '10': 'isPrimary'},
+  ],
+};
+
+/// Descriptor for `Field`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fieldDescriptor = $convert.base64Decode('CgVGaWVsZBIOCgJpZBgBIAEoCVICaWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEikKCmZpZWxkX3R5cGUYBCABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZRIWCgZmcm96ZW4YBSABKAhSBmZyb3plbhIeCgp2aXNpYmlsaXR5GAYgASgIUgp2aXNpYmlsaXR5EhQKBXdpZHRoGAcgASgFUgV3aWR0aBIdCgppc19wcmltYXJ5GAggASgIUglpc1ByaW1hcnk=');
+@$core.Deprecated('Use fieldOrderDescriptor instead')
+const FieldOrder$json = const {
+  '1': 'FieldOrder',
+  '2': const [
+    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
+  ],
+};
+
+/// Descriptor for `FieldOrder`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fieldOrderDescriptor = $convert.base64Decode('CgpGaWVsZE9yZGVyEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElk');
+@$core.Deprecated('Use gridFieldChangesetDescriptor instead')
+const GridFieldChangeset$json = const {
+  '1': 'GridFieldChangeset',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'inserted_fields', '3': 2, '4': 3, '5': 11, '6': '.IndexField', '10': 'insertedFields'},
+    const {'1': 'deleted_fields', '3': 3, '4': 3, '5': 11, '6': '.FieldOrder', '10': 'deletedFields'},
+    const {'1': 'updated_fields', '3': 4, '4': 3, '5': 11, '6': '.Field', '10': 'updatedFields'},
+  ],
+};
+
+/// Descriptor for `GridFieldChangeset`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List gridFieldChangesetDescriptor = $convert.base64Decode('ChJHcmlkRmllbGRDaGFuZ2VzZXQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEjQKD2luc2VydGVkX2ZpZWxkcxgCIAMoCzILLkluZGV4RmllbGRSDmluc2VydGVkRmllbGRzEjIKDmRlbGV0ZWRfZmllbGRzGAMgAygLMgsuRmllbGRPcmRlclINZGVsZXRlZEZpZWxkcxItCg51cGRhdGVkX2ZpZWxkcxgEIAMoCzIGLkZpZWxkUg11cGRhdGVkRmllbGRz');
+@$core.Deprecated('Use indexFieldDescriptor instead')
+const IndexField$json = const {
+  '1': 'IndexField',
+  '2': const [
+    const {'1': 'field', '3': 1, '4': 1, '5': 11, '6': '.Field', '10': 'field'},
+    const {'1': 'index', '3': 2, '4': 1, '5': 5, '10': 'index'},
+  ],
+};
+
+/// Descriptor for `IndexField`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List indexFieldDescriptor = $convert.base64Decode('CgpJbmRleEZpZWxkEhwKBWZpZWxkGAEgASgLMgYuRmllbGRSBWZpZWxkEhQKBWluZGV4GAIgASgFUgVpbmRleA==');
+@$core.Deprecated('Use getEditFieldContextPayloadDescriptor instead')
+const GetEditFieldContextPayload$json = const {
+  '1': 'GetEditFieldContextPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'fieldId'},
+    const {'1': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
+  ],
+  '8': const [
+    const {'1': 'one_of_field_id'},
+  ],
+};
+
+/// Descriptor for `GetEditFieldContextPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List getEditFieldContextPayloadDescriptor = $convert.base64Decode('ChpHZXRFZGl0RmllbGRDb250ZXh0UGF5bG9hZBIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSGwoIZmllbGRfaWQYAiABKAlIAFIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGVCEQoPb25lX29mX2ZpZWxkX2lk');
+@$core.Deprecated('Use editFieldPayloadDescriptor instead')
+const EditFieldPayload$json = const {
+  '1': 'EditFieldPayload',
+  '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': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
+    const {'1': 'create_if_not_exist', '3': 4, '4': 1, '5': 8, '10': 'createIfNotExist'},
+  ],
+};
+
+/// Descriptor for `EditFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGUSLQoTY3JlYXRlX2lmX25vdF9leGlzdBgEIAEoCFIQY3JlYXRlSWZOb3RFeGlzdA==');
+@$core.Deprecated('Use fieldTypeOptionContextDescriptor instead')
+const FieldTypeOptionContext$json = const {
+  '1': 'FieldTypeOptionContext',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'grid_field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'gridField'},
+    const {'1': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
+  ],
+};
+
+/// Descriptor for `FieldTypeOptionContext`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fieldTypeOptionContextDescriptor = $convert.base64Decode('ChZGaWVsZFR5cGVPcHRpb25Db250ZXh0EhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIlCgpncmlkX2ZpZWxkGAIgASgLMgYuRmllbGRSCWdyaWRGaWVsZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ==');
+@$core.Deprecated('Use fieldTypeOptionDataDescriptor instead')
+const FieldTypeOptionData$json = const {
+  '1': 'FieldTypeOptionData',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'field'},
+    const {'1': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
+  ],
+};
+
+/// Descriptor for `FieldTypeOptionData`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fieldTypeOptionDataDescriptor = $convert.base64Decode('ChNGaWVsZFR5cGVPcHRpb25EYXRhEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIcCgVmaWVsZBgCIAEoCzIGLkZpZWxkUgVmaWVsZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ==');
+@$core.Deprecated('Use repeatedFieldDescriptor instead')
+const RepeatedField$json = const {
+  '1': 'RepeatedField',
+  '2': const [
+    const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.Field', '10': 'items'},
+  ],
+};
+
+/// Descriptor for `RepeatedField`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List repeatedFieldDescriptor = $convert.base64Decode('Cg1SZXBlYXRlZEZpZWxkEhwKBWl0ZW1zGAEgAygLMgYuRmllbGRSBWl0ZW1z');
+@$core.Deprecated('Use repeatedFieldOrderDescriptor instead')
+const RepeatedFieldOrder$json = const {
+  '1': 'RepeatedFieldOrder',
+  '2': const [
+    const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.FieldOrder', '10': 'items'},
+  ],
+};
+
+/// Descriptor for `RepeatedFieldOrder`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List repeatedFieldOrderDescriptor = $convert.base64Decode('ChJSZXBlYXRlZEZpZWxkT3JkZXISIQoFaXRlbXMYASADKAsyCy5GaWVsZE9yZGVyUgVpdGVtcw==');
+@$core.Deprecated('Use insertFieldPayloadDescriptor instead')
+const InsertFieldPayload$json = const {
+  '1': 'InsertFieldPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'field'},
+    const {'1': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
+    const {'1': 'start_field_id', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'startFieldId'},
+  ],
+  '8': const [
+    const {'1': 'one_of_start_field_id'},
+  ],
+};
+
+/// Descriptor for `InsertFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List insertFieldPayloadDescriptor = $convert.base64Decode('ChJJbnNlcnRGaWVsZFBheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhwKBWZpZWxkGAIgASgLMgYuRmllbGRSBWZpZWxkEigKEHR5cGVfb3B0aW9uX2RhdGEYAyABKAxSDnR5cGVPcHRpb25EYXRhEiYKDnN0YXJ0X2ZpZWxkX2lkGAQgASgJSABSDHN0YXJ0RmllbGRJZEIXChVvbmVfb2Zfc3RhcnRfZmllbGRfaWQ=');
+@$core.Deprecated('Use updateFieldTypeOptionPayloadDescriptor instead')
+const UpdateFieldTypeOptionPayload$json = const {
+  '1': 'UpdateFieldTypeOptionPayload',
+  '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': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
+  ],
+};
+
+/// Descriptor for `UpdateFieldTypeOptionPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List updateFieldTypeOptionPayloadDescriptor = $convert.base64Decode('ChxVcGRhdGVGaWVsZFR5cGVPcHRpb25QYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ==');
+@$core.Deprecated('Use queryFieldPayloadDescriptor instead')
+const QueryFieldPayload$json = const {
+  '1': 'QueryFieldPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'field_orders', '3': 2, '4': 1, '5': 11, '6': '.RepeatedFieldOrder', '10': 'fieldOrders'},
+  ],
+};
+
+/// Descriptor for `QueryFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List queryFieldPayloadDescriptor = $convert.base64Decode('ChFRdWVyeUZpZWxkUGF5bG9hZBIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSNgoMZmllbGRfb3JkZXJzGAIgASgLMhMuUmVwZWF0ZWRGaWVsZE9yZGVyUgtmaWVsZE9yZGVycw==');
+@$core.Deprecated('Use fieldChangesetPayloadDescriptor instead')
+const FieldChangesetPayload$json = const {
+  '1': 'FieldChangesetPayload',
+  '2': const [
+    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
+    const {'1': 'grid_id', '3': 2, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'name', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'name'},
+    const {'1': 'desc', '3': 4, '4': 1, '5': 9, '9': 1, '10': 'desc'},
+    const {'1': 'field_type', '3': 5, '4': 1, '5': 14, '6': '.FieldType', '9': 2, '10': 'fieldType'},
+    const {'1': 'frozen', '3': 6, '4': 1, '5': 8, '9': 3, '10': 'frozen'},
+    const {'1': 'visibility', '3': 7, '4': 1, '5': 8, '9': 4, '10': 'visibility'},
+    const {'1': 'width', '3': 8, '4': 1, '5': 5, '9': 5, '10': 'width'},
+    const {'1': 'type_option_data', '3': 9, '4': 1, '5': 12, '9': 6, '10': 'typeOptionData'},
+  ],
+  '8': const [
+    const {'1': 'one_of_name'},
+    const {'1': 'one_of_desc'},
+    const {'1': 'one_of_field_type'},
+    const {'1': 'one_of_frozen'},
+    const {'1': 'one_of_visibility'},
+    const {'1': 'one_of_width'},
+    const {'1': 'one_of_type_option_data'},
+  ],
+};
+
+/// Descriptor for `FieldChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fieldChangesetPayloadDescriptor = $convert.base64Decode('ChVGaWVsZENoYW5nZXNldFBheWxvYWQSGQoIZmllbGRfaWQYASABKAlSB2ZpZWxkSWQSFwoHZ3JpZF9pZBgCIAEoCVIGZ3JpZElkEhQKBG5hbWUYAyABKAlIAFIEbmFtZRIUCgRkZXNjGAQgASgJSAFSBGRlc2MSKwoKZmllbGRfdHlwZRgFIAEoDjIKLkZpZWxkVHlwZUgCUglmaWVsZFR5cGUSGAoGZnJvemVuGAYgASgISANSBmZyb3plbhIgCgp2aXNpYmlsaXR5GAcgASgISARSCnZpc2liaWxpdHkSFgoFd2lkdGgYCCABKAVIBVIFd2lkdGgSKgoQdHlwZV9vcHRpb25fZGF0YRgJIAEoDEgGUg50eXBlT3B0aW9uRGF0YUINCgtvbmVfb2ZfbmFtZUINCgtvbmVfb2ZfZGVzY0ITChFvbmVfb2ZfZmllbGRfdHlwZUIPCg1vbmVfb2ZfZnJvemVuQhMKEW9uZV9vZl92aXNpYmlsaXR5Qg4KDG9uZV9vZl93aWR0aEIZChdvbmVfb2ZfdHlwZV9vcHRpb25fZGF0YQ==');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/field.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: field.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'field.pb.dart';
+

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 7 - 986
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart


+ 0 - 25
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbenum.dart

@@ -24,28 +24,3 @@ class MoveItemType extends $pb.ProtobufEnum {
   const MoveItemType._($core.int v, $core.String n) : super(v, n);
 }
 
-class FieldType extends $pb.ProtobufEnum {
-  static const FieldType RichText = FieldType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
-  static const FieldType Number = FieldType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Number');
-  static const FieldType DateTime = FieldType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DateTime');
-  static const FieldType SingleSelect = FieldType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SingleSelect');
-  static const FieldType MultiSelect = FieldType._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MultiSelect');
-  static const FieldType Checkbox = FieldType._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Checkbox');
-  static const FieldType URL = FieldType._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'URL');
-
-  static const $core.List<FieldType> values = <FieldType> [
-    RichText,
-    Number,
-    DateTime,
-    SingleSelect,
-    MultiSelect,
-    Checkbox,
-    URL,
-  ];
-
-  static final $core.Map<$core.int, FieldType> _byValue = $pb.ProtobufEnum.initByValue(values);
-  static FieldType? valueOf($core.int value) => _byValue[value];
-
-  const FieldType._($core.int v, $core.String n) : super(v, n);
-}
-

+ 0 - 205
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart

@@ -19,22 +19,6 @@ const MoveItemType$json = const {
 
 /// Descriptor for `MoveItemType`. Decode as a `google.protobuf.EnumDescriptorProto`.
 final $typed_data.Uint8List moveItemTypeDescriptor = $convert.base64Decode('CgxNb3ZlSXRlbVR5cGUSDQoJTW92ZUZpZWxkEAASCwoHTW92ZVJvdxAB');
-@$core.Deprecated('Use fieldTypeDescriptor instead')
-const FieldType$json = const {
-  '1': 'FieldType',
-  '2': const [
-    const {'1': 'RichText', '2': 0},
-    const {'1': 'Number', '2': 1},
-    const {'1': 'DateTime', '2': 2},
-    const {'1': 'SingleSelect', '2': 3},
-    const {'1': 'MultiSelect', '2': 4},
-    const {'1': 'Checkbox', '2': 5},
-    const {'1': 'URL', '2': 6},
-  ],
-};
-
-/// Descriptor for `FieldType`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List fieldTypeDescriptor = $convert.base64Decode('CglGaWVsZFR5cGUSDAoIUmljaFRleHQQABIKCgZOdW1iZXIQARIMCghEYXRlVGltZRACEhAKDFNpbmdsZVNlbGVjdBADEg8KC011bHRpU2VsZWN0EAQSDAoIQ2hlY2tib3gQBRIHCgNVUkwQBg==');
 @$core.Deprecated('Use gridDescriptor instead')
 const Grid$json = const {
   '1': 'Grid',
@@ -47,129 +31,6 @@ const Grid$json = const {
 
 /// Descriptor for `Grid`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List gridDescriptor = $convert.base64Decode('CgRHcmlkEg4KAmlkGAEgASgJUgJpZBIuCgxmaWVsZF9vcmRlcnMYAiADKAsyCy5GaWVsZE9yZGVyUgtmaWVsZE9yZGVycxIyCgxibG9ja19vcmRlcnMYAyADKAsyDy5HcmlkQmxvY2tPcmRlclILYmxvY2tPcmRlcnM=');
-@$core.Deprecated('Use fieldDescriptor instead')
-const Field$json = const {
-  '1': 'Field',
-  '2': const [
-    const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
-    const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
-    const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
-    const {'1': 'field_type', '3': 4, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
-    const {'1': 'frozen', '3': 5, '4': 1, '5': 8, '10': 'frozen'},
-    const {'1': 'visibility', '3': 6, '4': 1, '5': 8, '10': 'visibility'},
-    const {'1': 'width', '3': 7, '4': 1, '5': 5, '10': 'width'},
-    const {'1': 'is_primary', '3': 8, '4': 1, '5': 8, '10': 'isPrimary'},
-  ],
-};
-
-/// Descriptor for `Field`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List fieldDescriptor = $convert.base64Decode('CgVGaWVsZBIOCgJpZBgBIAEoCVICaWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEikKCmZpZWxkX3R5cGUYBCABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZRIWCgZmcm96ZW4YBSABKAhSBmZyb3plbhIeCgp2aXNpYmlsaXR5GAYgASgIUgp2aXNpYmlsaXR5EhQKBXdpZHRoGAcgASgFUgV3aWR0aBIdCgppc19wcmltYXJ5GAggASgIUglpc1ByaW1hcnk=');
-@$core.Deprecated('Use fieldOrderDescriptor instead')
-const FieldOrder$json = const {
-  '1': 'FieldOrder',
-  '2': const [
-    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
-  ],
-};
-
-/// Descriptor for `FieldOrder`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List fieldOrderDescriptor = $convert.base64Decode('CgpGaWVsZE9yZGVyEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElk');
-@$core.Deprecated('Use gridFieldChangesetDescriptor instead')
-const GridFieldChangeset$json = const {
-  '1': 'GridFieldChangeset',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'inserted_fields', '3': 2, '4': 3, '5': 11, '6': '.IndexField', '10': 'insertedFields'},
-    const {'1': 'deleted_fields', '3': 3, '4': 3, '5': 11, '6': '.FieldOrder', '10': 'deletedFields'},
-    const {'1': 'updated_fields', '3': 4, '4': 3, '5': 11, '6': '.Field', '10': 'updatedFields'},
-  ],
-};
-
-/// Descriptor for `GridFieldChangeset`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridFieldChangesetDescriptor = $convert.base64Decode('ChJHcmlkRmllbGRDaGFuZ2VzZXQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEjQKD2luc2VydGVkX2ZpZWxkcxgCIAMoCzILLkluZGV4RmllbGRSDmluc2VydGVkRmllbGRzEjIKDmRlbGV0ZWRfZmllbGRzGAMgAygLMgsuRmllbGRPcmRlclINZGVsZXRlZEZpZWxkcxItCg51cGRhdGVkX2ZpZWxkcxgEIAMoCzIGLkZpZWxkUg11cGRhdGVkRmllbGRz');
-@$core.Deprecated('Use indexFieldDescriptor instead')
-const IndexField$json = const {
-  '1': 'IndexField',
-  '2': const [
-    const {'1': 'field', '3': 1, '4': 1, '5': 11, '6': '.Field', '10': 'field'},
-    const {'1': 'index', '3': 2, '4': 1, '5': 5, '10': 'index'},
-  ],
-};
-
-/// Descriptor for `IndexField`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List indexFieldDescriptor = $convert.base64Decode('CgpJbmRleEZpZWxkEhwKBWZpZWxkGAEgASgLMgYuRmllbGRSBWZpZWxkEhQKBWluZGV4GAIgASgFUgVpbmRleA==');
-@$core.Deprecated('Use getEditFieldContextPayloadDescriptor instead')
-const GetEditFieldContextPayload$json = const {
-  '1': 'GetEditFieldContextPayload',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'fieldId'},
-    const {'1': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
-  ],
-  '8': const [
-    const {'1': 'one_of_field_id'},
-  ],
-};
-
-/// Descriptor for `GetEditFieldContextPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List getEditFieldContextPayloadDescriptor = $convert.base64Decode('ChpHZXRFZGl0RmllbGRDb250ZXh0UGF5bG9hZBIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSGwoIZmllbGRfaWQYAiABKAlIAFIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGVCEQoPb25lX29mX2ZpZWxkX2lk');
-@$core.Deprecated('Use editFieldPayloadDescriptor instead')
-const EditFieldPayload$json = const {
-  '1': 'EditFieldPayload',
-  '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': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
-    const {'1': 'create_if_not_exist', '3': 4, '4': 1, '5': 8, '10': 'createIfNotExist'},
-  ],
-};
-
-/// Descriptor for `EditFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List editFieldPayloadDescriptor = $convert.base64Decode('ChBFZGl0RmllbGRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIpCgpmaWVsZF90eXBlGAMgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGUSLQoTY3JlYXRlX2lmX25vdF9leGlzdBgEIAEoCFIQY3JlYXRlSWZOb3RFeGlzdA==');
-@$core.Deprecated('Use fieldTypeOptionContextDescriptor instead')
-const FieldTypeOptionContext$json = const {
-  '1': 'FieldTypeOptionContext',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'grid_field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'gridField'},
-    const {'1': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
-  ],
-};
-
-/// Descriptor for `FieldTypeOptionContext`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List fieldTypeOptionContextDescriptor = $convert.base64Decode('ChZGaWVsZFR5cGVPcHRpb25Db250ZXh0EhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIlCgpncmlkX2ZpZWxkGAIgASgLMgYuRmllbGRSCWdyaWRGaWVsZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ==');
-@$core.Deprecated('Use fieldTypeOptionDataDescriptor instead')
-const FieldTypeOptionData$json = const {
-  '1': 'FieldTypeOptionData',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'field'},
-    const {'1': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
-  ],
-};
-
-/// Descriptor for `FieldTypeOptionData`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List fieldTypeOptionDataDescriptor = $convert.base64Decode('ChNGaWVsZFR5cGVPcHRpb25EYXRhEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIcCgVmaWVsZBgCIAEoCzIGLkZpZWxkUgVmaWVsZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ==');
-@$core.Deprecated('Use repeatedFieldDescriptor instead')
-const RepeatedField$json = const {
-  '1': 'RepeatedField',
-  '2': const [
-    const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.Field', '10': 'items'},
-  ],
-};
-
-/// Descriptor for `RepeatedField`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List repeatedFieldDescriptor = $convert.base64Decode('Cg1SZXBlYXRlZEZpZWxkEhwKBWl0ZW1zGAEgAygLMgYuRmllbGRSBWl0ZW1z');
-@$core.Deprecated('Use repeatedFieldOrderDescriptor instead')
-const RepeatedFieldOrder$json = const {
-  '1': 'RepeatedFieldOrder',
-  '2': const [
-    const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.FieldOrder', '10': 'items'},
-  ],
-};
-
-/// Descriptor for `RepeatedFieldOrder`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List repeatedFieldOrderDescriptor = $convert.base64Decode('ChJSZXBlYXRlZEZpZWxkT3JkZXISIQoFaXRlbXMYASADKAsyCy5GaWVsZE9yZGVyUgVpdGVtcw==');
 @$core.Deprecated('Use rowOrderDescriptor instead')
 const RowOrder$json = const {
   '1': 'RowOrder',
@@ -350,45 +211,6 @@ const CreateRowPayload$json = const {
 
 /// Descriptor for `CreateRowPayload`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List createRowPayloadDescriptor = $convert.base64Decode('ChBDcmVhdGVSb3dQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIiCgxzdGFydF9yb3dfaWQYAiABKAlIAFIKc3RhcnRSb3dJZEIVChNvbmVfb2Zfc3RhcnRfcm93X2lk');
-@$core.Deprecated('Use insertFieldPayloadDescriptor instead')
-const InsertFieldPayload$json = const {
-  '1': 'InsertFieldPayload',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'field', '3': 2, '4': 1, '5': 11, '6': '.Field', '10': 'field'},
-    const {'1': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
-    const {'1': 'start_field_id', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'startFieldId'},
-  ],
-  '8': const [
-    const {'1': 'one_of_start_field_id'},
-  ],
-};
-
-/// Descriptor for `InsertFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List insertFieldPayloadDescriptor = $convert.base64Decode('ChJJbnNlcnRGaWVsZFBheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhwKBWZpZWxkGAIgASgLMgYuRmllbGRSBWZpZWxkEigKEHR5cGVfb3B0aW9uX2RhdGEYAyABKAxSDnR5cGVPcHRpb25EYXRhEiYKDnN0YXJ0X2ZpZWxkX2lkGAQgASgJSABSDHN0YXJ0RmllbGRJZEIXChVvbmVfb2Zfc3RhcnRfZmllbGRfaWQ=');
-@$core.Deprecated('Use updateFieldTypeOptionPayloadDescriptor instead')
-const UpdateFieldTypeOptionPayload$json = const {
-  '1': 'UpdateFieldTypeOptionPayload',
-  '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': 'type_option_data', '3': 3, '4': 1, '5': 12, '10': 'typeOptionData'},
-  ],
-};
-
-/// Descriptor for `UpdateFieldTypeOptionPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List updateFieldTypeOptionPayloadDescriptor = $convert.base64Decode('ChxVcGRhdGVGaWVsZFR5cGVPcHRpb25QYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIZCghmaWVsZF9pZBgCIAEoCVIHZmllbGRJZBIoChB0eXBlX29wdGlvbl9kYXRhGAMgASgMUg50eXBlT3B0aW9uRGF0YQ==');
-@$core.Deprecated('Use queryFieldPayloadDescriptor instead')
-const QueryFieldPayload$json = const {
-  '1': 'QueryFieldPayload',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'field_orders', '3': 2, '4': 1, '5': 11, '6': '.RepeatedFieldOrder', '10': 'fieldOrders'},
-  ],
-};
-
-/// Descriptor for `QueryFieldPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List queryFieldPayloadDescriptor = $convert.base64Decode('ChFRdWVyeUZpZWxkUGF5bG9hZBIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSNgoMZmllbGRfb3JkZXJzGAIgASgLMhMuUmVwZWF0ZWRGaWVsZE9yZGVyUgtmaWVsZE9yZGVycw==');
 @$core.Deprecated('Use queryGridBlocksPayloadDescriptor instead')
 const QueryGridBlocksPayload$json = const {
   '1': 'QueryGridBlocksPayload',
@@ -400,33 +222,6 @@ const QueryGridBlocksPayload$json = const {
 
 /// Descriptor for `QueryGridBlocksPayload`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List queryGridBlocksPayloadDescriptor = $convert.base64Decode('ChZRdWVyeUdyaWRCbG9ja3NQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIyCgxibG9ja19vcmRlcnMYAiADKAsyDy5HcmlkQmxvY2tPcmRlclILYmxvY2tPcmRlcnM=');
-@$core.Deprecated('Use fieldChangesetPayloadDescriptor instead')
-const FieldChangesetPayload$json = const {
-  '1': 'FieldChangesetPayload',
-  '2': const [
-    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
-    const {'1': 'grid_id', '3': 2, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'name', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'name'},
-    const {'1': 'desc', '3': 4, '4': 1, '5': 9, '9': 1, '10': 'desc'},
-    const {'1': 'field_type', '3': 5, '4': 1, '5': 14, '6': '.FieldType', '9': 2, '10': 'fieldType'},
-    const {'1': 'frozen', '3': 6, '4': 1, '5': 8, '9': 3, '10': 'frozen'},
-    const {'1': 'visibility', '3': 7, '4': 1, '5': 8, '9': 4, '10': 'visibility'},
-    const {'1': 'width', '3': 8, '4': 1, '5': 5, '9': 5, '10': 'width'},
-    const {'1': 'type_option_data', '3': 9, '4': 1, '5': 12, '9': 6, '10': 'typeOptionData'},
-  ],
-  '8': const [
-    const {'1': 'one_of_name'},
-    const {'1': 'one_of_desc'},
-    const {'1': 'one_of_field_type'},
-    const {'1': 'one_of_frozen'},
-    const {'1': 'one_of_visibility'},
-    const {'1': 'one_of_width'},
-    const {'1': 'one_of_type_option_data'},
-  ],
-};
-
-/// Descriptor for `FieldChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List fieldChangesetPayloadDescriptor = $convert.base64Decode('ChVGaWVsZENoYW5nZXNldFBheWxvYWQSGQoIZmllbGRfaWQYASABKAlSB2ZpZWxkSWQSFwoHZ3JpZF9pZBgCIAEoCVIGZ3JpZElkEhQKBG5hbWUYAyABKAlIAFIEbmFtZRIUCgRkZXNjGAQgASgJSAFSBGRlc2MSKwoKZmllbGRfdHlwZRgFIAEoDjIKLkZpZWxkVHlwZUgCUglmaWVsZFR5cGUSGAoGZnJvemVuGAYgASgISANSBmZyb3plbhIgCgp2aXNpYmlsaXR5GAcgASgISARSCnZpc2liaWxpdHkSFgoFd2lkdGgYCCABKAVIBVIFd2lkdGgSKgoQdHlwZV9vcHRpb25fZGF0YRgJIAEoDEgGUg50eXBlT3B0aW9uRGF0YUINCgtvbmVfb2ZfbmFtZUINCgtvbmVfb2ZfZGVzY0ITChFvbmVfb2ZfZmllbGRfdHlwZUIPCg1vbmVfb2ZfZnJvemVuQhMKEW9uZV9vZl92aXNpYmlsaXR5Qg4KDG9uZV9vZl93aWR0aEIZChdvbmVfb2ZfdHlwZV9vcHRpb25fZGF0YQ==');
 @$core.Deprecated('Use moveItemPayloadDescriptor instead')
 const MoveItemPayload$json = const {
   '1': 'MoveItemPayload',

+ 433 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pb.dart

@@ -0,0 +1,433 @@
+///
+//  Generated code. Do not modify.
+//  source: grid_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class ViewExtData extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ViewExtData', createEmptyInstance: create)
+    ..aOM<ViewFilter>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'filter', subBuilder: ViewFilter.create)
+    ..aOM<ViewGroup>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'group', subBuilder: ViewGroup.create)
+    ..aOM<ViewSort>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'sort', subBuilder: ViewSort.create)
+    ..hasRequiredFields = false
+  ;
+
+  ViewExtData._() : super();
+  factory ViewExtData({
+    ViewFilter? filter,
+    ViewGroup? group,
+    ViewSort? sort,
+  }) {
+    final _result = create();
+    if (filter != null) {
+      _result.filter = filter;
+    }
+    if (group != null) {
+      _result.group = group;
+    }
+    if (sort != null) {
+      _result.sort = sort;
+    }
+    return _result;
+  }
+  factory ViewExtData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ViewExtData.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')
+  ViewExtData clone() => ViewExtData()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  ViewExtData copyWith(void Function(ViewExtData) updates) => super.copyWith((message) => updates(message as ViewExtData)) as ViewExtData; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static ViewExtData create() => ViewExtData._();
+  ViewExtData createEmptyInstance() => create();
+  static $pb.PbList<ViewExtData> createRepeated() => $pb.PbList<ViewExtData>();
+  @$core.pragma('dart2js:noInline')
+  static ViewExtData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ViewExtData>(create);
+  static ViewExtData? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  ViewFilter get filter => $_getN(0);
+  @$pb.TagNumber(1)
+  set filter(ViewFilter v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasFilter() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearFilter() => clearField(1);
+  @$pb.TagNumber(1)
+  ViewFilter ensureFilter() => $_ensure(0);
+
+  @$pb.TagNumber(2)
+  ViewGroup get group => $_getN(1);
+  @$pb.TagNumber(2)
+  set group(ViewGroup v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasGroup() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearGroup() => clearField(2);
+  @$pb.TagNumber(2)
+  ViewGroup ensureGroup() => $_ensure(1);
+
+  @$pb.TagNumber(3)
+  ViewSort get sort => $_getN(2);
+  @$pb.TagNumber(3)
+  set sort(ViewSort v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasSort() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearSort() => clearField(3);
+  @$pb.TagNumber(3)
+  ViewSort ensureSort() => $_ensure(2);
+}
+
+enum ViewFilter_OneOfFieldId {
+  fieldId, 
+  notSet
+}
+
+class ViewFilter extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, ViewFilter_OneOfFieldId> _ViewFilter_OneOfFieldIdByTag = {
+    1 : ViewFilter_OneOfFieldId.fieldId,
+    0 : ViewFilter_OneOfFieldId.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ViewFilter', createEmptyInstance: create)
+    ..oo(0, [1])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..hasRequiredFields = false
+  ;
+
+  ViewFilter._() : super();
+  factory ViewFilter({
+    $core.String? fieldId,
+  }) {
+    final _result = create();
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    return _result;
+  }
+  factory ViewFilter.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ViewFilter.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')
+  ViewFilter clone() => ViewFilter()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  ViewFilter copyWith(void Function(ViewFilter) updates) => super.copyWith((message) => updates(message as ViewFilter)) as ViewFilter; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static ViewFilter create() => ViewFilter._();
+  ViewFilter createEmptyInstance() => create();
+  static $pb.PbList<ViewFilter> createRepeated() => $pb.PbList<ViewFilter>();
+  @$core.pragma('dart2js:noInline')
+  static ViewFilter getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ViewFilter>(create);
+  static ViewFilter? _defaultInstance;
+
+  ViewFilter_OneOfFieldId whichOneOfFieldId() => _ViewFilter_OneOfFieldIdByTag[$_whichOneof(0)]!;
+  void clearOneOfFieldId() => clearField($_whichOneof(0));
+
+  @$pb.TagNumber(1)
+  $core.String get fieldId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set fieldId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasFieldId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearFieldId() => clearField(1);
+}
+
+enum ViewGroup_OneOfGroupFieldId {
+  groupFieldId, 
+  notSet
+}
+
+enum ViewGroup_OneOfSubGroupFieldId {
+  subGroupFieldId, 
+  notSet
+}
+
+class ViewGroup extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, ViewGroup_OneOfGroupFieldId> _ViewGroup_OneOfGroupFieldIdByTag = {
+    1 : ViewGroup_OneOfGroupFieldId.groupFieldId,
+    0 : ViewGroup_OneOfGroupFieldId.notSet
+  };
+  static const $core.Map<$core.int, ViewGroup_OneOfSubGroupFieldId> _ViewGroup_OneOfSubGroupFieldIdByTag = {
+    2 : ViewGroup_OneOfSubGroupFieldId.subGroupFieldId,
+    0 : ViewGroup_OneOfSubGroupFieldId.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ViewGroup', createEmptyInstance: create)
+    ..oo(0, [1])
+    ..oo(1, [2])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'groupFieldId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'subGroupFieldId')
+    ..hasRequiredFields = false
+  ;
+
+  ViewGroup._() : super();
+  factory ViewGroup({
+    $core.String? groupFieldId,
+    $core.String? subGroupFieldId,
+  }) {
+    final _result = create();
+    if (groupFieldId != null) {
+      _result.groupFieldId = groupFieldId;
+    }
+    if (subGroupFieldId != null) {
+      _result.subGroupFieldId = subGroupFieldId;
+    }
+    return _result;
+  }
+  factory ViewGroup.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ViewGroup.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')
+  ViewGroup clone() => ViewGroup()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  ViewGroup copyWith(void Function(ViewGroup) updates) => super.copyWith((message) => updates(message as ViewGroup)) as ViewGroup; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static ViewGroup create() => ViewGroup._();
+  ViewGroup createEmptyInstance() => create();
+  static $pb.PbList<ViewGroup> createRepeated() => $pb.PbList<ViewGroup>();
+  @$core.pragma('dart2js:noInline')
+  static ViewGroup getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ViewGroup>(create);
+  static ViewGroup? _defaultInstance;
+
+  ViewGroup_OneOfGroupFieldId whichOneOfGroupFieldId() => _ViewGroup_OneOfGroupFieldIdByTag[$_whichOneof(0)]!;
+  void clearOneOfGroupFieldId() => clearField($_whichOneof(0));
+
+  ViewGroup_OneOfSubGroupFieldId whichOneOfSubGroupFieldId() => _ViewGroup_OneOfSubGroupFieldIdByTag[$_whichOneof(1)]!;
+  void clearOneOfSubGroupFieldId() => clearField($_whichOneof(1));
+
+  @$pb.TagNumber(1)
+  $core.String get groupFieldId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set groupFieldId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasGroupFieldId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearGroupFieldId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get subGroupFieldId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set subGroupFieldId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasSubGroupFieldId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearSubGroupFieldId() => clearField(2);
+}
+
+enum ViewSort_OneOfFieldId {
+  fieldId, 
+  notSet
+}
+
+class ViewSort extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, ViewSort_OneOfFieldId> _ViewSort_OneOfFieldIdByTag = {
+    1 : ViewSort_OneOfFieldId.fieldId,
+    0 : ViewSort_OneOfFieldId.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ViewSort', createEmptyInstance: create)
+    ..oo(0, [1])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..hasRequiredFields = false
+  ;
+
+  ViewSort._() : super();
+  factory ViewSort({
+    $core.String? fieldId,
+  }) {
+    final _result = create();
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    return _result;
+  }
+  factory ViewSort.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory ViewSort.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')
+  ViewSort clone() => ViewSort()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  ViewSort copyWith(void Function(ViewSort) updates) => super.copyWith((message) => updates(message as ViewSort)) as ViewSort; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static ViewSort create() => ViewSort._();
+  ViewSort createEmptyInstance() => create();
+  static $pb.PbList<ViewSort> createRepeated() => $pb.PbList<ViewSort>();
+  @$core.pragma('dart2js:noInline')
+  static ViewSort getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ViewSort>(create);
+  static ViewSort? _defaultInstance;
+
+  ViewSort_OneOfFieldId whichOneOfFieldId() => _ViewSort_OneOfFieldIdByTag[$_whichOneof(0)]!;
+  void clearOneOfFieldId() => clearField($_whichOneof(0));
+
+  @$pb.TagNumber(1)
+  $core.String get fieldId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set fieldId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasFieldId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearFieldId() => clearField(1);
+}
+
+enum GridInfoChangesetPayload_OneOfFilter {
+  filter, 
+  notSet
+}
+
+enum GridInfoChangesetPayload_OneOfGroup {
+  group, 
+  notSet
+}
+
+enum GridInfoChangesetPayload_OneOfSort {
+  sort, 
+  notSet
+}
+
+class GridInfoChangesetPayload extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, GridInfoChangesetPayload_OneOfFilter> _GridInfoChangesetPayload_OneOfFilterByTag = {
+    2 : GridInfoChangesetPayload_OneOfFilter.filter,
+    0 : GridInfoChangesetPayload_OneOfFilter.notSet
+  };
+  static const $core.Map<$core.int, GridInfoChangesetPayload_OneOfGroup> _GridInfoChangesetPayload_OneOfGroupByTag = {
+    3 : GridInfoChangesetPayload_OneOfGroup.group,
+    0 : GridInfoChangesetPayload_OneOfGroup.notSet
+  };
+  static const $core.Map<$core.int, GridInfoChangesetPayload_OneOfSort> _GridInfoChangesetPayload_OneOfSortByTag = {
+    4 : GridInfoChangesetPayload_OneOfSort.sort,
+    0 : GridInfoChangesetPayload_OneOfSort.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridInfoChangesetPayload', createEmptyInstance: create)
+    ..oo(0, [2])
+    ..oo(1, [3])
+    ..oo(2, [4])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOM<ViewFilter>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'filter', subBuilder: ViewFilter.create)
+    ..aOM<ViewGroup>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'group', subBuilder: ViewGroup.create)
+    ..aOM<ViewSort>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'sort', subBuilder: ViewSort.create)
+    ..hasRequiredFields = false
+  ;
+
+  GridInfoChangesetPayload._() : super();
+  factory GridInfoChangesetPayload({
+    $core.String? gridId,
+    ViewFilter? filter,
+    ViewGroup? group,
+    ViewSort? sort,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (filter != null) {
+      _result.filter = filter;
+    }
+    if (group != null) {
+      _result.group = group;
+    }
+    if (sort != null) {
+      _result.sort = sort;
+    }
+    return _result;
+  }
+  factory GridInfoChangesetPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GridInfoChangesetPayload.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')
+  GridInfoChangesetPayload clone() => GridInfoChangesetPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GridInfoChangesetPayload copyWith(void Function(GridInfoChangesetPayload) updates) => super.copyWith((message) => updates(message as GridInfoChangesetPayload)) as GridInfoChangesetPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GridInfoChangesetPayload create() => GridInfoChangesetPayload._();
+  GridInfoChangesetPayload createEmptyInstance() => create();
+  static $pb.PbList<GridInfoChangesetPayload> createRepeated() => $pb.PbList<GridInfoChangesetPayload>();
+  @$core.pragma('dart2js:noInline')
+  static GridInfoChangesetPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridInfoChangesetPayload>(create);
+  static GridInfoChangesetPayload? _defaultInstance;
+
+  GridInfoChangesetPayload_OneOfFilter whichOneOfFilter() => _GridInfoChangesetPayload_OneOfFilterByTag[$_whichOneof(0)]!;
+  void clearOneOfFilter() => clearField($_whichOneof(0));
+
+  GridInfoChangesetPayload_OneOfGroup whichOneOfGroup() => _GridInfoChangesetPayload_OneOfGroupByTag[$_whichOneof(1)]!;
+  void clearOneOfGroup() => clearField($_whichOneof(1));
+
+  GridInfoChangesetPayload_OneOfSort whichOneOfSort() => _GridInfoChangesetPayload_OneOfSortByTag[$_whichOneof(2)]!;
+  void clearOneOfSort() => clearField($_whichOneof(2));
+
+  @$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)
+  ViewFilter get filter => $_getN(1);
+  @$pb.TagNumber(2)
+  set filter(ViewFilter v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasFilter() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearFilter() => clearField(2);
+  @$pb.TagNumber(2)
+  ViewFilter ensureFilter() => $_ensure(1);
+
+  @$pb.TagNumber(3)
+  ViewGroup get group => $_getN(2);
+  @$pb.TagNumber(3)
+  set group(ViewGroup v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasGroup() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearGroup() => clearField(3);
+  @$pb.TagNumber(3)
+  ViewGroup ensureGroup() => $_ensure(2);
+
+  @$pb.TagNumber(4)
+  ViewSort get sort => $_getN(3);
+  @$pb.TagNumber(4)
+  set sort(ViewSort v) { setField(4, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasSort() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearSort() => clearField(4);
+  @$pb.TagNumber(4)
+  ViewSort ensureSort() => $_ensure(3);
+}
+

+ 7 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: grid_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 81 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pbjson.dart

@@ -0,0 +1,81 @@
+///
+//  Generated code. Do not modify.
+//  source: grid_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use viewExtDataDescriptor instead')
+const ViewExtData$json = const {
+  '1': 'ViewExtData',
+  '2': const [
+    const {'1': 'filter', '3': 1, '4': 1, '5': 11, '6': '.ViewFilter', '10': 'filter'},
+    const {'1': 'group', '3': 2, '4': 1, '5': 11, '6': '.ViewGroup', '10': 'group'},
+    const {'1': 'sort', '3': 3, '4': 1, '5': 11, '6': '.ViewSort', '10': 'sort'},
+  ],
+};
+
+/// Descriptor for `ViewExtData`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List viewExtDataDescriptor = $convert.base64Decode('CgtWaWV3RXh0RGF0YRIjCgZmaWx0ZXIYASABKAsyCy5WaWV3RmlsdGVyUgZmaWx0ZXISIAoFZ3JvdXAYAiABKAsyCi5WaWV3R3JvdXBSBWdyb3VwEh0KBHNvcnQYAyABKAsyCS5WaWV3U29ydFIEc29ydA==');
+@$core.Deprecated('Use viewFilterDescriptor instead')
+const ViewFilter$json = const {
+  '1': 'ViewFilter',
+  '2': const [
+    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '9': 0, '10': 'fieldId'},
+  ],
+  '8': const [
+    const {'1': 'one_of_field_id'},
+  ],
+};
+
+/// Descriptor for `ViewFilter`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List viewFilterDescriptor = $convert.base64Decode('CgpWaWV3RmlsdGVyEhsKCGZpZWxkX2lkGAEgASgJSABSB2ZpZWxkSWRCEQoPb25lX29mX2ZpZWxkX2lk');
+@$core.Deprecated('Use viewGroupDescriptor instead')
+const ViewGroup$json = const {
+  '1': 'ViewGroup',
+  '2': const [
+    const {'1': 'group_field_id', '3': 1, '4': 1, '5': 9, '9': 0, '10': 'groupFieldId'},
+    const {'1': 'sub_group_field_id', '3': 2, '4': 1, '5': 9, '9': 1, '10': 'subGroupFieldId'},
+  ],
+  '8': const [
+    const {'1': 'one_of_group_field_id'},
+    const {'1': 'one_of_sub_group_field_id'},
+  ],
+};
+
+/// Descriptor for `ViewGroup`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List viewGroupDescriptor = $convert.base64Decode('CglWaWV3R3JvdXASJgoOZ3JvdXBfZmllbGRfaWQYASABKAlIAFIMZ3JvdXBGaWVsZElkEi0KEnN1Yl9ncm91cF9maWVsZF9pZBgCIAEoCUgBUg9zdWJHcm91cEZpZWxkSWRCFwoVb25lX29mX2dyb3VwX2ZpZWxkX2lkQhsKGW9uZV9vZl9zdWJfZ3JvdXBfZmllbGRfaWQ=');
+@$core.Deprecated('Use viewSortDescriptor instead')
+const ViewSort$json = const {
+  '1': 'ViewSort',
+  '2': const [
+    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '9': 0, '10': 'fieldId'},
+  ],
+  '8': const [
+    const {'1': 'one_of_field_id'},
+  ],
+};
+
+/// Descriptor for `ViewSort`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List viewSortDescriptor = $convert.base64Decode('CghWaWV3U29ydBIbCghmaWVsZF9pZBgBIAEoCUgAUgdmaWVsZElkQhEKD29uZV9vZl9maWVsZF9pZA==');
+@$core.Deprecated('Use gridInfoChangesetPayloadDescriptor instead')
+const GridInfoChangesetPayload$json = const {
+  '1': 'GridInfoChangesetPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'filter', '3': 2, '4': 1, '5': 11, '6': '.ViewFilter', '9': 0, '10': 'filter'},
+    const {'1': 'group', '3': 3, '4': 1, '5': 11, '6': '.ViewGroup', '9': 1, '10': 'group'},
+    const {'1': 'sort', '3': 4, '4': 1, '5': 11, '6': '.ViewSort', '9': 2, '10': 'sort'},
+  ],
+  '8': const [
+    const {'1': 'one_of_filter'},
+    const {'1': 'one_of_group'},
+    const {'1': 'one_of_sort'},
+  ],
+};
+
+/// Descriptor for `GridInfoChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List gridInfoChangesetPayloadDescriptor = $convert.base64Decode('ChhHcmlkSW5mb0NoYW5nZXNldFBheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEiUKBmZpbHRlchgCIAEoCzILLlZpZXdGaWx0ZXJIAFIGZmlsdGVyEiIKBWdyb3VwGAMgASgLMgouVmlld0dyb3VwSAFSBWdyb3VwEh8KBHNvcnQYBCABKAsyCS5WaWV3U29ydEgCUgRzb3J0Qg8KDW9uZV9vZl9maWx0ZXJCDgoMb25lX29mX2dyb3VwQg0KC29uZV9vZl9zb3J0');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid_info.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: grid_info.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'grid_info.pb.dart';
+

+ 2 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/protobuf.dart

@@ -1,2 +1,4 @@
 // Auto-generated, do not edit 
 export './grid.pb.dart';
+export './grid_info.pb.dart';
+export './field.pb.dart';

+ 90 - 21
frontend/rust-lib/Cargo.lock

@@ -489,9 +489,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.6"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
 dependencies = [
  "cfg-if",
  "lazy_static",
@@ -561,6 +561,7 @@ dependencies = [
  "allo-isolate",
  "byteorder",
  "bytes",
+ "crossbeam-utils",
  "dart-notify",
  "ffi-support",
  "flowy-derive",
@@ -591,12 +592,13 @@ dependencies = [
 
 [[package]]
 name = "dashmap"
-version = "4.0.2"
+version = "5.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
+checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c"
 dependencies = [
  "cfg-if",
  "num_cpus",
+ "parking_lot 0.12.1",
 ]
 
 [[package]]
@@ -887,7 +889,7 @@ dependencies = [
  "lib-infra",
  "lib-ot",
  "log",
- "parking_lot",
+ "parking_lot 0.11.2",
  "pin-project",
  "protobuf",
  "serde",
@@ -946,6 +948,7 @@ dependencies = [
  "nanoid",
  "protobuf",
  "rayon",
+ "regex",
  "rust_decimal",
  "rusty-money",
  "serde",
@@ -1002,7 +1005,7 @@ dependencies = [
  "lib-ws",
  "log",
  "nanoid",
- "parking_lot",
+ "parking_lot 0.11.2",
  "protobuf",
  "reqwest",
  "serde",
@@ -1061,7 +1064,7 @@ dependencies = [
  "lib-log",
  "lib-ws",
  "log",
- "parking_lot",
+ "parking_lot 0.11.2",
  "protobuf",
  "serde",
  "tokio",
@@ -1085,7 +1088,7 @@ dependencies = [
  "lib-ot",
  "log",
  "md5",
- "parking_lot",
+ "parking_lot 0.11.2",
  "protobuf",
  "serde",
  "serde_json",
@@ -1187,7 +1190,7 @@ dependencies = [
  "log",
  "nanoid",
  "once_cell",
- "parking_lot",
+ "parking_lot 0.11.2",
  "protobuf",
  "serde",
  "serde_json",
@@ -1806,7 +1809,7 @@ dependencies = [
  "futures-util",
  "lib-infra",
  "log",
- "parking_lot",
+ "parking_lot 0.11.2",
  "paste",
  "pin-project",
  "protobuf",
@@ -1843,9 +1846,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
 
 [[package]]
 name = "lock_api"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
+checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
 dependencies = [
  "scopeguard",
 ]
@@ -2126,7 +2129,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 dependencies = [
  "instant",
  "lock_api",
- "parking_lot_core",
+ "parking_lot_core 0.8.5",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.3",
 ]
 
 [[package]]
@@ -2143,6 +2156,19 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "parking_lot_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.2.10",
+ "smallvec",
+ "windows-sys",
+]
+
 [[package]]
 name = "parse-zoneinfo"
 version = "0.3.0"
@@ -2526,7 +2552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
 dependencies = [
  "log",
- "parking_lot",
+ "parking_lot 0.11.2",
  "scheduled-thread-pool",
 ]
 
@@ -2652,9 +2678,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -2672,9 +2698,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
 
 [[package]]
 name = "remove_dir_all"
@@ -2804,7 +2830,7 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
 dependencies = [
- "parking_lot",
+ "parking_lot 0.11.2",
 ]
 
 [[package]]
@@ -2947,7 +2973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d"
 dependencies = [
  "lazy_static",
- "parking_lot",
+ "parking_lot 0.11.2",
  "serial_test_derive",
 ]
 
@@ -3241,7 +3267,7 @@ dependencies = [
  "mio",
  "num_cpus",
  "once_cell",
- "parking_lot",
+ "parking_lot 0.11.2",
  "pin-project-lite",
  "signal-hook-registry",
  "tokio-macros",
@@ -3766,6 +3792,49 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
 [[package]]
 name = "winreg"
 version = "0.7.0"

+ 2 - 0
frontend/rust-lib/dart-ffi/Cargo.toml

@@ -22,6 +22,8 @@ serde = { version = "1.0", features = ["derive"] }
 serde_json = {version = "1.0"}
 bytes = { version = "1.0" }
 once_cell = "1"
+crossbeam-utils = "0.8.7"
+
 
 lib-dispatch = {path = "../lib-dispatch" }
 flowy-sdk = {path = "../flowy-sdk"}

+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2021-07-09-063045_flowy-user/down.sql → frontend/rust-lib/flowy-database/migrations/2021-07-09-063045_user/down.sql


+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2021-07-09-063045_flowy-user/up.sql → frontend/rust-lib/flowy-database/migrations/2021-07-09-063045_user/up.sql


+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_flowy-user/down.sql → frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/down.sql


+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_flowy-user/up.sql → frontend/rust-lib/flowy-database/migrations/2021-07-14-022241_user-add-col/up.sql


+ 0 - 2
frontend/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/down.sql

@@ -1,2 +0,0 @@
--- This file should undo anything in `up.sql`
-DROP TABLE doc_table;

+ 0 - 7
frontend/rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql

@@ -1,7 +0,0 @@
--- Your SQL goes here
-CREATE TABLE doc_table (
-    id TEXT NOT NULL PRIMARY KEY,
---     data BLOB NOT NULL DEFAULT (x''),
-    data TEXT NOT NULL DEFAULT '',
-    rev_id BIGINT NOT NULL DEFAULT 0
-);

+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/down.sql → frontend/rust-lib/flowy-database/migrations/2021-09-22-074638_revision/down.sql


+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/up.sql → frontend/rust-lib/flowy-database/migrations/2021-09-22-074638_revision/up.sql


+ 0 - 2
frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/down.sql

@@ -1,2 +0,0 @@
--- This file should undo anything in `up.sql`
-DROP TABLE kv_table;

+ 0 - 5
frontend/rust-lib/flowy-database/migrations/2022-03-04-101530_flowy-grid/up.sql

@@ -1,5 +0,0 @@
--- Your SQL goes here
-CREATE TABLE kv_table (
-   key TEXT NOT NULL PRIMARY KEY,
-   value BLOB NOT NULL DEFAULT (x'')
-);

+ 1 - 0
frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/down.sql → frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_grid/down.sql

@@ -1,3 +1,4 @@
 -- This file should undo anything in `up.sql`
+DROP TABLE kv_table;
 DROP TABLE grid_rev_table;
 DROP TABLE grid_meta_rev_table;

+ 4 - 0
frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_flowy-grid/up.sql → frontend/rust-lib/flowy-database/migrations/2022-03-11-025536_grid/up.sql

@@ -1,4 +1,8 @@
 -- Your SQL goes here
+CREATE TABLE kv_table (
+    key TEXT NOT NULL PRIMARY KEY,
+    value BLOB NOT NULL DEFAULT (x'')
+);
 CREATE TABLE grid_rev_table (
     id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
     object_id TEXT NOT NULL DEFAULT '',

+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2022-04-05-015536_flowy-grid-block-index/down.sql → frontend/rust-lib/flowy-database/migrations/2022-04-05-015536_grid-block-index/down.sql


+ 0 - 0
frontend/rust-lib/flowy-database/migrations/2022-04-05-015536_flowy-grid-block-index/up.sql → frontend/rust-lib/flowy-database/migrations/2022-04-05-015536_grid-block-index/up.sql


+ 1 - 0
frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/down.sql

@@ -0,0 +1 @@
+-- This file should undo anything in `up.sql`

+ 2 - 0
frontend/rust-lib/flowy-database/migrations/2022-06-11-090029_view-add-col/up.sql

@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE view_table ADD COLUMN ext_data TEXT NOT NULL DEFAULT '';

+ 1 - 9
frontend/rust-lib/flowy-database/src/schema.rs

@@ -13,14 +13,6 @@ table! {
     }
 }
 
-table! {
-    doc_table (id) {
-        id -> Text,
-        data -> Text,
-        rev_id -> BigInt,
-    }
-}
-
 table! {
     grid_block_index_table (row_id) {
         row_id -> Text,
@@ -102,6 +94,7 @@ table! {
         view_type -> Integer,
         version -> BigInt,
         is_trash -> Bool,
+        ext_data -> Text,
     }
 }
 
@@ -119,7 +112,6 @@ table! {
 
 allow_tables_to_appear_in_same_query!(
     app_table,
-    doc_table,
     grid_block_index_table,
     grid_meta_rev_table,
     grid_rev_table,

+ 30 - 17
frontend/rust-lib/flowy-folder/src/event_map.rs

@@ -1,9 +1,9 @@
 use crate::{
     entities::{
-        app::{App, AppId, CreateAppParams, UpdateAppParams},
-        trash::{RepeatedTrash, RepeatedTrashId},
-        view::{CreateViewParams, RepeatedViewId, UpdateViewParams, View, ViewId},
-        workspace::{CreateWorkspaceParams, RepeatedWorkspace, UpdateWorkspaceParams, Workspace, WorkspaceId},
+        app::{AppId, CreateAppParams, UpdateAppParams},
+        trash::RepeatedTrashId,
+        view::{CreateViewParams, RepeatedViewId, UpdateViewParams, ViewId},
+        workspace::{CreateWorkspaceParams, UpdateWorkspaceParams, WorkspaceId},
     },
     errors::FlowyError,
     manager::FolderManager,
@@ -11,6 +11,7 @@ use crate::{
 };
 use flowy_database::{ConnectionPool, DBConnection};
 use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
+use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
 use lib_dispatch::prelude::*;
 use lib_infra::future::FutureResult;
 use std::sync::Arc;
@@ -42,6 +43,7 @@ pub fn create(folder: Arc<FolderManager>) -> Module {
         .data(folder.trash_controller.clone())
         .data(folder.clone());
 
+    // Workspace
     module = module
         .event(FolderEvent::CreateWorkspace, create_workspace_handler)
         .event(FolderEvent::ReadCurWorkspace, read_cur_workspace_handler)
@@ -49,22 +51,26 @@ pub fn create(folder: Arc<FolderManager>) -> Module {
         .event(FolderEvent::OpenWorkspace, open_workspace_handler)
         .event(FolderEvent::ReadWorkspaceApps, read_workspace_apps_handler);
 
+    // App
     module = module
         .event(FolderEvent::CreateApp, create_app_handler)
         .event(FolderEvent::ReadApp, read_app_handler)
         .event(FolderEvent::UpdateApp, update_app_handler)
         .event(FolderEvent::DeleteApp, delete_app_handler);
 
+    // View
     module = module
         .event(FolderEvent::CreateView, create_view_handler)
         .event(FolderEvent::ReadView, read_view_handler)
         .event(FolderEvent::UpdateView, update_view_handler)
+        .event(FolderEvent::ReadViewInfo, read_view_info_handler)
         .event(FolderEvent::DeleteView, delete_view_handler)
         .event(FolderEvent::DuplicateView, duplicate_view_handler)
         .event(FolderEvent::SetLatestView, set_latest_view_handler)
         .event(FolderEvent::CloseView, close_view_handler)
         .event(FolderEvent::MoveFolderItem, move_item_handler);
 
+    // Trash
     module = module
         .event(FolderEvent::ReadTrash, read_trash_handler)
         .event(FolderEvent::PutbackTrash, putback_trash_handler)
@@ -123,17 +129,20 @@ pub enum FolderEvent {
     #[event(input = "ViewId")]
     DuplicateView = 205,
 
-    #[event()]
-    CopyLink = 206,
-
     #[event(input = "ViewId")]
-    SetLatestView = 207,
+    CloseView = 206,
+
+    #[event(input = "ViewId", output = "ViewInfo")]
+    ReadViewInfo = 207,
+
+    #[event()]
+    CopyLink = 220,
 
     #[event(input = "ViewId")]
-    CloseView = 208,
+    SetLatestView = 221,
 
     #[event(input = "MoveFolderItemPayload")]
-    MoveFolderItem = 209,
+    MoveFolderItem = 230,
 
     #[event(output = "RepeatedTrash")]
     ReadTrash = 300,
@@ -155,27 +164,31 @@ pub trait FolderCouldServiceV1: Send + Sync {
     fn init(&self);
 
     // Workspace
-    fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, FlowyError>;
+    fn create_workspace(
+        &self,
+        token: &str,
+        params: CreateWorkspaceParams,
+    ) -> FutureResult<WorkspaceRevision, FlowyError>;
 
-    fn read_workspace(&self, token: &str, params: WorkspaceId) -> FutureResult<RepeatedWorkspace, FlowyError>;
+    fn read_workspace(&self, token: &str, params: WorkspaceId) -> FutureResult<Vec<WorkspaceRevision>, FlowyError>;
 
     fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> FutureResult<(), FlowyError>;
 
     fn delete_workspace(&self, token: &str, params: WorkspaceId) -> FutureResult<(), FlowyError>;
 
     // View
-    fn create_view(&self, token: &str, params: CreateViewParams) -> FutureResult<View, FlowyError>;
+    fn create_view(&self, token: &str, params: CreateViewParams) -> FutureResult<ViewRevision, FlowyError>;
 
-    fn read_view(&self, token: &str, params: ViewId) -> FutureResult<Option<View>, FlowyError>;
+    fn read_view(&self, token: &str, params: ViewId) -> FutureResult<Option<ViewRevision>, FlowyError>;
 
     fn delete_view(&self, token: &str, params: RepeatedViewId) -> FutureResult<(), FlowyError>;
 
     fn update_view(&self, token: &str, params: UpdateViewParams) -> FutureResult<(), FlowyError>;
 
     // App
-    fn create_app(&self, token: &str, params: CreateAppParams) -> FutureResult<App, FlowyError>;
+    fn create_app(&self, token: &str, params: CreateAppParams) -> FutureResult<AppRevision, FlowyError>;
 
-    fn read_app(&self, token: &str, params: AppId) -> FutureResult<Option<App>, FlowyError>;
+    fn read_app(&self, token: &str, params: AppId) -> FutureResult<Option<AppRevision>, FlowyError>;
 
     fn update_app(&self, token: &str, params: UpdateAppParams) -> FutureResult<(), FlowyError>;
 
@@ -186,5 +199,5 @@ pub trait FolderCouldServiceV1: Send + Sync {
 
     fn delete_trash(&self, token: &str, params: RepeatedTrashId) -> FutureResult<(), FlowyError>;
 
-    fn read_trash(&self, token: &str) -> FutureResult<RepeatedTrash, FlowyError>;
+    fn read_trash(&self, token: &str) -> FutureResult<Vec<TrashRevision>, FlowyError>;
 }

+ 15 - 9
frontend/rust-lib/flowy-folder/src/manager.rs

@@ -9,13 +9,12 @@ use crate::{
     },
 };
 use bytes::Bytes;
-use flowy_sync::client_document::default::{initial_quill_delta_string, initial_read_me};
-
 use flowy_error::FlowyError;
 use flowy_folder_data_model::entities::view::ViewDataType;
 use flowy_folder_data_model::user_default;
 use flowy_revision::disk::SQLiteTextBlockRevisionPersistence;
 use flowy_revision::{RevisionManager, RevisionPersistence, RevisionWebSocket};
+use flowy_sync::client_document::default::{initial_quill_delta_string, initial_read_me};
 use flowy_sync::{client_folder::FolderPad, entities::ws_data::ServerRevisionWSData};
 use lazy_static::lazy_static;
 use lib_infra::future::FutureResult;
@@ -199,9 +198,9 @@ impl DefaultFolderBuilder {
         view_controller: Arc<ViewController>,
     ) -> FlowyResult<()> {
         log::debug!("Create user default workspace");
-        let workspace = user_default::create_default_workspace();
-        set_current_workspace(&workspace.id);
-        for app in workspace.apps.iter() {
+        let workspace_rev = user_default::create_default_workspace();
+        set_current_workspace(&workspace_rev.id);
+        for app in workspace_rev.apps.iter() {
             for (index, view) in app.belongings.iter().enumerate() {
                 let view_data = if index == 0 {
                     initial_read_me().to_delta_str()
@@ -214,10 +213,12 @@ impl DefaultFolderBuilder {
                     .await?;
             }
         }
-        let folder = FolderPad::new(vec![workspace.clone()], vec![])?;
+        let folder = FolderPad::new(vec![workspace_rev.clone()], vec![])?;
         let folder_id = FolderId::new(user_id);
         let _ = persistence.save_folder(user_id, &folder_id, folder).await?;
-        let repeated_workspace = RepeatedWorkspace { items: vec![workspace] };
+        let repeated_workspace = RepeatedWorkspace {
+            items: vec![workspace_rev.into()],
+        };
         send_dart_notification(token, FolderNotification::UserCreateWorkspace)
             .payload(repeated_workspace)
             .send();
@@ -241,11 +242,16 @@ pub trait ViewDataProcessor {
 
     fn close_container(&self, view_id: &str) -> FutureResult<(), FlowyError>;
 
-    fn view_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;
+    fn get_delta_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;
 
     fn create_default_view(&self, user_id: &str, view_id: &str) -> FutureResult<Bytes, FlowyError>;
 
-    fn process_view_delta_data(&self, user_id: &str, view_id: &str, data: Vec<u8>) -> FutureResult<Bytes, FlowyError>;
+    fn create_view_from_delta_data(
+        &self,
+        user_id: &str,
+        view_id: &str,
+        data: Vec<u8>,
+    ) -> FutureResult<Bytes, FlowyError>;
 
     fn data_type(&self) -> ViewDataType;
 }

+ 19 - 16
frontend/rust-lib/flowy-folder/src/protobuf/model/event_map.rs

@@ -40,10 +40,11 @@ pub enum FolderEvent {
     UpdateView = 203,
     DeleteView = 204,
     DuplicateView = 205,
-    CopyLink = 206,
-    SetLatestView = 207,
-    CloseView = 208,
-    MoveFolderItem = 209,
+    CloseView = 206,
+    ReadViewInfo = 207,
+    CopyLink = 220,
+    SetLatestView = 221,
+    MoveFolderItem = 230,
     ReadTrash = 300,
     PutbackTrash = 301,
     DeleteTrash = 302,
@@ -73,10 +74,11 @@ impl ::protobuf::ProtobufEnum for FolderEvent {
             203 => ::std::option::Option::Some(FolderEvent::UpdateView),
             204 => ::std::option::Option::Some(FolderEvent::DeleteView),
             205 => ::std::option::Option::Some(FolderEvent::DuplicateView),
-            206 => ::std::option::Option::Some(FolderEvent::CopyLink),
-            207 => ::std::option::Option::Some(FolderEvent::SetLatestView),
-            208 => ::std::option::Option::Some(FolderEvent::CloseView),
-            209 => ::std::option::Option::Some(FolderEvent::MoveFolderItem),
+            206 => ::std::option::Option::Some(FolderEvent::CloseView),
+            207 => ::std::option::Option::Some(FolderEvent::ReadViewInfo),
+            220 => ::std::option::Option::Some(FolderEvent::CopyLink),
+            221 => ::std::option::Option::Some(FolderEvent::SetLatestView),
+            230 => ::std::option::Option::Some(FolderEvent::MoveFolderItem),
             300 => ::std::option::Option::Some(FolderEvent::ReadTrash),
             301 => ::std::option::Option::Some(FolderEvent::PutbackTrash),
             302 => ::std::option::Option::Some(FolderEvent::DeleteTrash),
@@ -103,9 +105,10 @@ impl ::protobuf::ProtobufEnum for FolderEvent {
             FolderEvent::UpdateView,
             FolderEvent::DeleteView,
             FolderEvent::DuplicateView,
+            FolderEvent::CloseView,
+            FolderEvent::ReadViewInfo,
             FolderEvent::CopyLink,
             FolderEvent::SetLatestView,
-            FolderEvent::CloseView,
             FolderEvent::MoveFolderItem,
             FolderEvent::ReadTrash,
             FolderEvent::PutbackTrash,
@@ -140,19 +143,19 @@ impl ::protobuf::reflect::ProtobufValue for FolderEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*\xc3\x03\n\x0bFolderEvent\x12\x13\n\x0fCreateWorks\
+    \n\x0fevent_map.proto*\xd6\x03\n\x0bFolderEvent\x12\x13\n\x0fCreateWorks\
     pace\x10\0\x12\x14\n\x10ReadCurWorkspace\x10\x01\x12\x12\n\x0eReadWorksp\
     aces\x10\x02\x12\x13\n\x0fDeleteWorkspace\x10\x03\x12\x11\n\rOpenWorkspa\
     ce\x10\x04\x12\x15\n\x11ReadWorkspaceApps\x10\x05\x12\r\n\tCreateApp\x10\
     e\x12\r\n\tDeleteApp\x10f\x12\x0b\n\x07ReadApp\x10g\x12\r\n\tUpdateApp\
     \x10h\x12\x0f\n\nCreateView\x10\xc9\x01\x12\r\n\x08ReadView\x10\xca\x01\
     \x12\x0f\n\nUpdateView\x10\xcb\x01\x12\x0f\n\nDeleteView\x10\xcc\x01\x12\
-    \x12\n\rDuplicateView\x10\xcd\x01\x12\r\n\x08CopyLink\x10\xce\x01\x12\
-    \x12\n\rSetLatestView\x10\xcf\x01\x12\x0e\n\tCloseView\x10\xd0\x01\x12\
-    \x13\n\x0eMoveFolderItem\x10\xd1\x01\x12\x0e\n\tReadTrash\x10\xac\x02\
-    \x12\x11\n\x0cPutbackTrash\x10\xad\x02\x12\x10\n\x0bDeleteTrash\x10\xae\
-    \x02\x12\x14\n\x0fRestoreAllTrash\x10\xaf\x02\x12\x13\n\x0eDeleteAllTras\
-    h\x10\xb0\x02b\x06proto3\
+    \x12\n\rDuplicateView\x10\xcd\x01\x12\x0e\n\tCloseView\x10\xce\x01\x12\
+    \x11\n\x0cReadViewInfo\x10\xcf\x01\x12\r\n\x08CopyLink\x10\xdc\x01\x12\
+    \x12\n\rSetLatestView\x10\xdd\x01\x12\x13\n\x0eMoveFolderItem\x10\xe6\
+    \x01\x12\x0e\n\tReadTrash\x10\xac\x02\x12\x11\n\x0cPutbackTrash\x10\xad\
+    \x02\x12\x10\n\x0bDeleteTrash\x10\xae\x02\x12\x14\n\x0fRestoreAllTrash\
+    \x10\xaf\x02\x12\x13\n\x0eDeleteAllTrash\x10\xb0\x02b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 5 - 4
frontend/rust-lib/flowy-folder/src/protobuf/proto/event_map.proto

@@ -16,10 +16,11 @@ enum FolderEvent {
     UpdateView = 203;
     DeleteView = 204;
     DuplicateView = 205;
-    CopyLink = 206;
-    SetLatestView = 207;
-    CloseView = 208;
-    MoveFolderItem = 209;
+    CloseView = 206;
+    ReadViewInfo = 207;
+    CopyLink = 220;
+    SetLatestView = 221;
+    MoveFolderItem = 230;
     ReadTrash = 300;
     PutbackTrash = 301;
     DeleteTrash = 302;

+ 23 - 16
frontend/rust-lib/flowy-folder/src/services/app/controller.rs

@@ -12,6 +12,7 @@ use crate::{
     },
 };
 
+use flowy_folder_data_model::revision::AppRevision;
 use futures::{FutureExt, StreamExt};
 use std::{collections::HashSet, sync::Arc};
 
@@ -48,7 +49,7 @@ impl AppController {
         self.create_app_on_local(app).await
     }
 
-    pub(crate) async fn create_app_on_local(&self, app: App) -> Result<App, FlowyError> {
+    pub(crate) async fn create_app_on_local(&self, app: AppRevision) -> Result<App, FlowyError> {
         let _ = self
             .persistence
             .begin_transaction(|transaction| {
@@ -57,10 +58,10 @@ impl AppController {
                 Ok(())
             })
             .await?;
-        Ok(app)
+        Ok(app.into())
     }
 
-    pub(crate) async fn read_app(&self, params: AppId) -> Result<App, FlowyError> {
+    pub(crate) async fn read_app(&self, params: AppId) -> Result<AppRevision, FlowyError> {
         let app = self
             .persistence
             .begin_transaction(|transaction| {
@@ -80,14 +81,15 @@ impl AppController {
         let changeset = AppChangeset::new(params.clone());
         let app_id = changeset.id.clone();
 
-        let app = self
+        let app: App = self
             .persistence
             .begin_transaction(|transaction| {
                 let _ = transaction.update_app(changeset)?;
                 let app = transaction.read_app(&app_id)?;
                 Ok(app)
             })
-            .await?;
+            .await?
+            .into();
         send_dart_notification(&app_id, FolderNotification::AppUpdated)
             .payload(app)
             .send();
@@ -108,8 +110,8 @@ impl AppController {
         Ok(())
     }
 
-    pub(crate) async fn read_local_apps(&self, ids: Vec<String>) -> Result<Vec<App>, FlowyError> {
-        let apps = self
+    pub(crate) async fn read_local_apps(&self, ids: Vec<String>) -> Result<Vec<AppRevision>, FlowyError> {
+        let app_revs = self
             .persistence
             .begin_transaction(|transaction| {
                 let mut apps = vec![];
@@ -119,13 +121,13 @@ impl AppController {
                 Ok(apps)
             })
             .await?;
-        Ok(apps)
+        Ok(app_revs)
     }
 }
 
 impl AppController {
     #[tracing::instrument(level = "trace", skip(self), err)]
-    async fn create_app_on_server(&self, params: CreateAppParams) -> Result<App, FlowyError> {
+    async fn create_app_on_server(&self, params: CreateAppParams) -> Result<AppRevision, FlowyError> {
         let token = self.user.token()?;
         let app = self.cloud_service.create_app(&token, params).await?;
         Ok(app)
@@ -154,12 +156,13 @@ impl AppController {
         let persistence = self.persistence.clone();
         tokio::spawn(async move {
             match server.read_app(&token, params).await {
-                Ok(Some(app)) => {
+                Ok(Some(app_rev)) => {
                     match persistence
-                        .begin_transaction(|transaction| transaction.create_app(app.clone()))
+                        .begin_transaction(|transaction| transaction.create_app(app_rev.clone()))
                         .await
                     {
                         Ok(_) => {
+                            let app: App = app_rev.into();
                             send_dart_notification(&app.id, FolderNotification::AppUpdated)
                                 .payload(app)
                                 .send();
@@ -240,7 +243,11 @@ fn notify_apps_changed<'a>(
     trash_controller: Arc<TrashController>,
     transaction: &'a (dyn FolderPersistenceTransaction + 'a),
 ) -> FlowyResult<()> {
-    let repeated_app = read_local_workspace_apps(workspace_id, trash_controller, transaction)?;
+    let items = read_local_workspace_apps(workspace_id, trash_controller, transaction)?
+        .into_iter()
+        .map(|app_rev| app_rev.into())
+        .collect();
+    let repeated_app = RepeatedApp { items };
     send_dart_notification(workspace_id, FolderNotification::WorkspaceAppsChanged)
         .payload(repeated_app)
         .send();
@@ -251,9 +258,9 @@ pub fn read_local_workspace_apps<'a>(
     workspace_id: &str,
     trash_controller: Arc<TrashController>,
     transaction: &'a (dyn FolderPersistenceTransaction + 'a),
-) -> Result<RepeatedApp, FlowyError> {
-    let mut apps = transaction.read_workspace_apps(workspace_id)?;
+) -> Result<Vec<AppRevision>, FlowyError> {
+    let mut app_revs = transaction.read_workspace_apps(workspace_id)?;
     let trash_ids = trash_controller.read_trash_ids(transaction)?;
-    apps.retain(|app| !trash_ids.contains(&app.id));
-    Ok(RepeatedApp { items: apps })
+    app_revs.retain(|app| !trash_ids.contains(&app.id));
+    Ok(app_revs)
 }

+ 7 - 9
frontend/rust-lib/flowy-folder/src/services/app/event_handler.rs

@@ -1,11 +1,9 @@
 use crate::{
-    entities::{
-        app::{App, AppId, CreateAppParams, CreateAppPayload, UpdateAppParams, UpdateAppPayload},
-        trash::Trash,
-    },
+    entities::app::{App, AppId, CreateAppParams, CreateAppPayload, UpdateAppParams, UpdateAppPayload},
     errors::FlowyError,
     services::{AppController, TrashController, ViewController},
 };
+use flowy_folder_data_model::revision::TrashRevision;
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::{convert::TryInto, sync::Arc};
 
@@ -29,8 +27,8 @@ pub(crate) async fn delete_app_handler(
         .read_local_apps(vec![params.value])
         .await?
         .into_iter()
-        .map(|app| app.into())
-        .collect::<Vec<Trash>>();
+        .map(|app_rev| app_rev.into())
+        .collect::<Vec<TrashRevision>>();
 
     let _ = trash_controller.add(trash).await?;
     Ok(())
@@ -53,8 +51,8 @@ pub(crate) async fn read_app_handler(
     view_controller: AppData<Arc<ViewController>>,
 ) -> DataResult<App, FlowyError> {
     let params: AppId = data.into_inner();
-    let mut app = app_controller.read_app(params.clone()).await?;
-    app.belongings = view_controller.read_views_belong_to(&params.value).await?;
+    let mut app_rev = app_controller.read_app(params.clone()).await?;
+    app_rev.belongings = view_controller.read_views_belong_to(&params.value).await?;
 
-    data_result(app)
+    data_result(app_rev.into())
 }

+ 5 - 7
frontend/rust-lib/flowy-folder/src/services/folder_editor.rs

@@ -1,16 +1,14 @@
-use flowy_sync::{
-    client_folder::{FolderChange, FolderPad},
-    entities::{revision::Revision, ws_data::ServerRevisionWSData},
-};
-
 use crate::manager::FolderId;
 use bytes::Bytes;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_sync::util::make_delta_from_revisions;
-
 use flowy_revision::{
     RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder, RevisionWebSocket,
 };
+use flowy_sync::util::make_delta_from_revisions;
+use flowy_sync::{
+    client_folder::{FolderChange, FolderPad},
+    entities::{revision::Revision, ws_data::ServerRevisionWSData},
+};
 use lib_infra::future::FutureResult;
 use lib_ot::core::PlainTextAttributes;
 

+ 8 - 11
frontend/rust-lib/flowy-folder/src/services/persistence/migration.rs

@@ -5,11 +5,8 @@ use crate::{
 };
 use flowy_database::kv::KV;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_folder_data_model::entities::{
-    app::{App, RepeatedApp},
-    view::{RepeatedView, View},
-    workspace::Workspace,
-};
+
+use flowy_folder_data_model::revision::{AppRevision, ViewRevision, WorkspaceRevision};
 use flowy_revision::disk::SQLiteTextBlockRevisionPersistence;
 use flowy_revision::{RevisionLoader, RevisionPersistence};
 use flowy_sync::{client_folder::FolderPad, entities::revision::md5};
@@ -42,25 +39,25 @@ impl FolderMigration {
         let workspaces = conn.immediate_transaction::<_, FlowyError, _>(|| {
             let mut workspaces = WorkspaceTableSql::read_workspaces(&self.user_id, None, conn)?
                 .into_iter()
-                .map(Workspace::from)
+                .map(WorkspaceRevision::from)
                 .collect::<Vec<_>>();
 
             for workspace in workspaces.iter_mut() {
                 let mut apps = AppTableSql::read_workspace_apps(&workspace.id, conn)?
                     .into_iter()
-                    .map(App::from)
+                    .map(AppRevision::from)
                     .collect::<Vec<_>>();
 
                 for app in apps.iter_mut() {
                     let views = ViewTableSql::read_views(&app.id, conn)?
                         .into_iter()
-                        .map(View::from)
+                        .map(ViewRevision::from)
                         .collect::<Vec<_>>();
 
-                    app.belongings = RepeatedView { items: views };
+                    app.belongings = views;
                 }
 
-                workspace.apps = RepeatedApp { items: apps };
+                workspace.apps = apps;
             }
             Ok(workspaces)
         })?;
@@ -72,7 +69,7 @@ impl FolderMigration {
         }
 
         let trash = conn.immediate_transaction::<_, FlowyError, _>(|| {
-            let trash = TrashTableSql::read_all(conn)?.take_items();
+            let trash = TrashTableSql::read_all(conn)?;
             Ok(trash)
         })?;
 

+ 12 - 17
frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs

@@ -9,12 +9,7 @@ use crate::{
 };
 use flowy_database::ConnectionPool;
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_folder_data_model::entities::{
-    app::App,
-    trash::{RepeatedTrash, Trash},
-    view::View,
-    workspace::Workspace,
-};
+use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
 use flowy_revision::disk::{RevisionRecord, RevisionState};
 use flowy_revision::mk_revision_disk_cache;
 use flowy_sync::client_folder::initial_folder_delta;
@@ -24,27 +19,27 @@ use tokio::sync::RwLock;
 pub use version_1::{app_sql::*, trash_sql::*, v1_impl::V1Transaction, view_sql::*, workspace_sql::*};
 
 pub trait FolderPersistenceTransaction {
-    fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()>;
-    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>>;
+    fn create_workspace(&self, user_id: &str, workspace_rev: WorkspaceRevision) -> FlowyResult<()>;
+    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<WorkspaceRevision>>;
     fn update_workspace(&self, changeset: WorkspaceChangeset) -> FlowyResult<()>;
     fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()>;
 
-    fn create_app(&self, app: App) -> FlowyResult<()>;
+    fn create_app(&self, app_rev: AppRevision) -> FlowyResult<()>;
     fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()>;
-    fn read_app(&self, app_id: &str) -> FlowyResult<App>;
-    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<App>>;
-    fn delete_app(&self, app_id: &str) -> FlowyResult<App>;
+    fn read_app(&self, app_id: &str) -> FlowyResult<AppRevision>;
+    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<AppRevision>>;
+    fn delete_app(&self, app_id: &str) -> FlowyResult<AppRevision>;
     fn move_app(&self, app_id: &str, from: usize, to: usize) -> FlowyResult<()>;
 
-    fn create_view(&self, view: View) -> FlowyResult<()>;
-    fn read_view(&self, view_id: &str) -> FlowyResult<View>;
-    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<View>>;
+    fn create_view(&self, view_rev: ViewRevision) -> FlowyResult<()>;
+    fn read_view(&self, view_id: &str) -> FlowyResult<ViewRevision>;
+    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<ViewRevision>>;
     fn update_view(&self, changeset: ViewChangeset) -> FlowyResult<()>;
     fn delete_view(&self, view_id: &str) -> FlowyResult<()>;
     fn move_view(&self, view_id: &str, from: usize, to: usize) -> FlowyResult<()>;
 
-    fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()>;
-    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash>;
+    fn create_trash(&self, trashes: Vec<TrashRevision>) -> FlowyResult<()>;
+    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<Vec<TrashRevision>>;
     fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()>;
 }
 

+ 14 - 14
frontend/rust-lib/flowy-folder/src/services/persistence/version_1/app_sql.rs

@@ -1,7 +1,6 @@
 use crate::entities::{
-    app::{App, UpdateAppParams},
+    app::UpdateAppParams,
     trash::{Trash, TrashType},
-    view::RepeatedView,
 };
 use crate::{errors::FlowyError, services::persistence::version_1::workspace_sql::WorkspaceTable};
 use flowy_database::{
@@ -9,11 +8,12 @@ use flowy_database::{
     schema::{app_table, app_table::dsl},
     SqliteConnection,
 };
+use flowy_folder_data_model::revision::AppRevision;
 
 pub struct AppTableSql();
 impl AppTableSql {
-    pub(crate) fn create_app(app: App, conn: &SqliteConnection) -> Result<(), FlowyError> {
-        let app_table = AppTable::new(app);
+    pub(crate) fn create_app(app_rev: AppRevision, conn: &SqliteConnection) -> Result<(), FlowyError> {
+        let app_table = AppTable::new(app_rev);
         match diesel_record_count!(app_table, &app_table.id, conn) {
             0 => diesel_insert_table!(app_table, &app_table, conn),
             _ => {
@@ -91,16 +91,16 @@ pub(crate) struct AppTable {
 }
 
 impl AppTable {
-    pub fn new(app: App) -> Self {
+    pub fn new(app_rev: AppRevision) -> Self {
         Self {
-            id: app.id,
-            workspace_id: app.workspace_id,
-            name: app.name,
-            desc: app.desc,
+            id: app_rev.id,
+            workspace_id: app_rev.workspace_id,
+            name: app_rev.name,
+            desc: app_rev.desc,
             color_style: Default::default(),
             last_view_id: None,
-            modified_time: app.modified_time,
-            create_time: app.create_time,
+            modified_time: app_rev.modified_time,
+            create_time: app_rev.create_time,
             version: 0,
             is_trash: false,
         }
@@ -147,14 +147,14 @@ impl AppChangeset {
         }
     }
 }
-impl std::convert::From<AppTable> for App {
+impl std::convert::From<AppTable> for AppRevision {
     fn from(table: AppTable) -> Self {
-        App {
+        AppRevision {
             id: table.id,
             workspace_id: table.workspace_id,
             name: table.name,
             desc: table.desc,
-            belongings: RepeatedView::default(),
+            belongings: vec![],
             version: table.version,
             modified_time: table.modified_time,
             create_time: table.create_time,

+ 27 - 11
frontend/rust-lib/flowy-folder/src/services/persistence/version_1/trash_sql.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::trash::{RepeatedTrash, Trash, TrashType},
+    entities::trash::{Trash, TrashType},
     errors::FlowyError,
 };
 use diesel::sql_types::Integer;
@@ -8,12 +8,13 @@ use flowy_database::{
     schema::{trash_table, trash_table::dsl},
     SqliteConnection,
 };
+use flowy_folder_data_model::revision::TrashRevision;
 
 pub struct TrashTableSql();
 impl TrashTableSql {
-    pub(crate) fn create_trash(trashes: Vec<Trash>, conn: &SqliteConnection) -> Result<(), FlowyError> {
-        for trash in trashes {
-            let trash_table: TrashTable = trash.into();
+    pub(crate) fn create_trash(trashes: Vec<TrashRevision>, conn: &SqliteConnection) -> Result<(), FlowyError> {
+        for trash_rev in trashes {
+            let trash_table: TrashTable = trash_rev.into();
             match diesel_record_count!(trash_table, &trash_table.id, conn) {
                 0 => diesel_insert_table!(trash_table, &trash_table, conn),
                 _ => {
@@ -26,10 +27,13 @@ impl TrashTableSql {
         Ok(())
     }
 
-    pub(crate) fn read_all(conn: &SqliteConnection) -> Result<RepeatedTrash, FlowyError> {
+    pub(crate) fn read_all(conn: &SqliteConnection) -> Result<Vec<TrashRevision>, FlowyError> {
         let trash_tables = dsl::trash_table.load::<TrashTable>(conn)?;
-        let items = trash_tables.into_iter().map(|t| t.into()).collect::<Vec<Trash>>();
-        Ok(RepeatedTrash { items })
+        let items = trash_tables
+            .into_iter()
+            .map(TrashRevision::from)
+            .collect::<Vec<TrashRevision>>();
+        Ok(items)
     }
 
     pub(crate) fn delete_all(conn: &SqliteConnection) -> Result<(), FlowyError> {
@@ -72,12 +76,11 @@ impl std::convert::From<TrashTable> for Trash {
     }
 }
 
-impl std::convert::From<Trash> for TrashTable {
-    fn from(trash: Trash) -> Self {
-        TrashTable {
+impl std::convert::From<TrashTable> for TrashRevision {
+    fn from(trash: TrashTable) -> Self {
+        TrashRevision {
             id: trash.id,
             name: trash.name,
-            desc: "".to_owned(),
             modified_time: trash.modified_time,
             create_time: trash.create_time,
             ty: trash.ty.into(),
@@ -85,6 +88,19 @@ impl std::convert::From<Trash> for TrashTable {
     }
 }
 
+impl std::convert::From<TrashRevision> for TrashTable {
+    fn from(trash_rev: TrashRevision) -> Self {
+        TrashTable {
+            id: trash_rev.id,
+            name: trash_rev.name,
+            desc: "".to_string(),
+            modified_time: trash_rev.modified_time,
+            create_time: trash_rev.create_time,
+            ty: trash_rev.ty.into(),
+        }
+    }
+}
+
 #[derive(AsChangeset, Identifiable, Clone, Default, Debug)]
 #[table_name = "trash_table"]
 pub(crate) struct TrashChangeset {

+ 41 - 47
frontend/rust-lib/flowy-folder/src/services/persistence/version_1/v1_impl.rs

@@ -8,24 +8,20 @@ use crate::services::persistence::{
 };
 use flowy_database::DBConnection;
 use flowy_error::FlowyResult;
-use flowy_folder_data_model::entities::{
-    app::App,
-    trash::{RepeatedTrash, Trash},
-    view::View,
-    workspace::Workspace,
-};
+use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
 
+/// V1Transaction is deprecated since version 0.0.2 version
 pub struct V1Transaction<'a>(pub &'a DBConnection);
 
 impl<'a> FolderPersistenceTransaction for V1Transaction<'a> {
-    fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> {
-        let _ = WorkspaceTableSql::create_workspace(user_id, workspace, &*self.0)?;
+    fn create_workspace(&self, user_id: &str, workspace_rev: WorkspaceRevision) -> FlowyResult<()> {
+        let _ = WorkspaceTableSql::create_workspace(user_id, workspace_rev, &*self.0)?;
         Ok(())
     }
 
-    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>> {
+    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<WorkspaceRevision>> {
         let tables = WorkspaceTableSql::read_workspaces(user_id, workspace_id, &*self.0)?;
-        let workspaces = tables.into_iter().map(Workspace::from).collect::<Vec<_>>();
+        let workspaces = tables.into_iter().map(WorkspaceRevision::from).collect::<Vec<_>>();
         Ok(workspaces)
     }
 
@@ -37,8 +33,8 @@ impl<'a> FolderPersistenceTransaction for V1Transaction<'a> {
         WorkspaceTableSql::delete_workspace(workspace_id, &*self.0)
     }
 
-    fn create_app(&self, app: App) -> FlowyResult<()> {
-        let _ = AppTableSql::create_app(app, &*self.0)?;
+    fn create_app(&self, app_rev: AppRevision) -> FlowyResult<()> {
+        let _ = AppTableSql::create_app(app_rev, &*self.0)?;
         Ok(())
     }
 
@@ -47,39 +43,39 @@ impl<'a> FolderPersistenceTransaction for V1Transaction<'a> {
         Ok(())
     }
 
-    fn read_app(&self, app_id: &str) -> FlowyResult<App> {
-        let table = AppTableSql::read_app(app_id, &*self.0)?;
-        Ok(App::from(table))
+    fn read_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
+        let app_revision: AppRevision = AppTableSql::read_app(app_id, &*self.0)?.into();
+        Ok(app_revision)
     }
 
-    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<App>> {
+    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<AppRevision>> {
         let tables = AppTableSql::read_workspace_apps(workspace_id, &*self.0)?;
-        let apps = tables.into_iter().map(App::from).collect::<Vec<_>>();
+        let apps = tables.into_iter().map(AppRevision::from).collect::<Vec<_>>();
         Ok(apps)
     }
 
-    fn delete_app(&self, app_id: &str) -> FlowyResult<App> {
-        let table = AppTableSql::delete_app(app_id, &*self.0)?;
-        Ok(App::from(table))
+    fn delete_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
+        let app_revision: AppRevision = AppTableSql::delete_app(app_id, &*self.0)?.into();
+        Ok(app_revision)
     }
 
     fn move_app(&self, _app_id: &str, _from: usize, _to: usize) -> FlowyResult<()> {
         Ok(())
     }
 
-    fn create_view(&self, view: View) -> FlowyResult<()> {
-        let _ = ViewTableSql::create_view(view, &*self.0)?;
+    fn create_view(&self, view_rev: ViewRevision) -> FlowyResult<()> {
+        let _ = ViewTableSql::create_view(view_rev, &*self.0)?;
         Ok(())
     }
 
-    fn read_view(&self, view_id: &str) -> FlowyResult<View> {
-        let table = ViewTableSql::read_view(view_id, &*self.0)?;
-        Ok(View::from(table))
+    fn read_view(&self, view_id: &str) -> FlowyResult<ViewRevision> {
+        let view_revision: ViewRevision = ViewTableSql::read_view(view_id, &*self.0)?.into();
+        Ok(view_revision)
     }
 
-    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<View>> {
+    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<ViewRevision>> {
         let tables = ViewTableSql::read_views(belong_to_id, &*self.0)?;
-        let views = tables.into_iter().map(View::from).collect::<Vec<_>>();
+        let views = tables.into_iter().map(ViewRevision::from).collect::<Vec<_>>();
         Ok(views)
     }
 
@@ -97,19 +93,17 @@ impl<'a> FolderPersistenceTransaction for V1Transaction<'a> {
         Ok(())
     }
 
-    fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> {
+    fn create_trash(&self, trashes: Vec<TrashRevision>) -> FlowyResult<()> {
         let _ = TrashTableSql::create_trash(trashes, &*self.0)?;
         Ok(())
     }
 
-    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> {
+    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<Vec<TrashRevision>> {
         match trash_id {
             None => TrashTableSql::read_all(&*self.0),
             Some(trash_id) => {
-                let table = TrashTableSql::read(&trash_id, &*self.0)?;
-                Ok(RepeatedTrash {
-                    items: vec![Trash::from(table)],
-                })
+                let trash_revision: TrashRevision = TrashTableSql::read(&trash_id, &*self.0)?.into();
+                Ok(vec![trash_revision])
             }
         }
     }
@@ -132,11 +126,11 @@ impl<T> FolderPersistenceTransaction for Box<T>
 where
     T: FolderPersistenceTransaction + ?Sized,
 {
-    fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> {
-        (**self).create_workspace(user_id, workspace)
+    fn create_workspace(&self, user_id: &str, workspace_rev: WorkspaceRevision) -> FlowyResult<()> {
+        (**self).create_workspace(user_id, workspace_rev)
     }
 
-    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>> {
+    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<WorkspaceRevision>> {
         (**self).read_workspaces(user_id, workspace_id)
     }
 
@@ -148,23 +142,23 @@ where
         (**self).delete_workspace(workspace_id)
     }
 
-    fn create_app(&self, app: App) -> FlowyResult<()> {
-        (**self).create_app(app)
+    fn create_app(&self, app_rev: AppRevision) -> FlowyResult<()> {
+        (**self).create_app(app_rev)
     }
 
     fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()> {
         (**self).update_app(changeset)
     }
 
-    fn read_app(&self, app_id: &str) -> FlowyResult<App> {
+    fn read_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
         (**self).read_app(app_id)
     }
 
-    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<App>> {
+    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<AppRevision>> {
         (**self).read_workspace_apps(workspace_id)
     }
 
-    fn delete_app(&self, app_id: &str) -> FlowyResult<App> {
+    fn delete_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
         (**self).delete_app(app_id)
     }
 
@@ -172,15 +166,15 @@ where
         Ok(())
     }
 
-    fn create_view(&self, view: View) -> FlowyResult<()> {
-        (**self).create_view(view)
+    fn create_view(&self, view_rev: ViewRevision) -> FlowyResult<()> {
+        (**self).create_view(view_rev)
     }
 
-    fn read_view(&self, view_id: &str) -> FlowyResult<View> {
+    fn read_view(&self, view_id: &str) -> FlowyResult<ViewRevision> {
         (**self).read_view(view_id)
     }
 
-    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<View>> {
+    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<ViewRevision>> {
         (**self).read_views(belong_to_id)
     }
 
@@ -196,11 +190,11 @@ where
         Ok(())
     }
 
-    fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> {
+    fn create_trash(&self, trashes: Vec<TrashRevision>) -> FlowyResult<()> {
         (**self).create_trash(trashes)
     }
 
-    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> {
+    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<Vec<TrashRevision>> {
         (**self).read_trash(trash_id)
     }
 

+ 21 - 17
frontend/rust-lib/flowy-folder/src/services/persistence/version_1/view_sql.rs

@@ -1,7 +1,7 @@
 use crate::{
     entities::{
         trash::{Trash, TrashType},
-        view::{RepeatedView, UpdateViewParams, View, ViewDataType},
+        view::{UpdateViewParams, ViewDataType},
     },
     errors::FlowyError,
     services::persistence::version_1::app_sql::AppTable,
@@ -12,12 +12,14 @@ use flowy_database::{
     schema::{view_table, view_table::dsl},
     SqliteConnection,
 };
+
+use flowy_folder_data_model::revision::ViewRevision;
 use lib_infra::util::timestamp;
 
 pub struct ViewTableSql();
 impl ViewTableSql {
-    pub(crate) fn create_view(view: View, conn: &SqliteConnection) -> Result<(), FlowyError> {
-        let view_table = ViewTable::new(view);
+    pub(crate) fn create_view(view_rev: ViewRevision, conn: &SqliteConnection) -> Result<(), FlowyError> {
+        let view_table = ViewTable::new(view_rev);
         match diesel_record_count!(view_table, &view_table.id, conn) {
             0 => diesel_insert_table!(view_table, &view_table, conn),
             _ => {
@@ -79,51 +81,53 @@ pub(crate) struct ViewTable {
     pub view_type: SqlViewDataType,
     pub version: i64,
     pub is_trash: bool,
+    pub ext_data: String,
 }
 
 impl ViewTable {
-    pub fn new(view: View) -> Self {
-        let data_type = match view.data_type {
+    pub fn new(view_rev: ViewRevision) -> Self {
+        let data_type = match view_rev.data_type {
             ViewDataType::TextBlock => SqlViewDataType::Block,
             ViewDataType::Grid => SqlViewDataType::Grid,
         };
 
         ViewTable {
-            id: view.id,
-            belong_to_id: view.belong_to_id,
-            name: view.name,
-            desc: view.desc,
-            modified_time: view.modified_time,
-            create_time: view.create_time,
-            thumbnail: view.thumbnail,
+            id: view_rev.id,
+            belong_to_id: view_rev.belong_to_id,
+            name: view_rev.name,
+            desc: view_rev.desc,
+            modified_time: view_rev.modified_time,
+            create_time: view_rev.create_time,
+            thumbnail: view_rev.thumbnail,
             view_type: data_type,
-            version: 0,
+            ext_data: view_rev.ext_data,
+            version: view_rev.version,
             is_trash: false,
         }
     }
 }
 
-impl std::convert::From<ViewTable> for View {
+impl std::convert::From<ViewTable> for ViewRevision {
     fn from(table: ViewTable) -> Self {
         let data_type = match table.view_type {
             SqlViewDataType::Block => ViewDataType::TextBlock,
             SqlViewDataType::Grid => ViewDataType::Grid,
         };
 
-        View {
+        ViewRevision {
             id: table.id,
             belong_to_id: table.belong_to_id,
             name: table.name,
             desc: table.desc,
             data_type,
-            belongings: RepeatedView::default(),
+            belongings: vec![],
             modified_time: table.modified_time,
             version: table.version,
             create_time: table.create_time,
             ext_data: "".to_string(),
             thumbnail: table.thumbnail,
             // Store the view in ViewTable was deprecated since v0.0.2.
-            // No need worry about plugin_type.
+            // No need to worry about plugin_type.
             plugin_type: 0,
         }
     }

+ 14 - 18
frontend/rust-lib/flowy-folder/src/services/persistence/version_1/workspace_sql.rs

@@ -1,23 +1,19 @@
-use crate::{
-    entities::{
-        app::RepeatedApp,
-        workspace::{UpdateWorkspaceParams, Workspace},
-    },
-    errors::FlowyError,
-};
+use crate::{entities::workspace::UpdateWorkspaceParams, errors::FlowyError};
 use diesel::SqliteConnection;
 use flowy_database::{
     prelude::*,
     schema::{workspace_table, workspace_table::dsl},
 };
+use flowy_folder_data_model::revision::WorkspaceRevision;
+
 pub(crate) struct WorkspaceTableSql();
 impl WorkspaceTableSql {
     pub(crate) fn create_workspace(
         user_id: &str,
-        workspace: Workspace,
+        workspace_rev: WorkspaceRevision,
         conn: &SqliteConnection,
     ) -> Result<(), FlowyError> {
-        let table = WorkspaceTable::new(workspace, user_id);
+        let table = WorkspaceTable::new(workspace_rev, user_id);
         match diesel_record_count!(workspace_table, &table.id, conn) {
             0 => diesel_insert_table!(workspace_table, &table, conn),
             _ => {
@@ -74,26 +70,26 @@ pub struct WorkspaceTable {
 
 impl WorkspaceTable {
     #[allow(dead_code)]
-    pub fn new(workspace: Workspace, user_id: &str) -> Self {
+    pub fn new(workspace_rev: WorkspaceRevision, user_id: &str) -> Self {
         WorkspaceTable {
-            id: workspace.id,
-            name: workspace.name,
-            desc: workspace.desc,
-            modified_time: workspace.modified_time,
-            create_time: workspace.create_time,
+            id: workspace_rev.id,
+            name: workspace_rev.name,
+            desc: workspace_rev.desc,
+            modified_time: workspace_rev.modified_time,
+            create_time: workspace_rev.create_time,
             user_id: user_id.to_owned(),
             version: 0,
         }
     }
 }
 
-impl std::convert::From<WorkspaceTable> for Workspace {
+impl std::convert::From<WorkspaceTable> for WorkspaceRevision {
     fn from(table: WorkspaceTable) -> Self {
-        Workspace {
+        WorkspaceRevision {
             id: table.id,
             name: table.name,
             desc: table.desc,
-            apps: RepeatedApp::default(),
+            apps: vec![],
             modified_time: table.modified_time,
             create_time: table.create_time,
         }

+ 31 - 36
frontend/rust-lib/flowy-folder/src/services/persistence/version_2/v2_impl.rs

@@ -3,23 +3,18 @@ use crate::services::{
     persistence::{AppChangeset, FolderPersistenceTransaction, ViewChangeset, WorkspaceChangeset},
 };
 use flowy_error::{FlowyError, FlowyResult};
-use flowy_folder_data_model::entities::{
-    app::App,
-    trash::{RepeatedTrash, Trash},
-    view::View,
-    workspace::Workspace,
-};
+use flowy_folder_data_model::revision::{AppRevision, TrashRevision, ViewRevision, WorkspaceRevision};
 use std::sync::Arc;
 
 impl FolderPersistenceTransaction for FolderEditor {
-    fn create_workspace(&self, _user_id: &str, workspace: Workspace) -> FlowyResult<()> {
-        if let Some(change) = self.folder.write().create_workspace(workspace)? {
+    fn create_workspace(&self, _user_id: &str, workspace_rev: WorkspaceRevision) -> FlowyResult<()> {
+        if let Some(change) = self.folder.write().create_workspace(workspace_rev)? {
             let _ = self.apply_change(change)?;
         }
         Ok(())
     }
 
-    fn read_workspaces(&self, _user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>> {
+    fn read_workspaces(&self, _user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<WorkspaceRevision>> {
         let workspaces = self.folder.read().read_workspaces(workspace_id)?;
         Ok(workspaces)
     }
@@ -42,8 +37,8 @@ impl FolderPersistenceTransaction for FolderEditor {
         Ok(())
     }
 
-    fn create_app(&self, app: App) -> FlowyResult<()> {
-        if let Some(change) = self.folder.write().create_app(app)? {
+    fn create_app(&self, app_rev: AppRevision) -> FlowyResult<()> {
+        if let Some(change) = self.folder.write().create_app(app_rev)? {
             let _ = self.apply_change(change)?;
         }
         Ok(())
@@ -60,22 +55,22 @@ impl FolderPersistenceTransaction for FolderEditor {
         Ok(())
     }
 
-    fn read_app(&self, app_id: &str) -> FlowyResult<App> {
+    fn read_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
         let app = self.folder.read().read_app(app_id)?;
         Ok(app)
     }
 
-    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<App>> {
+    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<AppRevision>> {
         let workspaces = self.folder.read().read_workspaces(Some(workspace_id.to_owned()))?;
         match workspaces.first() {
             None => {
                 Err(FlowyError::record_not_found().context(format!("can't find workspace with id {}", workspace_id)))
             }
-            Some(workspace) => Ok(workspace.apps.clone().take_items()),
+            Some(workspace) => Ok(workspace.apps.clone()),
         }
     }
 
-    fn delete_app(&self, app_id: &str) -> FlowyResult<App> {
+    fn delete_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
         let app = self.folder.read().read_app(app_id)?;
         if let Some(change) = self.folder.write().delete_app(app_id)? {
             let _ = self.apply_change(change)?;
@@ -90,19 +85,19 @@ impl FolderPersistenceTransaction for FolderEditor {
         Ok(())
     }
 
-    fn create_view(&self, view: View) -> FlowyResult<()> {
-        if let Some(change) = self.folder.write().create_view(view)? {
+    fn create_view(&self, view_rev: ViewRevision) -> FlowyResult<()> {
+        if let Some(change) = self.folder.write().create_view(view_rev)? {
             let _ = self.apply_change(change)?;
         }
         Ok(())
     }
 
-    fn read_view(&self, view_id: &str) -> FlowyResult<View> {
+    fn read_view(&self, view_id: &str) -> FlowyResult<ViewRevision> {
         let view = self.folder.read().read_view(view_id)?;
         Ok(view)
     }
 
-    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<View>> {
+    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<ViewRevision>> {
         let views = self.folder.read().read_views(belong_to_id)?;
         Ok(views)
     }
@@ -132,16 +127,16 @@ impl FolderPersistenceTransaction for FolderEditor {
         Ok(())
     }
 
-    fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> {
+    fn create_trash(&self, trashes: Vec<TrashRevision>) -> FlowyResult<()> {
         if let Some(change) = self.folder.write().create_trash(trashes)? {
             let _ = self.apply_change(change)?;
         }
         Ok(())
     }
 
-    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> {
+    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<Vec<TrashRevision>> {
         let trash = self.folder.read().read_trash(trash_id)?;
-        Ok(RepeatedTrash { items: trash })
+        Ok(trash)
     }
 
     fn delete_trash(&self, trash_ids: Option<Vec<String>>) -> FlowyResult<()> {
@@ -156,11 +151,11 @@ impl<T> FolderPersistenceTransaction for Arc<T>
 where
     T: FolderPersistenceTransaction + ?Sized,
 {
-    fn create_workspace(&self, user_id: &str, workspace: Workspace) -> FlowyResult<()> {
-        (**self).create_workspace(user_id, workspace)
+    fn create_workspace(&self, user_id: &str, workspace_rev: WorkspaceRevision) -> FlowyResult<()> {
+        (**self).create_workspace(user_id, workspace_rev)
     }
 
-    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<Workspace>> {
+    fn read_workspaces(&self, user_id: &str, workspace_id: Option<String>) -> FlowyResult<Vec<WorkspaceRevision>> {
         (**self).read_workspaces(user_id, workspace_id)
     }
 
@@ -172,23 +167,23 @@ where
         (**self).delete_workspace(workspace_id)
     }
 
-    fn create_app(&self, app: App) -> FlowyResult<()> {
-        (**self).create_app(app)
+    fn create_app(&self, app_rev: AppRevision) -> FlowyResult<()> {
+        (**self).create_app(app_rev)
     }
 
     fn update_app(&self, changeset: AppChangeset) -> FlowyResult<()> {
         (**self).update_app(changeset)
     }
 
-    fn read_app(&self, app_id: &str) -> FlowyResult<App> {
+    fn read_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
         (**self).read_app(app_id)
     }
 
-    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<App>> {
+    fn read_workspace_apps(&self, workspace_id: &str) -> FlowyResult<Vec<AppRevision>> {
         (**self).read_workspace_apps(workspace_id)
     }
 
-    fn delete_app(&self, app_id: &str) -> FlowyResult<App> {
+    fn delete_app(&self, app_id: &str) -> FlowyResult<AppRevision> {
         (**self).delete_app(app_id)
     }
 
@@ -196,15 +191,15 @@ where
         (**self).move_app(app_id, from, to)
     }
 
-    fn create_view(&self, view: View) -> FlowyResult<()> {
-        (**self).create_view(view)
+    fn create_view(&self, view_rev: ViewRevision) -> FlowyResult<()> {
+        (**self).create_view(view_rev)
     }
 
-    fn read_view(&self, view_id: &str) -> FlowyResult<View> {
+    fn read_view(&self, view_id: &str) -> FlowyResult<ViewRevision> {
         (**self).read_view(view_id)
     }
 
-    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<View>> {
+    fn read_views(&self, belong_to_id: &str) -> FlowyResult<Vec<ViewRevision>> {
         (**self).read_views(belong_to_id)
     }
 
@@ -220,11 +215,11 @@ where
         (**self).move_view(view_id, from, to)
     }
 
-    fn create_trash(&self, trashes: Vec<Trash>) -> FlowyResult<()> {
+    fn create_trash(&self, trashes: Vec<TrashRevision>) -> FlowyResult<()> {
         (**self).create_trash(trashes)
     }
 
-    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<RepeatedTrash> {
+    fn read_trash(&self, trash_id: Option<String>) -> FlowyResult<Vec<TrashRevision>> {
         (**self).read_trash(trash_id)
     }
 

+ 33 - 25
frontend/rust-lib/flowy-folder/src/services/trash/controller.rs

@@ -6,6 +6,7 @@ use crate::{
     services::persistence::{FolderPersistence, FolderPersistenceTransaction},
 };
 
+use flowy_folder_data_model::revision::TrashRevision;
 use std::{fmt::Formatter, sync::Arc};
 use tokio::sync::{broadcast, mpsc};
 
@@ -66,18 +67,18 @@ impl TrashController {
 
     #[tracing::instrument(level = "debug", skip(self)  err)]
     pub async fn restore_all_trash(&self) -> FlowyResult<()> {
-        let repeated_trash = self
+        let trash_identifier: RepeatedTrashId = self
             .persistence
             .begin_transaction(|transaction| {
                 let trash = transaction.read_trash(None);
                 let _ = transaction.delete_trash(None);
                 trash
             })
-            .await?;
+            .await?
+            .into();
 
-        let identifiers: RepeatedTrashId = repeated_trash.items.clone().into();
         let (tx, mut rx) = mpsc::channel::<FlowyResult<()>>(1);
-        let _ = self.notify.send(TrashEvent::Putback(identifiers, tx));
+        let _ = self.notify.send(TrashEvent::Putback(trash_identifier, tx));
         let _ = rx.recv().await;
 
         notify_trash_changed(RepeatedTrash { items: vec![] });
@@ -87,12 +88,13 @@ impl TrashController {
 
     #[tracing::instrument(level = "debug", skip(self), err)]
     pub async fn delete_all_trash(&self) -> FlowyResult<()> {
-        let repeated_trash = self
+        let all_trash_identifiers: RepeatedTrashId = self
             .persistence
             .begin_transaction(|transaction| transaction.read_trash(None))
-            .await?;
-        let trash_identifiers: RepeatedTrashId = repeated_trash.items.clone().into();
-        let _ = self.delete_with_identifiers(trash_identifiers.clone()).await?;
+            .await?
+            .into();
+
+        let _ = self.delete_with_identifiers(all_trash_identifiers).await?;
 
         notify_trash_changed(RepeatedTrash { items: vec![] });
         let _ = self.delete_all_trash_on_server().await?;
@@ -102,11 +104,12 @@ impl TrashController {
     #[tracing::instrument(level = "debug", skip(self), err)]
     pub async fn delete(&self, trash_identifiers: RepeatedTrashId) -> FlowyResult<()> {
         let _ = self.delete_with_identifiers(trash_identifiers.clone()).await?;
-        let repeated_trash = self
+        let trash_revs = self
             .persistence
             .begin_transaction(|transaction| transaction.read_trash(None))
             .await?;
-        notify_trash_changed(repeated_trash);
+
+        notify_trash_changed(trash_revs);
         let _ = self.delete_trash_on_server(trash_identifiers)?;
 
         Ok(())
@@ -147,10 +150,10 @@ impl TrashController {
     // CREATE and DROP tables operations because those are auto-commit in the
     // database.
     #[tracing::instrument(name = "add_trash", level = "debug", skip(self, trash), fields(trash_ids), err)]
-    pub async fn add<T: Into<Trash>>(&self, trash: Vec<T>) -> Result<(), FlowyError> {
+    pub async fn add<T: Into<TrashRevision>>(&self, trash: Vec<T>) -> Result<(), FlowyError> {
         let (tx, mut rx) = mpsc::channel::<FlowyResult<()>>(1);
-        let repeated_trash = trash.into_iter().map(|t| t.into()).collect::<Vec<Trash>>();
-        let identifiers = repeated_trash.iter().map(|t| t.into()).collect::<Vec<TrashId>>();
+        let trash_revs: Vec<TrashRevision> = trash.into_iter().map(|t| t.into()).collect();
+        let identifiers = trash_revs.iter().map(|t| t.into()).collect::<Vec<TrashId>>();
 
         tracing::Span::current().record(
             "trash_ids",
@@ -167,8 +170,9 @@ impl TrashController {
         let _ = self
             .persistence
             .begin_transaction(|transaction| {
-                let _ = transaction.create_trash(repeated_trash.clone())?;
-                let _ = self.create_trash_on_server(repeated_trash);
+                let _ = transaction.create_trash(trash_revs.clone())?;
+                let _ = self.create_trash_on_server(trash_revs);
+
                 notify_trash_changed(transaction.read_trash(None)?);
                 Ok(())
             })
@@ -184,12 +188,16 @@ impl TrashController {
     }
 
     pub async fn read_trash(&self) -> Result<RepeatedTrash, FlowyError> {
-        let repeated_trash = self
+        let items: Vec<Trash> = self
             .persistence
             .begin_transaction(|transaction| transaction.read_trash(None))
-            .await?;
+            .await?
+            .into_iter()
+            .map(|trash_rev| trash_rev.into())
+            .collect();
+
         let _ = self.read_trash_on_server()?;
-        Ok(repeated_trash)
+        Ok(RepeatedTrash { items })
     }
 
     pub fn read_trash_ids<'a>(
@@ -198,7 +206,6 @@ impl TrashController {
     ) -> Result<Vec<String>, FlowyError> {
         let ids = transaction
             .read_trash(None)?
-            .into_inner()
             .into_iter()
             .map(|item| item.id)
             .collect::<Vec<String>>();
@@ -244,18 +251,18 @@ impl TrashController {
 
         tokio::spawn(async move {
             match server.read_trash(&token).await {
-                Ok(repeated_trash) => {
-                    tracing::debug!("Remote trash count: {}", repeated_trash.items.len());
+                Ok(trash_rev) => {
+                    tracing::debug!("Remote trash count: {}", trash_rev.len());
                     let result = persistence
                         .begin_transaction(|transaction| {
-                            let _ = transaction.create_trash(repeated_trash.items.clone())?;
+                            let _ = transaction.create_trash(trash_rev.clone())?;
                             transaction.read_trash(None)
                         })
                         .await;
 
                     match result {
-                        Ok(repeated_trash) => {
-                            notify_trash_changed(repeated_trash);
+                        Ok(trash_revs) => {
+                            notify_trash_changed(trash_revs);
                         }
                         Err(e) => log::error!("Save trash failed: {:?}", e),
                     }
@@ -275,7 +282,8 @@ impl TrashController {
 }
 
 #[tracing::instrument(level = "debug", skip(repeated_trash), fields(n_trash))]
-fn notify_trash_changed(repeated_trash: RepeatedTrash) {
+fn notify_trash_changed<T: Into<RepeatedTrash>>(repeated_trash: T) {
+    let repeated_trash = repeated_trash.into();
     tracing::Span::current().record("n_trash", &repeated_trash.len());
     send_anonymous_dart_notification(FolderNotification::TrashUpdated)
         .payload(repeated_trash)

+ 97 - 57
frontend/rust-lib/flowy-folder/src/services/view/controller.rs

@@ -15,6 +15,8 @@ use crate::{
 use bytes::Bytes;
 use flowy_database::kv::KV;
 use flowy_folder_data_model::entities::view::{gen_view_id, ViewDataType};
+use flowy_folder_data_model::entities::ViewInfo;
+use flowy_folder_data_model::revision::ViewRevision;
 use flowy_sync::entities::text_block_info::TextBlockId;
 use futures::{FutureExt, StreamExt};
 use std::{collections::HashSet, sync::Arc};
@@ -52,7 +54,10 @@ impl ViewController {
     }
 
     #[tracing::instrument(level = "trace", skip(self, params), fields(name = %params.name), err)]
-    pub(crate) async fn create_view_from_params(&self, mut params: CreateViewParams) -> Result<View, FlowyError> {
+    pub(crate) async fn create_view_from_params(
+        &self,
+        mut params: CreateViewParams,
+    ) -> Result<ViewRevision, FlowyError> {
         let processor = self.get_data_processor(&params.data_type)?;
         let user_id = self.user.user_id()?;
         if params.data.is_empty() {
@@ -60,16 +65,16 @@ impl ViewController {
             params.data = view_data.to_vec();
         } else {
             let delta_data = processor
-                .process_view_delta_data(&user_id, &params.view_id, params.data.clone())
+                .create_view_from_delta_data(&user_id, &params.view_id, params.data.clone())
                 .await?;
             let _ = self
                 .create_view(&params.view_id, params.data_type.clone(), delta_data)
                 .await?;
         };
 
-        let view = self.create_view_on_server(params).await?;
-        let _ = self.create_view_on_local(view.clone()).await?;
-        Ok(view)
+        let view_rev = self.create_view_on_server(params).await?;
+        let _ = self.create_view_on_local(view_rev.clone()).await?;
+        Ok(view_rev)
     }
 
     #[tracing::instrument(level = "debug", skip(self, view_id, delta_data), err)]
@@ -88,12 +93,12 @@ impl ViewController {
         Ok(())
     }
 
-    pub(crate) async fn create_view_on_local(&self, view: View) -> Result<(), FlowyError> {
+    pub(crate) async fn create_view_on_local(&self, view_rev: ViewRevision) -> Result<(), FlowyError> {
         let trash_controller = self.trash_controller.clone();
         self.persistence
             .begin_transaction(|transaction| {
-                let belong_to_id = view.belong_to_id.clone();
-                let _ = transaction.create_view(view)?;
+                let belong_to_id = view_rev.belong_to_id.clone();
+                let _ = transaction.create_view(view_rev)?;
                 let _ = notify_views_changed(&belong_to_id, trash_controller, &transaction)?;
                 Ok(())
             })
@@ -101,8 +106,8 @@ impl ViewController {
     }
 
     #[tracing::instrument(level = "debug", skip(self, view_id), fields(view_id = %view_id.value), err)]
-    pub(crate) async fn read_view(&self, view_id: ViewId) -> Result<View, FlowyError> {
-        let view = self
+    pub(crate) async fn read_view(&self, view_id: ViewId) -> Result<ViewRevision, FlowyError> {
+        let view_rev = self
             .persistence
             .begin_transaction(|transaction| {
                 let view = transaction.read_view(&view_id.value)?;
@@ -114,10 +119,39 @@ impl ViewController {
             })
             .await?;
         let _ = self.read_view_on_server(view_id);
-        Ok(view)
+        Ok(view_rev)
     }
 
-    pub(crate) async fn read_local_views(&self, ids: Vec<String>) -> Result<Vec<View>, FlowyError> {
+    #[tracing::instrument(level = "debug", skip(self, view_id), fields(view_id = %view_id.value), err)]
+    pub(crate) async fn read_view_info(&self, view_id: ViewId) -> Result<ViewInfo, FlowyError> {
+        let view_info = self
+            .persistence
+            .begin_transaction(|transaction| {
+                let view_rev = transaction.read_view(&view_id.value)?;
+
+                let items: Vec<View> = view_rev
+                    .belongings
+                    .into_iter()
+                    .map(|view_rev| view_rev.into())
+                    .collect();
+
+                let view_info = ViewInfo {
+                    id: view_rev.id,
+                    belong_to_id: view_rev.belong_to_id,
+                    name: view_rev.name,
+                    desc: view_rev.desc,
+                    data_type: view_rev.data_type,
+                    belongings: RepeatedView { items },
+                    ext_data: view_rev.ext_data,
+                };
+                Ok(view_info)
+            })
+            .await?;
+
+        Ok(view_info)
+    }
+
+    pub(crate) async fn read_local_views(&self, ids: Vec<String>) -> Result<Vec<ViewRevision>, FlowyError> {
         self.persistence
             .begin_transaction(|transaction| {
                 let mut views = vec![];
@@ -170,22 +204,22 @@ impl ViewController {
 
     #[tracing::instrument(level = "debug", skip(self), err)]
     pub(crate) async fn duplicate_view(&self, view_id: &str) -> Result<(), FlowyError> {
-        let view = self
+        let view_rev = self
             .persistence
             .begin_transaction(|transaction| transaction.read_view(view_id))
             .await?;
 
-        let processor = self.get_data_processor(&view.data_type)?;
-        let delta_bytes = processor.view_delta_data(view_id).await?;
+        let processor = self.get_data_processor(&view_rev.data_type)?;
+        let delta_bytes = processor.get_delta_data(view_id).await?;
         let duplicate_params = CreateViewParams {
-            belong_to_id: view.belong_to_id.clone(),
-            name: format!("{} (copy)", &view.name),
-            desc: view.desc,
-            thumbnail: view.thumbnail,
-            data_type: view.data_type,
+            belong_to_id: view_rev.belong_to_id.clone(),
+            name: format!("{} (copy)", &view_rev.name),
+            desc: view_rev.desc,
+            thumbnail: view_rev.thumbnail,
+            data_type: view_rev.data_type,
             data: delta_bytes.to_vec(),
             view_id: gen_view_id(),
-            plugin_type: view.plugin_type,
+            plugin_type: view_rev.plugin_type,
         };
 
         let _ = self.create_view_from_params(duplicate_params).await?;
@@ -194,7 +228,7 @@ impl ViewController {
 
     // belong_to_id will be the app_id or view_id.
     #[tracing::instrument(level = "trace", skip(self), err)]
-    pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, FlowyError> {
+    pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<Vec<ViewRevision>, FlowyError> {
         self.persistence
             .begin_transaction(|transaction| {
                 read_belonging_views_on_local(belong_to_id, self.trash_controller.clone(), &transaction)
@@ -203,35 +237,36 @@ impl ViewController {
     }
 
     #[tracing::instrument(level = "debug", skip(self, params), err)]
-    pub(crate) async fn update_view(&self, params: UpdateViewParams) -> Result<View, FlowyError> {
+    pub(crate) async fn update_view(&self, params: UpdateViewParams) -> Result<ViewRevision, FlowyError> {
         let changeset = ViewChangeset::new(params.clone());
         let view_id = changeset.id.clone();
-        let view = self
+        let view_rev = self
             .persistence
             .begin_transaction(|transaction| {
                 let _ = transaction.update_view(changeset)?;
-                let view = transaction.read_view(&view_id)?;
+                let view_rev = transaction.read_view(&view_id)?;
+                let view: View = view_rev.clone().into();
                 send_dart_notification(&view_id, FolderNotification::ViewUpdated)
-                    .payload(view.clone())
+                    .payload(view)
                     .send();
-                let _ = notify_views_changed(&view.belong_to_id, self.trash_controller.clone(), &transaction)?;
-                Ok(view)
+                let _ = notify_views_changed(&view_rev.belong_to_id, self.trash_controller.clone(), &transaction)?;
+                Ok(view_rev)
             })
             .await?;
 
         let _ = self.update_view_on_server(params);
-        Ok(view)
+        Ok(view_rev)
     }
 
-    pub(crate) async fn latest_visit_view(&self) -> FlowyResult<Option<View>> {
+    pub(crate) async fn latest_visit_view(&self) -> FlowyResult<Option<ViewRevision>> {
         match KV::get_str(LATEST_VIEW_ID) {
             None => Ok(None),
             Some(view_id) => {
-                let view = self
+                let view_rev = self
                     .persistence
                     .begin_transaction(|transaction| transaction.read_view(&view_id))
                     .await?;
-                Ok(Some(view))
+                Ok(Some(view_rev))
             }
         }
     }
@@ -239,10 +274,10 @@ impl ViewController {
 
 impl ViewController {
     #[tracing::instrument(level = "debug", skip(self, params), err)]
-    async fn create_view_on_server(&self, params: CreateViewParams) -> Result<View, FlowyError> {
+    async fn create_view_on_server(&self, params: CreateViewParams) -> Result<ViewRevision, FlowyError> {
         let token = self.user.token()?;
-        let view = self.cloud_service.create_view(&token, params).await?;
-        Ok(view)
+        let view_rev = self.cloud_service.create_view(&token, params).await?;
+        Ok(view_rev)
     }
 
     #[tracing::instrument(level = "debug", skip(self), err)]
@@ -269,14 +304,15 @@ impl ViewController {
         // TODO: Retry with RetryAction?
         tokio::spawn(async move {
             match server.read_view(&token, params).await {
-                Ok(Some(view)) => {
+                Ok(Some(view_rev)) => {
                     match persistence
-                        .begin_transaction(|transaction| transaction.create_view(view.clone()))
+                        .begin_transaction(|transaction| transaction.create_view(view_rev.clone()))
                         .await
                     {
                         Ok(_) => {
+                            let view: View = view_rev.into();
                             send_dart_notification(&view.id, FolderNotification::ViewUpdated)
-                                .payload(view.clone())
+                                .payload(view)
                                 .send();
                         }
                         Err(e) => log::error!("Save view failed: {:?}", e),
@@ -350,10 +386,10 @@ async fn handle_trash_event(
         TrashEvent::NewTrash(identifiers, ret) => {
             let result = persistence
                 .begin_transaction(|transaction| {
-                    let views = read_local_views_with_transaction(identifiers, &transaction)?;
-                    for view in views {
-                        let _ = notify_views_changed(&view.belong_to_id, trash_can.clone(), &transaction)?;
-                        notify_dart(view, FolderNotification::ViewDeleted);
+                    let view_revs = read_local_views_with_transaction(identifiers, &transaction)?;
+                    for view_rev in view_revs {
+                        let _ = notify_views_changed(&view_rev.belong_to_id, trash_can.clone(), &transaction)?;
+                        notify_dart(view_rev.into(), FolderNotification::ViewDeleted);
                     }
                     Ok(())
                 })
@@ -363,10 +399,10 @@ async fn handle_trash_event(
         TrashEvent::Putback(identifiers, ret) => {
             let result = persistence
                 .begin_transaction(|transaction| {
-                    let views = read_local_views_with_transaction(identifiers, &transaction)?;
-                    for view in views {
-                        let _ = notify_views_changed(&view.belong_to_id, trash_can.clone(), &transaction)?;
-                        notify_dart(view, FolderNotification::ViewRestored);
+                    let view_revs = read_local_views_with_transaction(identifiers, &transaction)?;
+                    for view_rev in view_revs {
+                        let _ = notify_views_changed(&view_rev.belong_to_id, trash_can.clone(), &transaction)?;
+                        notify_dart(view_rev.into(), FolderNotification::ViewRestored);
                     }
                     Ok(())
                 })
@@ -425,13 +461,12 @@ fn get_data_processor(
 fn read_local_views_with_transaction<'a>(
     identifiers: RepeatedTrashId,
     transaction: &'a (dyn FolderPersistenceTransaction + 'a),
-) -> Result<Vec<View>, FlowyError> {
-    let mut views = vec![];
+) -> Result<Vec<ViewRevision>, FlowyError> {
+    let mut view_revs = vec![];
     for identifier in identifiers.items {
-        let view = transaction.read_view(&identifier.id)?;
-        views.push(view);
+        view_revs.push(transaction.read_view(&identifier.id)?);
     }
-    Ok(views)
+    Ok(view_revs)
 }
 
 fn notify_dart(view: View, notification: FolderNotification) {
@@ -449,8 +484,13 @@ fn notify_views_changed<'a>(
     trash_controller: Arc<TrashController>,
     transaction: &'a (dyn FolderPersistenceTransaction + 'a),
 ) -> FlowyResult<()> {
-    let repeated_view = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), transaction)?;
-    tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
+    let items: Vec<View> = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), transaction)?
+        .into_iter()
+        .map(|view_rev| view_rev.into())
+        .collect();
+    tracing::Span::current().record("view_count", &format!("{}", items.len()).as_str());
+
+    let repeated_view = RepeatedView { items };
     send_dart_notification(belong_to_id, FolderNotification::AppViewsChanged)
         .payload(repeated_view)
         .send();
@@ -461,10 +501,10 @@ fn read_belonging_views_on_local<'a>(
     belong_to_id: &str,
     trash_controller: Arc<TrashController>,
     transaction: &'a (dyn FolderPersistenceTransaction + 'a),
-) -> FlowyResult<RepeatedView> {
-    let mut views = transaction.read_views(belong_to_id)?;
+) -> FlowyResult<Vec<ViewRevision>> {
+    let mut view_revs = transaction.read_views(belong_to_id)?;
     let trash_ids = trash_controller.read_trash_ids(transaction)?;
-    views.retain(|view_table| !trash_ids.contains(&view_table.id));
+    view_revs.retain(|view_table| !trash_ids.contains(&view_table.id));
 
-    Ok(RepeatedView { items: views })
+    Ok(view_revs)
 }

+ 18 - 8
frontend/rust-lib/flowy-folder/src/services/view/event_handler.rs

@@ -11,6 +11,8 @@ use crate::{
     services::{TrashController, ViewController},
 };
 use flowy_folder_data_model::entities::view::{MoveFolderItemParams, MoveFolderItemPayload, MoveFolderItemType};
+use flowy_folder_data_model::entities::ViewInfo;
+use flowy_folder_data_model::revision::TrashRevision;
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::{convert::TryInto, sync::Arc};
 
@@ -19,8 +21,8 @@ pub(crate) async fn create_view_handler(
     controller: AppData<Arc<ViewController>>,
 ) -> DataResult<View, FlowyError> {
     let params: CreateViewParams = data.into_inner().try_into()?;
-    let view = controller.create_view_from_params(params).await?;
-    data_result(view)
+    let view_rev = controller.create_view_from_params(params).await?;
+    data_result(view_rev.into())
 }
 
 pub(crate) async fn read_view_handler(
@@ -28,12 +30,17 @@ pub(crate) async fn read_view_handler(
     controller: AppData<Arc<ViewController>>,
 ) -> DataResult<View, FlowyError> {
     let view_id: ViewId = data.into_inner();
-    let mut view = controller.read_view(view_id.clone()).await?;
-    // For the moment, app and view can contains lots of views. Reading the view
-    // belongings using the view_id.
-    view.belongings = controller.read_views_belong_to(&view_id.value).await?;
+    let view_rev = controller.read_view(view_id.clone()).await?;
+    data_result(view_rev.into())
+}
 
-    data_result(view)
+pub(crate) async fn read_view_info_handler(
+    data: Data<ViewId>,
+    controller: AppData<Arc<ViewController>>,
+) -> DataResult<ViewInfo, FlowyError> {
+    let view_id: ViewId = data.into_inner();
+    let view_info = controller.read_view_info(view_id.clone()).await?;
+    data_result(view_info)
 }
 
 #[tracing::instrument(level = "debug", skip(data, controller), err)]
@@ -61,7 +68,10 @@ pub(crate) async fn delete_view_handler(
         .read_local_views(params.items)
         .await?
         .into_iter()
-        .map(|view| view.into())
+        .map(|view| {
+            let trash_rev: TrashRevision = view.into();
+            trash_rev.into()
+        })
         .collect::<Vec<Trash>>();
 
     let _ = trash_controller.add(trash).await?;

+ 27 - 15
frontend/rust-lib/flowy-folder/src/services/workspace/controller.rs

@@ -9,7 +9,8 @@ use crate::{
     },
 };
 use flowy_database::kv::KV;
-use flowy_folder_data_model::entities::{app::RepeatedApp, workspace::*};
+use flowy_folder_data_model::entities::workspace::*;
+use flowy_folder_data_model::revision::{AppRevision, WorkspaceRevision};
 use std::sync::Arc;
 
 pub struct WorkspaceController {
@@ -37,7 +38,7 @@ impl WorkspaceController {
     pub(crate) async fn create_workspace_from_params(
         &self,
         params: CreateWorkspaceParams,
-    ) -> Result<Workspace, FlowyError> {
+    ) -> Result<WorkspaceRevision, FlowyError> {
         let workspace = self.create_workspace_on_server(params.clone()).await?;
         let user_id = self.user.user_id()?;
         let token = self.user.token()?;
@@ -47,7 +48,10 @@ impl WorkspaceController {
                 let _ = transaction.create_workspace(&user_id, workspace.clone())?;
                 transaction.read_workspaces(&user_id, None)
             })
-            .await?;
+            .await?
+            .into_iter()
+            .map(|workspace_rev| workspace_rev.into())
+            .collect();
         let repeated_workspace = RepeatedWorkspace { items: workspaces };
         send_dart_notification(&token, FolderNotification::UserCreateWorkspace)
             .payload(repeated_workspace)
@@ -109,16 +113,16 @@ impl WorkspaceController {
         }
     }
 
-    pub(crate) async fn read_current_workspace_apps(&self) -> Result<RepeatedApp, FlowyError> {
+    pub(crate) async fn read_current_workspace_apps(&self) -> Result<Vec<AppRevision>, FlowyError> {
         let workspace_id = get_current_workspace()?;
-        let repeated_app = self
+        let app_revs = self
             .persistence
             .begin_transaction(|transaction| {
                 read_local_workspace_apps(&workspace_id, self.trash_controller.clone(), &transaction)
             })
             .await?;
         // TODO: read from server
-        Ok(repeated_app)
+        Ok(app_revs)
     }
 
     #[tracing::instrument(level = "debug", skip(self, transaction), err)]
@@ -129,7 +133,11 @@ impl WorkspaceController {
         transaction: &'a (dyn FolderPersistenceTransaction + 'a),
     ) -> Result<RepeatedWorkspace, FlowyError> {
         let workspace_id = workspace_id.to_owned();
-        let workspaces = transaction.read_workspaces(user_id, workspace_id)?;
+        let workspaces = transaction
+            .read_workspaces(user_id, workspace_id)?
+            .into_iter()
+            .map(|workspace_rev| workspace_rev.into())
+            .collect();
         Ok(RepeatedWorkspace { items: workspaces })
     }
 
@@ -139,22 +147,26 @@ impl WorkspaceController {
         user_id: &str,
         transaction: &'a (dyn FolderPersistenceTransaction + 'a),
     ) -> Result<Workspace, FlowyError> {
-        let mut workspaces = transaction.read_workspaces(user_id, Some(workspace_id.clone()))?;
-        if workspaces.is_empty() {
+        let mut workspace_revs = transaction.read_workspaces(user_id, Some(workspace_id.clone()))?;
+        if workspace_revs.is_empty() {
             return Err(FlowyError::record_not_found().context(format!("{} workspace not found", workspace_id)));
         }
-        debug_assert_eq!(workspaces.len(), 1);
-        let workspace = workspaces.drain(..1).collect::<Vec<Workspace>>().pop().unwrap();
+        debug_assert_eq!(workspace_revs.len(), 1);
+        let workspace = workspace_revs
+            .drain(..1)
+            .map(|workspace_rev| workspace_rev.into())
+            .collect::<Vec<Workspace>>()
+            .pop()
+            .unwrap();
         Ok(workspace)
     }
 }
 
 impl WorkspaceController {
     #[tracing::instrument(level = "trace", skip(self), err)]
-    async fn create_workspace_on_server(&self, params: CreateWorkspaceParams) -> Result<Workspace, FlowyError> {
+    async fn create_workspace_on_server(&self, params: CreateWorkspaceParams) -> Result<WorkspaceRevision, FlowyError> {
         let token = self.user.token()?;
-        let workspace = self.cloud_service.create_workspace(&token, params).await?;
-        Ok(workspace)
+        self.cloud_service.create_workspace(&token, params).await
     }
 
     #[tracing::instrument(level = "trace", skip(self), err)]
@@ -211,7 +223,7 @@ pub async fn notify_workspace_setting_did_change(
             let setting = match transaction.read_view(view_id) {
                 Ok(latest_view) => CurrentWorkspaceSetting {
                     workspace,
-                    latest_view: Some(latest_view),
+                    latest_view: Some(latest_view.into()),
                 },
                 Err(_) => CurrentWorkspaceSetting {
                     workspace,

+ 39 - 20
frontend/rust-lib/flowy-folder/src/services/workspace/event_handler.rs

@@ -9,7 +9,6 @@ use flowy_folder_data_model::entities::{
     view::View,
     workspace::{CurrentWorkspaceSetting, RepeatedWorkspace, WorkspaceId, *},
 };
-
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::{convert::TryInto, sync::Arc};
 
@@ -20,15 +19,21 @@ pub(crate) async fn create_workspace_handler(
 ) -> DataResult<Workspace, FlowyError> {
     let controller = controller.get_ref().clone();
     let params: CreateWorkspaceParams = data.into_inner().try_into()?;
-    let detail = controller.create_workspace_from_params(params).await?;
-    data_result(detail)
+    let workspace_rev = controller.create_workspace_from_params(params).await?;
+    data_result(workspace_rev.into())
 }
 
 #[tracing::instrument(level = "debug", skip(controller), err)]
 pub(crate) async fn read_workspace_apps_handler(
     controller: AppData<Arc<WorkspaceController>>,
 ) -> DataResult<RepeatedApp, FlowyError> {
-    let repeated_app = controller.read_current_workspace_apps().await?;
+    let items = controller
+        .read_current_workspace_apps()
+        .await?
+        .into_iter()
+        .map(|app_rev| app_rev.into())
+        .collect();
+    let repeated_app = RepeatedApp { items };
     data_result(repeated_app)
 }
 
@@ -58,8 +63,10 @@ pub(crate) async fn read_workspaces_handler(
             let mut workspaces =
                 workspace_controller.read_local_workspaces(params.value.clone(), &user_id, &transaction)?;
             for workspace in workspaces.iter_mut() {
-                let apps =
-                    read_local_workspace_apps(&workspace.id, trash_controller.clone(), &transaction)?.into_inner();
+                let apps = read_local_workspace_apps(&workspace.id, trash_controller.clone(), &transaction)?
+                    .into_iter()
+                    .map(|app_rev| app_rev.into())
+                    .collect();
                 workspace.apps.items = apps;
             }
             Ok(workspaces)
@@ -88,7 +95,12 @@ pub async fn read_cur_workspace_handler(
         })
         .await?;
 
-    let latest_view: Option<View> = folder.view_controller.latest_visit_view().await.unwrap_or(None);
+    let latest_view: Option<View> = folder
+        .view_controller
+        .latest_visit_view()
+        .await
+        .unwrap_or(None)
+        .map(|view_rev| view_rev.into());
     let setting = CurrentWorkspaceSetting { workspace, latest_view };
     let _ = read_workspaces_on_server(folder, user_id, params);
     data_result(setting)
@@ -104,25 +116,25 @@ fn read_workspaces_on_server(
     let persistence = folder_manager.persistence.clone();
 
     tokio::spawn(async move {
-        let workspaces = server.read_workspace(&token, params).await?;
+        let workspace_revs = server.read_workspace(&token, params).await?;
         let _ = persistence
             .begin_transaction(|transaction| {
-                tracing::trace!("Save {} workspace", workspaces.len());
-                for workspace in &workspaces.items {
-                    let m_workspace = workspace.clone();
-                    let apps = m_workspace.apps.clone().into_inner();
+                tracing::trace!("Save {} workspace", workspace_revs.len());
+                for workspace_rev in &workspace_revs {
+                    let m_workspace = workspace_rev.clone();
+                    let app_revs = m_workspace.apps.clone();
                     let _ = transaction.create_workspace(&user_id, m_workspace)?;
-                    tracing::trace!("Save {} apps", apps.len());
-                    for app in apps {
-                        let views = app.belongings.clone().into_inner();
-                        match transaction.create_app(app) {
+                    tracing::trace!("Save {} apps", app_revs.len());
+                    for app_rev in app_revs {
+                        let view_revs = app_rev.belongings.clone();
+                        match transaction.create_app(app_rev) {
                             Ok(_) => {}
                             Err(e) => log::error!("create app failed: {:?}", e),
                         }
 
-                        tracing::trace!("Save {} views", views.len());
-                        for view in views {
-                            match transaction.create_view(view) {
+                        tracing::trace!("Save {} views", view_revs.len());
+                        for view_rev in view_revs {
+                            match transaction.create_view(view_rev) {
                                 Ok(_) => {}
                                 Err(e) => log::error!("create view failed: {:?}", e),
                             }
@@ -133,8 +145,15 @@ fn read_workspaces_on_server(
             })
             .await?;
 
+        let repeated_workspace = RepeatedWorkspace {
+            items: workspace_revs
+                .into_iter()
+                .map(|workspace_rev| workspace_rev.into())
+                .collect(),
+        };
+
         send_dart_notification(&token, FolderNotification::WorkspaceListUpdated)
-            .payload(workspaces)
+            .payload(repeated_workspace)
             .send();
         Result::<(), FlowyError>::Ok(())
     });

+ 2 - 6
frontend/rust-lib/flowy-folder/tests/workspace/folder_test.rs

@@ -1,7 +1,7 @@
 use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest};
-
 use flowy_folder::entities::workspace::CreateWorkspacePayload;
 use flowy_folder_data_model::entities::view::ViewDataType;
+
 use flowy_revision::disk::RevisionState;
 use flowy_test::{event_builder::*, FlowySDKTest};
 
@@ -38,11 +38,9 @@ async fn workspace_create() {
 async fn workspace_read() {
     let mut test = FolderTest::new().await;
     let workspace = test.workspace.clone();
-    let json = serde_json::to_string(&workspace).unwrap();
 
     test.run_scripts(vec![
         ReadWorkspace(Some(workspace.id.clone())),
-        AssertWorkspaceJson(json),
         AssertWorkspace(workspace),
     ])
     .await;
@@ -58,8 +56,7 @@ async fn workspace_create_with_apps() {
     .await;
 
     let app = test.app.clone();
-    let json = serde_json::to_string(&app).unwrap();
-    test.run_scripts(vec![ReadApp(app.id), AssertAppJson(json)]).await;
+    test.run_scripts(vec![ReadApp(app.id)]).await;
 }
 
 #[tokio::test]
@@ -343,7 +340,6 @@ async fn folder_sync_revision_with_new_view() {
 
     let view = test.view.clone();
     assert_eq!(view.name, view_name);
-    assert_eq!(view.desc, view_desc);
     test.run_scripts(vec![ReadView(view.id.clone()), AssertView(view)])
         .await;
 }

+ 17 - 14
frontend/rust-lib/flowy-folder/tests/workspace/script.rs

@@ -14,6 +14,7 @@ use flowy_folder_data_model::entities::{
     view::{CreateViewPayload, UpdateViewPayload},
     workspace::{CreateWorkspacePayload, RepeatedWorkspace},
 };
+
 use flowy_revision::disk::RevisionState;
 use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
 use flowy_sync::entities::text_block_info::TextBlockInfo;
@@ -28,7 +29,7 @@ pub enum FolderScript {
         name: String,
         desc: String,
     },
-    AssertWorkspaceJson(String),
+    // AssertWorkspaceRevisionJson(String),
     AssertWorkspace(Workspace),
     ReadWorkspace(Option<String>),
 
@@ -37,7 +38,7 @@ pub enum FolderScript {
         name: String,
         desc: String,
     },
-    AssertAppJson(String),
+    // AssertAppRevisionJson(String),
     AssertApp(App),
     ReadApp(String),
     UpdateApp {
@@ -138,14 +139,15 @@ impl FolderTest {
                 let workspace = create_workspace(sdk, &name, &desc).await;
                 self.workspace = workspace;
             }
-            FolderScript::AssertWorkspaceJson(expected_json) => {
-                let workspace = read_workspace(sdk, Some(self.workspace.id.clone()))
-                    .await
-                    .pop()
-                    .unwrap();
-                let json = serde_json::to_string(&workspace).unwrap();
-                assert_eq!(json, expected_json);
-            }
+            // FolderScript::AssertWorkspaceRevisionJson(expected_json) => {
+            //     let workspace = read_workspace(sdk, Some(self.workspace.id.clone()))
+            //         .await
+            //         .pop()
+            //         .unwrap();
+            //     let workspace_revision: WorkspaceRevision = workspace.into();
+            //     let json = serde_json::to_string(&workspace_revision).unwrap();
+            //     assert_eq!(json, expected_json);
+            // }
             FolderScript::AssertWorkspace(workspace) => {
                 assert_eq!(self.workspace, workspace);
             }
@@ -157,10 +159,11 @@ impl FolderTest {
                 let app = create_app(sdk, &self.workspace.id, &name, &desc).await;
                 self.app = app;
             }
-            FolderScript::AssertAppJson(expected_json) => {
-                let json = serde_json::to_string(&self.app).unwrap();
-                assert_eq!(json, expected_json);
-            }
+            // FolderScript::AssertAppRevisionJson(expected_json) => {
+            //     let app_revision: AppRevision = self.app.clone().into();
+            //     let json = serde_json::to_string(&app_revision).unwrap();
+            //     assert_eq!(json, expected_json);
+            // }
             FolderScript::AssertApp(app) => {
                 assert_eq!(self.app, app);
             }

+ 2 - 1
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -28,7 +28,7 @@ chrono = "0.4.19"
 nanoid = "0.4.0"
 bytes = { version = "1.0" }
 diesel = {version = "1.4.8", features = ["sqlite"]}
-dashmap = "4.0"
+dashmap = "5"
 tokio = {version = "1", features = ["sync"]}
 rayon = "1.5.2"
 serde = { version = "1.0", features = ["derive"] }
@@ -36,6 +36,7 @@ serde_json = {version = "1.0"}
 serde_repr = "0.1"
 indexmap = {version = "1.8.1", features = ["serde"]}
 fancy-regex = "0.10.0"
+regex = "1.5.6"
 url = { version = "2"}
 futures = "0.3.15"
 

+ 30 - 29
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -4,6 +4,7 @@ use crate::services::field::type_options::*;
 use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_json_str};
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::*;
+use flowy_grid_data_model::revision::FieldRevision;
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::sync::Arc;
 
@@ -42,8 +43,8 @@ pub(crate) async fn get_fields_handler(
     let params: QueryFieldParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let field_orders = params.field_orders.items;
-    let field_metas = editor.get_field_metas(Some(field_orders)).await?;
-    let repeated_field: RepeatedField = field_metas.into_iter().map(Field::from).collect::<Vec<_>>().into();
+    let field_revs = editor.get_field_revs(Some(field_orders)).await?;
+    let repeated_field: RepeatedField = field_revs.into_iter().map(Field::from).collect::<Vec<_>>().into();
     data_result(repeated_field)
 }
 
@@ -105,15 +106,15 @@ pub(crate) async fn switch_to_field_handler(
         .await?;
 
     // Get the FieldMeta with field_id, if it doesn't exist, we create the default FieldMeta from the FieldType.
-    let field_meta = editor
-        .get_field_meta(&params.field_id)
+    let field_rev = editor
+        .get_field_rev(&params.field_id)
         .await
-        .unwrap_or(editor.next_field_meta(&params.field_type).await?);
+        .unwrap_or(editor.next_field_rev(&params.field_type).await?);
 
-    let type_option_data = get_type_option_data(&field_meta, &params.field_type).await?;
+    let type_option_data = get_type_option_data(&field_rev, &params.field_type).await?;
     let data = FieldTypeOptionData {
         grid_id: params.grid_id,
-        field: field_meta.into(),
+        field: field_rev.into(),
         type_option_data,
     };
 
@@ -139,13 +140,13 @@ pub(crate) async fn get_field_type_option_data_handler(
 ) -> DataResult<FieldTypeOptionData, FlowyError> {
     let params: EditFieldParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    match editor.get_field_meta(&params.field_id).await {
+    match editor.get_field_rev(&params.field_id).await {
         None => Err(FlowyError::record_not_found()),
-        Some(field_meta) => {
-            let type_option_data = get_type_option_data(&field_meta, &field_meta.field_type).await?;
+        Some(field_rev) => {
+            let type_option_data = get_type_option_data(&field_rev, &field_rev.field_type).await?;
             let data = FieldTypeOptionData {
                 grid_id: params.grid_id,
-                field: field_meta.into(),
+                field: field_rev.into(),
                 type_option_data,
             };
             data_result(data)
@@ -161,12 +162,12 @@ pub(crate) async fn create_field_type_option_data_handler(
 ) -> DataResult<FieldTypeOptionData, FlowyError> {
     let params: CreateFieldParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    let field_meta = editor.create_next_field_meta(&params.field_type).await?;
-    let type_option_data = get_type_option_data(&field_meta, &field_meta.field_type).await?;
+    let field_rev = editor.create_next_field_rev(&params.field_type).await?;
+    let type_option_data = get_type_option_data(&field_rev, &field_rev.field_type).await?;
 
     data_result(FieldTypeOptionData {
         grid_id: params.grid_id,
-        field: field_meta.into(),
+        field: field_rev.into(),
         type_option_data,
     })
 }
@@ -183,11 +184,11 @@ pub(crate) async fn move_item_handler(
 }
 
 /// The FieldMeta contains multiple data, each of them belongs to a specific FieldType.
-async fn get_type_option_data(field_meta: &FieldMeta, field_type: &FieldType) -> FlowyResult<Vec<u8>> {
-    let s = field_meta
+async fn get_type_option_data(field_rev: &FieldRevision, field_type: &FieldType) -> FlowyResult<Vec<u8>> {
+    let s = field_rev
         .get_type_option_str(field_type)
         .unwrap_or_else(|| default_type_option_builder_from_type(field_type).entry().json_str());
-    let builder = type_option_builder_from_json_str(&s, &field_meta.field_type);
+    let builder = type_option_builder_from_json_str(&s, &field_rev.field_type);
     let type_option_data = builder.entry().protobuf_bytes().to_vec();
 
     Ok(type_option_data)
@@ -270,10 +271,10 @@ pub(crate) async fn new_select_option_handler(
 ) -> DataResult<SelectOption, FlowyError> {
     let params: CreateSelectOptionParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    match editor.get_field_meta(&params.field_id).await {
+    match editor.get_field_rev(&params.field_id).await {
         None => Err(ErrorCode::InvalidData.into()),
-        Some(field_meta) => {
-            let type_option = select_option_operation(&field_meta)?;
+        Some(field_rev) => {
+            let type_option = select_option_operation(&field_rev)?;
             let select_option = type_option.create_option(&params.option_name);
             data_result(select_option)
         }
@@ -288,8 +289,8 @@ pub(crate) async fn update_select_option_handler(
     let changeset: SelectOptionChangeset = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&changeset.cell_identifier.grid_id)?;
 
-    if let Some(mut field_meta) = editor.get_field_meta(&changeset.cell_identifier.field_id).await {
-        let mut type_option = select_option_operation(&field_meta)?;
+    if let Some(mut field_rev) = editor.get_field_rev(&changeset.cell_identifier.field_id).await {
+        let mut type_option = select_option_operation(&field_rev)?;
         let mut cell_content_changeset = None;
 
         if let Some(option) = changeset.insert_option {
@@ -306,8 +307,8 @@ pub(crate) async fn update_select_option_handler(
             type_option.delete_option(option);
         }
 
-        field_meta.insert_type_option_entry(&*type_option);
-        let _ = editor.replace_field(field_meta).await?;
+        field_rev.insert_type_option_entry(&*type_option);
+        let _ = editor.replace_field(field_rev).await?;
 
         let changeset = CellChangeset {
             grid_id: changeset.cell_identifier.grid_id,
@@ -327,15 +328,15 @@ pub(crate) async fn get_select_option_handler(
 ) -> DataResult<SelectOptionCellData, FlowyError> {
     let params: CellIdentifier = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    match editor.get_field_meta(&params.field_id).await {
+    match editor.get_field_rev(&params.field_id).await {
         None => {
             tracing::error!("Can't find the select option field with id: {}", params.field_id);
             data_result(SelectOptionCellData::default())
         }
-        Some(field_meta) => {
-            let cell_meta = editor.get_cell_meta(&params.row_id, &params.field_id).await?;
-            let type_option = select_option_operation(&field_meta)?;
-            let option_context = type_option.select_option_cell_data(&cell_meta);
+        Some(field_rev) => {
+            let cell_rev = editor.get_cell_rev(&params.row_id, &params.field_id).await?;
+            let type_option = select_option_operation(&field_rev)?;
+            let option_context = type_option.select_option_cell_data(&cell_rev);
             data_result(option_context)
         }
     }

Vissa filer visades inte eftersom för många filer har ändrats