appflowy 3 سال پیش
والد
کامیت
b547e94273
63فایلهای تغییر یافته به همراه1908 افزوده شده و 361 حذف شده
  1. 13 13
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pb.dart
  2. 5 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pbjson.dart
  3. 11 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbenum.dart
  4. 9 6
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbjson.dart
  5. 2 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart
  6. 2 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart
  7. 1 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/protobuf.dart
  8. 234 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pb.dart
  9. 24 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbenum.dart
  10. 51 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbjson.dart
  11. 9 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbserver.dart
  12. 13 13
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pb.dart
  13. 5 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart
  14. 5 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_user_detail.pb.dart
  15. 2 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_user_detail.pbjson.dart
  16. 4 1
      rust-lib/flowy-database/migrations/2021-07-09-063045_flowy-user/down.sql
  17. 34 0
      rust-lib/flowy-database/migrations/2021-07-09-063045_flowy-user/up.sql
  18. 0 4
      rust-lib/flowy-database/migrations/2021-07-13-063102_flowy-workspace/down.sql
  19. 0 34
      rust-lib/flowy-database/migrations/2021-07-13-063102_flowy-workspace/up.sql
  20. 6 3
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  21. 2 2
      rust-lib/flowy-sdk/src/flowy_server.rs
  22. 2 2
      rust-lib/flowy-workspace/src/entities/app/app_create.rs
  23. 1 1
      rust-lib/flowy-workspace/src/entities/app/mod.rs
  24. 1 0
      rust-lib/flowy-workspace/src/entities/mod.rs
  25. 4 0
      rust-lib/flowy-workspace/src/entities/view/mod.rs
  26. 9 0
      rust-lib/flowy-workspace/src/entities/view/parser/mod.rs
  27. 18 0
      rust-lib/flowy-workspace/src/entities/view/parser/view_desc.rs
  28. 22 0
      rust-lib/flowy-workspace/src/entities/view/parser/view_name.rs
  29. 20 0
      rust-lib/flowy-workspace/src/entities/view/parser/view_thumbnail.rs
  30. 13 0
      rust-lib/flowy-workspace/src/entities/view/parser/view_type.rs
  31. 104 0
      rust-lib/flowy-workspace/src/entities/view/view_create.rs
  32. 1 1
      rust-lib/flowy-workspace/src/entities/workspace/workspace_create.rs
  33. 2 2
      rust-lib/flowy-workspace/src/entities/workspace/workspace_user_detail.rs
  34. 14 5
      rust-lib/flowy-workspace/src/errors.rs
  35. 5 1
      rust-lib/flowy-workspace/src/event.rs
  36. 2 2
      rust-lib/flowy-workspace/src/handlers/app_handler.rs
  37. 2 0
      rust-lib/flowy-workspace/src/handlers/mod.rs
  38. 16 0
      rust-lib/flowy-workspace/src/handlers/view_handler.rs
  39. 2 2
      rust-lib/flowy-workspace/src/handlers/workspace_handler.rs
  40. 35 19
      rust-lib/flowy-workspace/src/macros.rs
  41. 4 1
      rust-lib/flowy-workspace/src/module.rs
  42. 58 58
      rust-lib/flowy-workspace/src/protobuf/model/app_create.rs
  43. 57 41
      rust-lib/flowy-workspace/src/protobuf/model/errors.rs
  44. 16 11
      rust-lib/flowy-workspace/src/protobuf/model/event.rs
  45. 3 0
      rust-lib/flowy-workspace/src/protobuf/model/mod.rs
  46. 803 0
      rust-lib/flowy-workspace/src/protobuf/model/view_create.rs
  47. 43 43
      rust-lib/flowy-workspace/src/protobuf/model/workspace_create.rs
  48. 27 27
      rust-lib/flowy-workspace/src/protobuf/model/workspace_user_detail.rs
  49. 1 1
      rust-lib/flowy-workspace/src/protobuf/proto/app_create.proto
  50. 8 5
      rust-lib/flowy-workspace/src/protobuf/proto/errors.proto
  51. 1 0
      rust-lib/flowy-workspace/src/protobuf/proto/event.proto
  52. 19 0
      rust-lib/flowy-workspace/src/protobuf/proto/view_create.proto
  53. 1 1
      rust-lib/flowy-workspace/src/protobuf/proto/workspace_create.proto
  54. 1 1
      rust-lib/flowy-workspace/src/protobuf/proto/workspace_user_detail.proto
  55. 6 6
      rust-lib/flowy-workspace/src/services/app_controller.rs
  56. 2 0
      rust-lib/flowy-workspace/src/services/mod.rs
  57. 28 0
      rust-lib/flowy-workspace/src/services/view_controller.rs
  58. 4 4
      rust-lib/flowy-workspace/src/services/workspace_controller.rs
  59. 5 16
      rust-lib/flowy-workspace/src/sql_tables/app/app.rs
  60. 106 0
      rust-lib/flowy-workspace/src/sql_tables/view/view.rs
  61. 5 7
      rust-lib/flowy-workspace/src/sql_tables/workspace/workspace.rs
  62. 2 2
      rust-lib/flowy-workspace/tests/event/app_test.rs
  63. 3 3
      rust-lib/flowy-workspace/tests/event/workspace_test.rs

+ 13 - 13
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pb.dart

@@ -147,8 +147,8 @@ class ColorStyle extends $pb.GeneratedMessage {
   void clearThemeColor() => clearField(1);
 }
 
-class AppDetail extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'AppDetail', createEmptyInstance: create)
+class App extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'App', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'workspaceId')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
@@ -156,8 +156,8 @@ class AppDetail extends $pb.GeneratedMessage {
     ..hasRequiredFields = false
   ;
 
-  AppDetail._() : super();
-  factory AppDetail({
+  App._() : super();
+  factory App({
     $core.String? id,
     $core.String? workspaceId,
     $core.String? name,
@@ -178,26 +178,26 @@ class AppDetail extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory AppDetail.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory AppDetail.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory App.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory App.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')
-  AppDetail clone() => AppDetail()..mergeFromMessage(this);
+  App clone() => App()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  AppDetail copyWith(void Function(AppDetail) updates) => super.copyWith((message) => updates(message as AppDetail)) as AppDetail; // ignore: deprecated_member_use
+  App copyWith(void Function(App) updates) => super.copyWith((message) => updates(message as App)) as App; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static AppDetail create() => AppDetail._();
-  AppDetail createEmptyInstance() => create();
-  static $pb.PbList<AppDetail> createRepeated() => $pb.PbList<AppDetail>();
+  static App create() => App._();
+  App createEmptyInstance() => create();
+  static $pb.PbList<App> createRepeated() => $pb.PbList<App>();
   @$core.pragma('dart2js:noInline')
-  static AppDetail getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<AppDetail>(create);
-  static AppDetail? _defaultInstance;
+  static App getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<App>(create);
+  static App? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get id => $_getSZ(0);

+ 5 - 5
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pbjson.dart

@@ -31,9 +31,9 @@ const ColorStyle$json = const {
 
 /// Descriptor for `ColorStyle`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List colorStyleDescriptor = $convert.base64Decode('CgpDb2xvclN0eWxlEh8KC3RoZW1lX2NvbG9yGAEgASgJUgp0aGVtZUNvbG9y');
-@$core.Deprecated('Use appDetailDescriptor instead')
-const AppDetail$json = const {
-  '1': 'AppDetail',
+@$core.Deprecated('Use appDescriptor instead')
+const App$json = const {
+  '1': 'App',
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'workspace_id', '3': 2, '4': 1, '5': 9, '10': 'workspaceId'},
@@ -42,5 +42,5 @@ const AppDetail$json = const {
   ],
 };
 
-/// Descriptor for `AppDetail`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List appDetailDescriptor = $convert.base64Decode('CglBcHBEZXRhaWwSDgoCaWQYASABKAlSAmlkEiEKDHdvcmtzcGFjZV9pZBgCIAEoCVILd29ya3NwYWNlSWQSEgoEbmFtZRgDIAEoCVIEbmFtZRISCgRkZXNjGAQgASgJUgRkZXNj');
+/// Descriptor for `App`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List appDescriptor = $convert.base64Decode('CgNBcHASDgoCaWQYASABKAlSAmlkEiEKDHdvcmtzcGFjZV9pZBgCIAEoCVILd29ya3NwYWNlSWQSEgoEbmFtZRgDIAEoCVIEbmFtZRISCgRkZXNjGAQgASgJUgRkZXNj');

+ 11 - 5
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbenum.dart

@@ -14,11 +14,14 @@ class WorkspaceErrorCode extends $pb.ProtobufEnum {
   static const WorkspaceErrorCode WorkspaceNameInvalid = WorkspaceErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceNameInvalid');
   static const WorkspaceErrorCode WorkspaceIdInvalid = WorkspaceErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceIdInvalid');
   static const WorkspaceErrorCode AppColorStyleInvalid = WorkspaceErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppColorStyleInvalid');
-  static const WorkspaceErrorCode AppIdInvalid = WorkspaceErrorCode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppIdInvalid');
-  static const WorkspaceErrorCode DatabaseConnectionFail = WorkspaceErrorCode._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseConnectionFail');
-  static const WorkspaceErrorCode WorkspaceDatabaseError = WorkspaceErrorCode._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDatabaseError');
-  static const WorkspaceErrorCode UserInternalError = WorkspaceErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserInternalError');
-  static const WorkspaceErrorCode UserNotLoginYet = WorkspaceErrorCode._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNotLoginYet');
+  static const WorkspaceErrorCode AppIdInvalid = WorkspaceErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppIdInvalid');
+  static const WorkspaceErrorCode AppNameInvalid = WorkspaceErrorCode._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppNameInvalid');
+  static const WorkspaceErrorCode ViewNameInvalid = WorkspaceErrorCode._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewNameInvalid');
+  static const WorkspaceErrorCode ViewThumbnailName = WorkspaceErrorCode._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewThumbnailName');
+  static const WorkspaceErrorCode DatabaseConnectionFail = WorkspaceErrorCode._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseConnectionFail');
+  static const WorkspaceErrorCode WorkspaceDatabaseError = WorkspaceErrorCode._(101, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDatabaseError');
+  static const WorkspaceErrorCode UserInternalError = WorkspaceErrorCode._(102, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserInternalError');
+  static const WorkspaceErrorCode UserNotLoginYet = WorkspaceErrorCode._(103, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNotLoginYet');
 
   static const $core.List<WorkspaceErrorCode> values = <WorkspaceErrorCode> [
     Unknown,
@@ -26,6 +29,9 @@ class WorkspaceErrorCode extends $pb.ProtobufEnum {
     WorkspaceIdInvalid,
     AppColorStyleInvalid,
     AppIdInvalid,
+    AppNameInvalid,
+    ViewNameInvalid,
+    ViewThumbnailName,
     DatabaseConnectionFail,
     WorkspaceDatabaseError,
     UserInternalError,

+ 9 - 6
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbjson.dart

@@ -16,16 +16,19 @@ const WorkspaceErrorCode$json = const {
     const {'1': 'WorkspaceNameInvalid', '2': 1},
     const {'1': 'WorkspaceIdInvalid', '2': 2},
     const {'1': 'AppColorStyleInvalid', '2': 3},
-    const {'1': 'AppIdInvalid', '2': 4},
-    const {'1': 'DatabaseConnectionFail', '2': 5},
-    const {'1': 'WorkspaceDatabaseError', '2': 6},
-    const {'1': 'UserInternalError', '2': 10},
-    const {'1': 'UserNotLoginYet', '2': 11},
+    const {'1': 'AppIdInvalid', '2': 10},
+    const {'1': 'AppNameInvalid', '2': 11},
+    const {'1': 'ViewNameInvalid', '2': 20},
+    const {'1': 'ViewThumbnailName', '2': 21},
+    const {'1': 'DatabaseConnectionFail', '2': 100},
+    const {'1': 'WorkspaceDatabaseError', '2': 101},
+    const {'1': 'UserInternalError', '2': 102},
+    const {'1': 'UserNotLoginYet', '2': 103},
   ],
 };
 
 /// Descriptor for `WorkspaceErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceErrorCodeDescriptor = $convert.base64Decode('ChJXb3Jrc3BhY2VFcnJvckNvZGUSCwoHVW5rbm93bhAAEhgKFFdvcmtzcGFjZU5hbWVJbnZhbGlkEAESFgoSV29ya3NwYWNlSWRJbnZhbGlkEAISGAoUQXBwQ29sb3JTdHlsZUludmFsaWQQAxIQCgxBcHBJZEludmFsaWQQBBIaChZEYXRhYmFzZUNvbm5lY3Rpb25GYWlsEAUSGgoWV29ya3NwYWNlRGF0YWJhc2VFcnJvchAGEhUKEVVzZXJJbnRlcm5hbEVycm9yEAoSEwoPVXNlck5vdExvZ2luWWV0EAs=');
+final $typed_data.Uint8List workspaceErrorCodeDescriptor = $convert.base64Decode('ChJXb3Jrc3BhY2VFcnJvckNvZGUSCwoHVW5rbm93bhAAEhgKFFdvcmtzcGFjZU5hbWVJbnZhbGlkEAESFgoSV29ya3NwYWNlSWRJbnZhbGlkEAISGAoUQXBwQ29sb3JTdHlsZUludmFsaWQQAxIQCgxBcHBJZEludmFsaWQQChISCg5BcHBOYW1lSW52YWxpZBALEhMKD1ZpZXdOYW1lSW52YWxpZBAUEhUKEVZpZXdUaHVtYm5haWxOYW1lEBUSGgoWRGF0YWJhc2VDb25uZWN0aW9uRmFpbBBkEhoKFldvcmtzcGFjZURhdGFiYXNlRXJyb3IQZRIVChFVc2VySW50ZXJuYWxFcnJvchBmEhMKD1VzZXJOb3RMb2dpbllldBBn');
 @$core.Deprecated('Use workspaceErrorDescriptor instead')
 const WorkspaceError$json = const {
   '1': 'WorkspaceError',

+ 2 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart

@@ -13,11 +13,13 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
   static const WorkspaceEvent CreateWorkspace = WorkspaceEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateWorkspace');
   static const WorkspaceEvent GetWorkspaceDetail = WorkspaceEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetWorkspaceDetail');
   static const WorkspaceEvent CreateApp = WorkspaceEvent._(101, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateApp');
+  static const WorkspaceEvent CreateView = WorkspaceEvent._(201, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateView');
 
   static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
     CreateWorkspace,
     GetWorkspaceDetail,
     CreateApp,
+    CreateView,
   ];
 
   static final $core.Map<$core.int, WorkspaceEvent> _byValue = $pb.ProtobufEnum.initByValue(values);

+ 2 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart

@@ -15,8 +15,9 @@ const WorkspaceEvent$json = const {
     const {'1': 'CreateWorkspace', '2': 0},
     const {'1': 'GetWorkspaceDetail', '2': 1},
     const {'1': 'CreateApp', '2': 101},
+    const {'1': 'CreateView', '2': 201},
   ],
 };
 
 /// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIWChJHZXRXb3Jrc3BhY2VEZXRhaWwQARINCglDcmVhdGVBcHAQZQ==');
+final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIWChJHZXRXb3Jrc3BhY2VEZXRhaWwQARINCglDcmVhdGVBcHAQZRIPCgpDcmVhdGVWaWV3EMkB');

+ 1 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/protobuf.dart

@@ -3,6 +3,7 @@ export './errors.pb.dart';
 export './workspace_update.pb.dart';
 export './app_create.pb.dart';
 export './event.pb.dart';
+export './view_create.pb.dart';
 export './workspace_user_detail.pb.dart';
 export './workspace_create.pb.dart';
 export './app_update.pb.dart';

+ 234 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pb.dart

@@ -0,0 +1,234 @@
+///
+//  Generated code. Do not modify.
+//  source: view_create.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_create.pbenum.dart';
+
+export 'view_create.pbenum.dart';
+
+enum CreateViewRequest_OneOfThumbnail {
+  thumbnail, 
+  notSet
+}
+
+class CreateViewRequest extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, CreateViewRequest_OneOfThumbnail> _CreateViewRequest_OneOfThumbnailByTag = {
+    4 : CreateViewRequest_OneOfThumbnail.thumbnail,
+    0 : CreateViewRequest_OneOfThumbnail.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateViewRequest', createEmptyInstance: create)
+    ..oo(0, [4])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'appId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
+    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
+    ..e<ViewTypeIdentifier>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewTypeIdentifier.Docs, valueOf: ViewTypeIdentifier.valueOf, enumValues: ViewTypeIdentifier.values)
+    ..hasRequiredFields = false
+  ;
+
+  CreateViewRequest._() : super();
+  factory CreateViewRequest({
+    $core.String? appId,
+    $core.String? name,
+    $core.String? desc,
+    $core.String? thumbnail,
+    ViewTypeIdentifier? viewType,
+  }) {
+    final _result = create();
+    if (appId != null) {
+      _result.appId = appId;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (desc != null) {
+      _result.desc = desc;
+    }
+    if (thumbnail != null) {
+      _result.thumbnail = thumbnail;
+    }
+    if (viewType != null) {
+      _result.viewType = viewType;
+    }
+    return _result;
+  }
+  factory CreateViewRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory CreateViewRequest.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')
+  CreateViewRequest clone() => CreateViewRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  CreateViewRequest copyWith(void Function(CreateViewRequest) updates) => super.copyWith((message) => updates(message as CreateViewRequest)) as CreateViewRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static CreateViewRequest create() => CreateViewRequest._();
+  CreateViewRequest createEmptyInstance() => create();
+  static $pb.PbList<CreateViewRequest> createRepeated() => $pb.PbList<CreateViewRequest>();
+  @$core.pragma('dart2js:noInline')
+  static CreateViewRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateViewRequest>(create);
+  static CreateViewRequest? _defaultInstance;
+
+  CreateViewRequest_OneOfThumbnail whichOneOfThumbnail() => _CreateViewRequest_OneOfThumbnailByTag[$_whichOneof(0)]!;
+  void clearOneOfThumbnail() => clearField($_whichOneof(0));
+
+  @$pb.TagNumber(1)
+  $core.String get appId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set appId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasAppId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearAppId() => 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)
+  $core.String get thumbnail => $_getSZ(3);
+  @$pb.TagNumber(4)
+  set thumbnail($core.String v) { $_setString(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasThumbnail() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearThumbnail() => clearField(4);
+
+  @$pb.TagNumber(5)
+  ViewTypeIdentifier get viewType => $_getN(4);
+  @$pb.TagNumber(5)
+  set viewType(ViewTypeIdentifier v) { setField(5, v); }
+  @$pb.TagNumber(5)
+  $core.bool hasViewType() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearViewType() => clearField(5);
+}
+
+class View extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'View', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'appId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
+    ..e<ViewTypeIdentifier>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewTypeIdentifier.Docs, valueOf: ViewTypeIdentifier.valueOf, enumValues: ViewTypeIdentifier.values)
+    ..hasRequiredFields = false
+  ;
+
+  View._() : super();
+  factory View({
+    $core.String? id,
+    $core.String? appId,
+    $core.String? name,
+    $core.String? desc,
+    ViewTypeIdentifier? viewType,
+  }) {
+    final _result = create();
+    if (id != null) {
+      _result.id = id;
+    }
+    if (appId != null) {
+      _result.appId = appId;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (desc != null) {
+      _result.desc = desc;
+    }
+    if (viewType != null) {
+      _result.viewType = viewType;
+    }
+    return _result;
+  }
+  factory View.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory View.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')
+  View clone() => View()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  View copyWith(void Function(View) updates) => super.copyWith((message) => updates(message as View)) as View; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static View create() => View._();
+  View createEmptyInstance() => create();
+  static $pb.PbList<View> createRepeated() => $pb.PbList<View>();
+  @$core.pragma('dart2js:noInline')
+  static View getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<View>(create);
+  static View? _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 appId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set appId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasAppId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearAppId() => 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)
+  ViewTypeIdentifier get viewType => $_getN(4);
+  @$pb.TagNumber(5)
+  set viewType(ViewTypeIdentifier v) { setField(5, v); }
+  @$pb.TagNumber(5)
+  $core.bool hasViewType() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearViewType() => clearField(5);
+}
+

+ 24 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbenum.dart

@@ -0,0 +1,24 @@
+///
+//  Generated code. Do not modify.
+//  source: view_create.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 ViewTypeIdentifier extends $pb.ProtobufEnum {
+  static const ViewTypeIdentifier Docs = ViewTypeIdentifier._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Docs');
+
+  static const $core.List<ViewTypeIdentifier> values = <ViewTypeIdentifier> [
+    Docs,
+  ];
+
+  static final $core.Map<$core.int, ViewTypeIdentifier> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static ViewTypeIdentifier? valueOf($core.int value) => _byValue[value];
+
+  const ViewTypeIdentifier._($core.int v, $core.String n) : super(v, n);
+}
+

+ 51 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbjson.dart

@@ -0,0 +1,51 @@
+///
+//  Generated code. Do not modify.
+//  source: view_create.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 viewTypeIdentifierDescriptor instead')
+const ViewTypeIdentifier$json = const {
+  '1': 'ViewTypeIdentifier',
+  '2': const [
+    const {'1': 'Docs', '2': 0},
+  ],
+};
+
+/// Descriptor for `ViewTypeIdentifier`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List viewTypeIdentifierDescriptor = $convert.base64Decode('ChJWaWV3VHlwZUlkZW50aWZpZXISCAoERG9jcxAA');
+@$core.Deprecated('Use createViewRequestDescriptor instead')
+const CreateViewRequest$json = const {
+  '1': 'CreateViewRequest',
+  '2': const [
+    const {'1': 'app_id', '3': 1, '4': 1, '5': 9, '10': 'appId'},
+    const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
+    const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
+    const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '9': 0, '10': 'thumbnail'},
+    const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewTypeIdentifier', '10': 'viewType'},
+  ],
+  '8': const [
+    const {'1': 'one_of_thumbnail'},
+  ],
+};
+
+/// Descriptor for `CreateViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List createViewRequestDescriptor = $convert.base64Decode('ChFDcmVhdGVWaWV3UmVxdWVzdBIVCgZhcHBfaWQYASABKAlSBWFwcElkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEZGVzYxgDIAEoCVIEZGVzYxIeCgl0aHVtYm5haWwYBCABKAlIAFIJdGh1bWJuYWlsEjAKCXZpZXdfdHlwZRgFIAEoDjITLlZpZXdUeXBlSWRlbnRpZmllclIIdmlld1R5cGVCEgoQb25lX29mX3RodW1ibmFpbA==');
+@$core.Deprecated('Use viewDescriptor instead')
+const View$json = const {
+  '1': 'View',
+  '2': const [
+    const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
+    const {'1': 'app_id', '3': 2, '4': 1, '5': 9, '10': 'appId'},
+    const {'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'},
+    const {'1': 'desc', '3': 4, '4': 1, '5': 9, '10': 'desc'},
+    const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewTypeIdentifier', '10': 'viewType'},
+  ],
+};
+
+/// Descriptor for `View`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List viewDescriptor = $convert.base64Decode('CgRWaWV3Eg4KAmlkGAEgASgJUgJpZBIVCgZhcHBfaWQYAiABKAlSBWFwcElkEhIKBG5hbWUYAyABKAlSBG5hbWUSEgoEZGVzYxgEIAEoCVIEZGVzYxIwCgl2aWV3X3R5cGUYBSABKA4yEy5WaWV3VHlwZUlkZW50aWZpZXJSCHZpZXdUeXBl');

+ 9 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_create.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: view_create.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_create.pb.dart';
+

+ 13 - 13
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pb.dart

@@ -70,16 +70,16 @@ class CreateWorkspaceRequest extends $pb.GeneratedMessage {
   void clearDesc() => clearField(2);
 }
 
-class WorkspaceDetail extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'WorkspaceDetail', createEmptyInstance: create)
+class Workspace extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Workspace', 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')
     ..hasRequiredFields = false
   ;
 
-  WorkspaceDetail._() : super();
-  factory WorkspaceDetail({
+  Workspace._() : super();
+  factory Workspace({
     $core.String? id,
     $core.String? name,
     $core.String? desc,
@@ -96,26 +96,26 @@ class WorkspaceDetail extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory WorkspaceDetail.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory WorkspaceDetail.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory Workspace.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory Workspace.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')
-  WorkspaceDetail clone() => WorkspaceDetail()..mergeFromMessage(this);
+  Workspace clone() => Workspace()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  WorkspaceDetail copyWith(void Function(WorkspaceDetail) updates) => super.copyWith((message) => updates(message as WorkspaceDetail)) as WorkspaceDetail; // ignore: deprecated_member_use
+  Workspace copyWith(void Function(Workspace) updates) => super.copyWith((message) => updates(message as Workspace)) as Workspace; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static WorkspaceDetail create() => WorkspaceDetail._();
-  WorkspaceDetail createEmptyInstance() => create();
-  static $pb.PbList<WorkspaceDetail> createRepeated() => $pb.PbList<WorkspaceDetail>();
+  static Workspace create() => Workspace._();
+  Workspace createEmptyInstance() => create();
+  static $pb.PbList<Workspace> createRepeated() => $pb.PbList<Workspace>();
   @$core.pragma('dart2js:noInline')
-  static WorkspaceDetail getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<WorkspaceDetail>(create);
-  static WorkspaceDetail? _defaultInstance;
+  static Workspace getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Workspace>(create);
+  static Workspace? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get id => $_getSZ(0);

+ 5 - 5
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart

@@ -19,9 +19,9 @@ const CreateWorkspaceRequest$json = const {
 
 /// Descriptor for `CreateWorkspaceRequest`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List createWorkspaceRequestDescriptor = $convert.base64Decode('ChZDcmVhdGVXb3Jrc3BhY2VSZXF1ZXN0EhIKBG5hbWUYASABKAlSBG5hbWUSEgoEZGVzYxgCIAEoCVIEZGVzYw==');
-@$core.Deprecated('Use workspaceDetailDescriptor instead')
-const WorkspaceDetail$json = const {
-  '1': 'WorkspaceDetail',
+@$core.Deprecated('Use workspaceDescriptor instead')
+const Workspace$json = const {
+  '1': 'Workspace',
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
@@ -29,5 +29,5 @@ const WorkspaceDetail$json = const {
   ],
 };
 
-/// Descriptor for `WorkspaceDetail`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List workspaceDetailDescriptor = $convert.base64Decode('Cg9Xb3Jrc3BhY2VEZXRhaWwSDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEZGVzYxgDIAEoCVIEZGVzYw==');
+/// Descriptor for `Workspace`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List workspaceDescriptor = $convert.base64Decode('CglXb3Jrc3BhY2USDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEZGVzYxgDIAEoCVIEZGVzYw==');

+ 5 - 5
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_user_detail.pb.dart

@@ -75,14 +75,14 @@ class UserWorkspace extends $pb.GeneratedMessage {
 class UserWorkspaceDetail extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserWorkspaceDetail', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'owner')
-    ..aOM<$0.WorkspaceDetail>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'workspace', subBuilder: $0.WorkspaceDetail.create)
+    ..aOM<$0.Workspace>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'workspace', subBuilder: $0.Workspace.create)
     ..hasRequiredFields = false
   ;
 
   UserWorkspaceDetail._() : super();
   factory UserWorkspaceDetail({
     $core.String? owner,
-    $0.WorkspaceDetail? workspace,
+    $0.Workspace? workspace,
   }) {
     final _result = create();
     if (owner != null) {
@@ -124,14 +124,14 @@ class UserWorkspaceDetail extends $pb.GeneratedMessage {
   void clearOwner() => clearField(1);
 
   @$pb.TagNumber(2)
-  $0.WorkspaceDetail get workspace => $_getN(1);
+  $0.Workspace get workspace => $_getN(1);
   @$pb.TagNumber(2)
-  set workspace($0.WorkspaceDetail v) { setField(2, v); }
+  set workspace($0.Workspace v) { setField(2, v); }
   @$pb.TagNumber(2)
   $core.bool hasWorkspace() => $_has(1);
   @$pb.TagNumber(2)
   void clearWorkspace() => clearField(2);
   @$pb.TagNumber(2)
-  $0.WorkspaceDetail ensureWorkspace() => $_ensure(1);
+  $0.Workspace ensureWorkspace() => $_ensure(1);
 }
 

+ 2 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_user_detail.pbjson.dart

@@ -24,9 +24,9 @@ const UserWorkspaceDetail$json = const {
   '1': 'UserWorkspaceDetail',
   '2': const [
     const {'1': 'owner', '3': 1, '4': 1, '5': 9, '10': 'owner'},
-    const {'1': 'workspace', '3': 2, '4': 1, '5': 11, '6': '.WorkspaceDetail', '10': 'workspace'},
+    const {'1': 'workspace', '3': 2, '4': 1, '5': 11, '6': '.Workspace', '10': 'workspace'},
   ],
 };
 
 /// Descriptor for `UserWorkspaceDetail`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List userWorkspaceDetailDescriptor = $convert.base64Decode('ChNVc2VyV29ya3NwYWNlRGV0YWlsEhQKBW93bmVyGAEgASgJUgVvd25lchIuCgl3b3Jrc3BhY2UYAiABKAsyEC5Xb3Jrc3BhY2VEZXRhaWxSCXdvcmtzcGFjZQ==');
+final $typed_data.Uint8List userWorkspaceDetailDescriptor = $convert.base64Decode('ChNVc2VyV29ya3NwYWNlRGV0YWlsEhQKBW93bmVyGAEgASgJUgVvd25lchIoCgl3b3Jrc3BhY2UYAiABKAsyCi5Xb3Jrc3BhY2VSCXdvcmtzcGFjZQ==');

+ 4 - 1
rust-lib/flowy-database/migrations/2021-07-09-063045_flowy-user/down.sql

@@ -1,2 +1,5 @@
 -- This file should undo anything in `up.sql`
-DROP TABLE user_table;
+DROP TABLE user_table;
+DROP TABLE workspace_table;
+DROP TABLE app_table;
+DROP TABLE view_table;

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

@@ -6,3 +6,37 @@ CREATE TABLE user_table (
         password TEXT NOT NULL DEFAULT '',
         email TEXT NOT NULL DEFAULT ''
 );
+
+CREATE TABLE workspace_table (
+                                 id TEXT NOT NULL PRIMARY KEY,
+                                 name TEXT NOT NULL DEFAULT '',
+                                 desc TEXT NOT NULL DEFAULT '',
+                                 modified_time BIGINT NOT NULL DEFAULT 0,
+                                 create_time BIGINT NOT NULL DEFAULT 0,
+                                 user_id TEXT NOT NULL DEFAULT '',
+                                 version BIGINT NOT NULL DEFAULT 0
+);
+
+CREATE TABLE app_table (
+                           id TEXT NOT NULL PRIMARY KEY,
+                           workspace_id TEXT NOT NULL DEFAULT '',
+                           name TEXT NOT NULL DEFAULT '',
+                           desc TEXT NOT NULL DEFAULT '',
+                           color_style BLOB NOT NULL DEFAULT (x''),
+                           last_view_id TEXT DEFAULT '',
+                           modified_time BIGINT NOT NULL DEFAULT 0,
+                           create_time BIGINT NOT NULL DEFAULT 0,
+                           version BIGINT NOT NULL DEFAULT 0
+);
+
+CREATE TABLE view_table (
+                            id TEXT NOT NULL PRIMARY KEY,
+                            app_id TEXT NOT NULL DEFAULT '',
+                            name TEXT NOT NULL DEFAULT '',
+                            desc TEXT NOT NULL DEFAULT '',
+                            modified_time BIGINT NOT NULL DEFAULT 0,
+                            create_time BIGINT NOT NULL DEFAULT 0,
+                            thumbnail TEXT NOT NULL DEFAULT '',
+                            view_type INTEGER NOT NULL DEFAULT 0,
+                            version BIGINT NOT NULL DEFAULT 0
+);

+ 0 - 4
rust-lib/flowy-database/migrations/2021-07-13-063102_flowy-workspace/down.sql

@@ -1,4 +0,0 @@
--- This file should undo anything in `up.sql`
-DROP TABLE workspace_table;
-DROP TABLE app_table;
-DROP TABLE view_table;

+ 0 - 34
rust-lib/flowy-database/migrations/2021-07-13-063102_flowy-workspace/up.sql

@@ -1,34 +0,0 @@
--- Your SQL goes here
-CREATE TABLE workspace_table (
-     id TEXT NOT NULL PRIMARY KEY,
-     name TEXT NOT NULL DEFAULT '',
-     desc TEXT NOT NULL DEFAULT '',
-     modified_time BIGINT NOT NULL DEFAULT 0,
-     create_time BIGINT NOT NULL DEFAULT 0,
-     user_id TEXT NOT NULL DEFAULT '',
-     version BIGINT NOT NULL DEFAULT 0
-);
-
-CREATE TABLE app_table (
-    id TEXT NOT NULL PRIMARY KEY,
-    workspace_id TEXT NOT NULL DEFAULT '',
-    name TEXT NOT NULL DEFAULT '',
-    desc TEXT NOT NULL DEFAULT '',
-    color_style BLOB NOT NULL DEFAULT (x''),
-    last_view_id TEXT DEFAULT '',
-    modified_time BIGINT NOT NULL DEFAULT 0,
-    create_time BIGINT NOT NULL DEFAULT 0,
-    version BIGINT NOT NULL DEFAULT 0
-);
-
-CREATE TABLE view_table (
-    id TEXT NOT NULL PRIMARY KEY,
-    app_id TEXT NOT NULL DEFAULT '',
-    name TEXT NOT NULL DEFAULT '',
-    desc TEXT NOT NULL DEFAULT '',
-    modified_time BIGINT NOT NULL DEFAULT 0,
-    create_time BIGINT NOT NULL DEFAULT 0,
-    thumbnail TEXT NOT NULL DEFAULT '',
-    view_type INTEGER NOT NULL DEFAULT 0,
-    version BIGINT NOT NULL DEFAULT 0
-);

+ 6 - 3
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -18,13 +18,15 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         "KeyValue"
         | "CreateAppRequest"
         | "ColorStyle"
-        | "AppDetail"
+        | "App"
         | "UpdateAppRequest"
         | "UpdateWorkspaceRequest"
         | "CreateWorkspaceRequest"
-        | "WorkspaceDetail"
+        | "Workspace"
         | "UserWorkspace"
         | "UserWorkspaceDetail"
+        | "CreateViewRequest"
+        | "View"
         | "WorkspaceError"
         | "FFIRequest"
         | "FFIResponse"
@@ -37,7 +39,8 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "SignInParams"
         | "UserError"
         => TypeCategory::Protobuf,
-        "WorkspaceEvent"
+        "ViewTypeIdentifier"
+        | "WorkspaceEvent"
         | "WorkspaceErrorCode"
         | "FFIStatusCode"
         | "UserStatus"

+ 2 - 2
rust-lib/flowy-sdk/src/flowy_server.rs

@@ -6,7 +6,7 @@ use flowy_user::{
     sql_tables::UserTable,
 };
 use flowy_workspace::{
-    entities::workspace::{CreateWorkspaceRequest, WorkspaceDetail},
+    entities::workspace::{CreateWorkspaceRequest, Workspace},
     errors::WorkspaceError,
     event::WorkspaceEvent::CreateWorkspace,
 };
@@ -66,7 +66,7 @@ impl UserServer for FlowyServerMocker {
             fut: Box::pin(async move {
                 let _ = EventDispatch::async_send(request)
                     .await
-                    .parse::<WorkspaceDetail, WorkspaceError>()
+                    .parse::<Workspace, WorkspaceError>()
                     .map_err(|e| {
                         ErrorBuilder::new(UserErrorCode::CreateDefaultWorkspaceFailed)
                             .error(e)

+ 2 - 2
rust-lib/flowy-workspace/src/entities/app/app_create.rs

@@ -41,7 +41,7 @@ impl TryInto<CreateAppParams> for CreateAppRequest {
 
     fn try_into(self) -> Result<CreateAppParams, Self::Error> {
         let name = AppName::parse(self.name).map_err(|e| {
-            ErrorBuilder::new(WorkspaceErrorCode::WorkspaceNameInvalid)
+            ErrorBuilder::new(WorkspaceErrorCode::AppNameInvalid)
                 .msg(e)
                 .build()
         })?;
@@ -68,7 +68,7 @@ impl TryInto<CreateAppParams> for CreateAppRequest {
 }
 
 #[derive(ProtoBuf, Default, Debug)]
-pub struct AppDetail {
+pub struct App {
     #[pb(index = 1)]
     pub id: String,
 

+ 1 - 1
rust-lib/flowy-workspace/src/entities/app/mod.rs

@@ -3,4 +3,4 @@ pub use app_update::*;
 
 mod app_create;
 mod app_update;
-mod parser;
+pub mod parser;

+ 1 - 0
rust-lib/flowy-workspace/src/entities/mod.rs

@@ -1,2 +1,3 @@
 pub mod app;
+pub mod view;
 pub mod workspace;

+ 4 - 0
rust-lib/flowy-workspace/src/entities/view/mod.rs

@@ -0,0 +1,4 @@
+mod parser;
+mod view_create;
+
+pub use view_create::*;

+ 9 - 0
rust-lib/flowy-workspace/src/entities/view/parser/mod.rs

@@ -0,0 +1,9 @@
+mod view_desc;
+mod view_name;
+mod view_thumbnail;
+mod view_type;
+
+pub use view_desc::*;
+pub use view_name::*;
+pub use view_thumbnail::*;
+pub use view_type::*;

+ 18 - 0
rust-lib/flowy-workspace/src/entities/view/parser/view_desc.rs

@@ -0,0 +1,18 @@
+use unicode_segmentation::UnicodeSegmentation;
+
+#[derive(Debug)]
+pub struct ViewDesc(pub String);
+
+impl ViewDesc {
+    pub fn parse(s: String) -> Result<ViewDesc, String> {
+        if s.graphemes(true).count() > 1000 {
+            return Err(format!("View desc too long"));
+        }
+
+        Ok(Self(s))
+    }
+}
+
+impl AsRef<str> for ViewDesc {
+    fn as_ref(&self) -> &str { &self.0 }
+}

+ 22 - 0
rust-lib/flowy-workspace/src/entities/view/parser/view_name.rs

@@ -0,0 +1,22 @@
+use unicode_segmentation::UnicodeSegmentation;
+
+#[derive(Debug)]
+pub struct ViewName(pub String);
+
+impl ViewName {
+    pub fn parse(s: String) -> Result<ViewName, String> {
+        if s.trim().is_empty() {
+            return Err(format!("View name can not be empty or whitespace"));
+        }
+
+        if s.graphemes(true).count() > 256 {
+            return Err(format!("View name too long"));
+        }
+
+        Ok(Self(s))
+    }
+}
+
+impl AsRef<str> for ViewName {
+    fn as_ref(&self) -> &str { &self.0 }
+}

+ 20 - 0
rust-lib/flowy-workspace/src/entities/view/parser/view_thumbnail.rs

@@ -0,0 +1,20 @@
+use unicode_segmentation::UnicodeSegmentation;
+
+#[derive(Debug)]
+pub struct ViewThumbnail(pub String);
+
+impl ViewThumbnail {
+    pub fn parse(s: String) -> Result<ViewThumbnail, String> {
+        if s.trim().is_empty() {
+            return Err(format!("View thumbnail can not be empty or whitespace"));
+        }
+
+        // TODO: verify the thumbnail url is valid or not
+
+        Ok(Self(s))
+    }
+}
+
+impl AsRef<str> for ViewThumbnail {
+    fn as_ref(&self) -> &str { &self.0 }
+}

+ 13 - 0
rust-lib/flowy-workspace/src/entities/view/parser/view_type.rs

@@ -0,0 +1,13 @@
+use crate::{entities::view::ViewTypeIdentifier, sql_tables::view::ViewType};
+use unicode_segmentation::UnicodeSegmentation;
+
+#[derive(Debug)]
+pub struct ViewTypeCheck(pub ViewType);
+
+impl ViewTypeCheck {
+    pub fn parse(s: ViewTypeIdentifier) -> Result<ViewTypeCheck, String> {
+        match s {
+            ViewTypeIdentifier::Docs => Ok(Self(ViewType::Docs)),
+        }
+    }
+}

+ 104 - 0
rust-lib/flowy-workspace/src/entities/view/view_create.rs

@@ -0,0 +1,104 @@
+use crate::{
+    entities::{app::parser::AppId, view::parser::*},
+    errors::{ErrorBuilder, WorkspaceError, WorkspaceErrorCode},
+    sql_tables::view::ViewType,
+};
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
+use std::convert::TryInto;
+
+#[derive(Debug, ProtoBuf_Enum)]
+pub enum ViewTypeIdentifier {
+    Docs = 0,
+}
+
+impl std::default::Default for ViewTypeIdentifier {
+    fn default() -> Self { ViewTypeIdentifier::Docs }
+}
+
+#[derive(Default, ProtoBuf)]
+pub struct CreateViewRequest {
+    #[pb(index = 1)]
+    pub app_id: String,
+
+    #[pb(index = 2)]
+    pub name: String,
+
+    #[pb(index = 3)]
+    pub desc: String,
+
+    #[pb(index = 4, one_of)]
+    pub thumbnail: Option<String>,
+
+    #[pb(index = 5)]
+    pub view_type: ViewTypeIdentifier,
+}
+
+pub struct CreateViewParams {
+    pub app_id: String,
+    pub name: String,
+    pub desc: String,
+    pub thumbnail: String,
+    pub view_type: ViewType,
+}
+
+impl TryInto<CreateViewParams> for CreateViewRequest {
+    type Error = WorkspaceError;
+
+    fn try_into(self) -> Result<CreateViewParams, Self::Error> {
+        let name = ViewName::parse(self.name)
+            .map_err(|e| {
+                ErrorBuilder::new(WorkspaceErrorCode::ViewNameInvalid)
+                    .msg(e)
+                    .build()
+            })?
+            .0;
+
+        let app_id = AppId::parse(self.app_id)
+            .map_err(|e| {
+                ErrorBuilder::new(WorkspaceErrorCode::AppIdInvalid)
+                    .msg(e)
+                    .build()
+            })?
+            .0;
+
+        let thumbnail = match self.thumbnail {
+            None => "".to_string(),
+            Some(thumbnail) => {
+                ViewThumbnail::parse(thumbnail)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::ViewThumbnailName)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0
+            },
+        };
+
+        let view_type = ViewTypeCheck::parse(self.view_type).unwrap().0;
+        Ok(CreateViewParams {
+            app_id,
+            name,
+            desc: self.desc,
+            thumbnail,
+            view_type,
+        })
+    }
+}
+
+#[derive(ProtoBuf, Default, Debug)]
+pub struct View {
+    #[pb(index = 1)]
+    pub id: String,
+
+    #[pb(index = 2)]
+    pub app_id: String,
+
+    #[pb(index = 3)]
+    pub name: String,
+
+    #[pb(index = 4)]
+    pub desc: String,
+
+    #[pb(index = 5)]
+    pub view_type: ViewTypeIdentifier,
+}

+ 1 - 1
rust-lib/flowy-workspace/src/entities/workspace/workspace_create.rs

@@ -34,7 +34,7 @@ impl TryInto<CreateWorkspaceParams> for CreateWorkspaceRequest {
 }
 
 #[derive(ProtoBuf, Default, Debug)]
-pub struct WorkspaceDetail {
+pub struct Workspace {
     #[pb(index = 1)]
     pub id: String,
 

+ 2 - 2
rust-lib/flowy-workspace/src/entities/workspace/workspace_user_detail.rs

@@ -1,4 +1,4 @@
-use crate::entities::workspace::WorkspaceDetail;
+use crate::entities::workspace::Workspace;
 use flowy_derive::ProtoBuf;
 
 #[derive(ProtoBuf, Default, Debug)]
@@ -16,5 +16,5 @@ pub struct UserWorkspaceDetail {
     pub owner: String,
 
     #[pb(index = 2)]
-    pub workspace: WorkspaceDetail,
+    pub workspace: Workspace,
 }

+ 14 - 5
rust-lib/flowy-workspace/src/errors.rs

@@ -36,19 +36,28 @@ pub enum WorkspaceErrorCode {
     AppColorStyleInvalid = 3,
 
     #[display(fmt = "App id is invalid")]
-    AppIdInvalid         = 4,
+    AppIdInvalid         = 10,
+
+    #[display(fmt = "App name is invalid")]
+    AppNameInvalid       = 11,
+
+    #[display(fmt = "View name is invalid")]
+    ViewNameInvalid      = 20,
+
+    #[display(fmt = "Thumbnail of the view is invalid")]
+    ViewThumbnailName    = 21,
 
     #[display(fmt = "Get database connection failed")]
-    DatabaseConnectionFail = 5,
+    DatabaseConnectionFail = 100,
 
     #[display(fmt = "Database internal error")]
-    WorkspaceDatabaseError = 6,
+    WorkspaceDatabaseError = 101,
 
     #[display(fmt = "User internal error")]
-    UserInternalError    = 10,
+    UserInternalError    = 102,
 
     #[display(fmt = "User not login yet")]
-    UserNotLoginYet      = 11,
+    UserNotLoginYet      = 103,
 }
 
 impl std::default::Default for WorkspaceErrorCode {

+ 5 - 1
rust-lib/flowy-workspace/src/event.rs

@@ -13,6 +13,10 @@ pub enum WorkspaceEvent {
     GetWorkspaceDetail = 1,
 
     #[display(fmt = "Create app")]
-    #[event(input = "CreateAppRequest", output = "AppDetail")]
+    #[event(input = "CreateAppRequest", output = "App")]
     CreateApp          = 101,
+
+    #[display(fmt = "Create view")]
+    #[event(input = "CreateViewRequest", output = "View")]
+    CreateView         = 201,
 }

+ 2 - 2
rust-lib/flowy-workspace/src/handlers/app_handler.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::app::{AppDetail, CreateAppParams, CreateAppRequest},
+    entities::app::{App, CreateAppParams, CreateAppRequest},
     errors::WorkspaceError,
     services::AppController,
 };
@@ -9,7 +9,7 @@ use std::{convert::TryInto, sync::Arc};
 pub async fn create_app(
     data: Data<CreateAppRequest>,
     controller: ModuleData<Arc<AppController>>,
-) -> ResponseResult<AppDetail, WorkspaceError> {
+) -> ResponseResult<App, WorkspaceError> {
     let params: CreateAppParams = data.into_inner().try_into()?;
     let detail = controller.save_app(params)?;
     response_ok(detail)

+ 2 - 0
rust-lib/flowy-workspace/src/handlers/mod.rs

@@ -1,5 +1,7 @@
 mod app_handler;
+mod view_handler;
 mod workspace_handler;
 
 pub use app_handler::*;
+pub use view_handler::*;
 pub use workspace_handler::*;

+ 16 - 0
rust-lib/flowy-workspace/src/handlers/view_handler.rs

@@ -0,0 +1,16 @@
+use crate::{
+    entities::view::{CreateViewParams, CreateViewRequest, View},
+    errors::WorkspaceError,
+    services::ViewController,
+};
+use flowy_dispatch::prelude::{response_ok, Data, ModuleData, ResponseResult};
+use std::{convert::TryInto, sync::Arc};
+
+pub async fn create_view(
+    data: Data<CreateViewRequest>,
+    controller: ModuleData<Arc<ViewController>>,
+) -> ResponseResult<View, WorkspaceError> {
+    let params: CreateViewParams = data.into_inner().try_into()?;
+    let view = controller.save_view(params).await?;
+    response_ok(view)
+}

+ 2 - 2
rust-lib/flowy-workspace/src/handlers/workspace_handler.rs

@@ -4,7 +4,7 @@ use crate::{
         CreateWorkspaceRequest,
         UserWorkspace,
         UserWorkspaceDetail,
-        WorkspaceDetail,
+        Workspace,
     },
     errors::WorkspaceError,
     services::WorkspaceController,
@@ -15,7 +15,7 @@ use std::{convert::TryInto, sync::Arc};
 pub async fn create_workspace(
     data: Data<CreateWorkspaceRequest>,
     controller: ModuleData<Arc<WorkspaceController>>,
-) -> ResponseResult<WorkspaceDetail, WorkspaceError> {
+) -> ResponseResult<Workspace, WorkspaceError> {
     let controller = controller.get_ref().clone();
     let params: CreateWorkspaceParams = data.into_inner().try_into()?;
     let detail = controller.save_workspace(params).await?;

+ 35 - 19
rust-lib/flowy-workspace/src/macros.rs

@@ -9,25 +9,7 @@ macro_rules! impl_sql_binary_expression {
                 out: &mut diesel::serialize::Output<W, diesel::sqlite::Sqlite>,
             ) -> diesel::serialize::Result {
                 let bytes: Vec<u8> = self.try_into().map_err(|e| format!("{:?}", e))?;
-                diesel::serialize::ToSql::<
-                                                                        diesel::sql_types::Binary,
-                                                                        diesel::sqlite::Sqlite,
-                                                                    >::to_sql(&bytes, out)
-
-                // match self.try_into() {
-                //     Ok(bytes) => diesel::serialize::ToSql::<
-                //         diesel::sql_types::Binary,
-                //         diesel::sqlite::Sqlite,
-                //     >::to_sql(&bytes, out),
-                //     Err(e) => {
-                //         log::error!(
-                //             "{:?} serialize to bytes fail. {:?}",
-                //             std::any::type_name::<$target>(),
-                //             e
-                //         );
-                //         panic!();
-                //     },
-                // }
+                diesel::serialize::ToSql::<diesel::sql_types::Binary,diesel::sqlite::Sqlite,>::to_sql(&bytes, out)
             }
         }
         // https://docs.diesel.rs/src/diesel/sqlite/types/mod.rs.html#30-33
@@ -64,3 +46,37 @@ macro_rules! impl_sql_binary_expression {
         }
     };
 }
+
+#[macro_export]
+macro_rules! impl_sql_integer_expression {
+    ($target:ident) => {
+        use diesel::{
+            deserialize,
+            deserialize::FromSql,
+            serialize,
+            serialize::{Output, ToSql},
+        };
+        use std::io::Write;
+
+        impl<DB> ToSql<Integer, DB> for $target
+        where
+            DB: diesel::backend::Backend,
+            i32: ToSql<Integer, DB>,
+        {
+            fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
+                (*self as i32).to_sql(out)
+            }
+        }
+
+        impl<DB> FromSql<Integer, DB> for $target
+        where
+            DB: diesel::backend::Backend,
+            i32: FromSql<Integer, DB>,
+        {
+            fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
+                let smaill_int = i32::from_sql(bytes)?;
+                Ok($target::from(smaill_int))
+            }
+        }
+    };
+}

+ 4 - 1
rust-lib/flowy-workspace/src/module.rs

@@ -7,7 +7,7 @@ use crate::{
 };
 use flowy_database::DBConnection;
 
-use crate::{entities::workspace::UserWorkspace, handlers::*};
+use crate::{entities::workspace::UserWorkspace, handlers::*, services::ViewController};
 use std::sync::Arc;
 
 pub trait WorkspaceUser: Send + Sync {
@@ -19,12 +19,15 @@ pub trait WorkspaceUser: Send + Sync {
 pub fn create(user: Arc<dyn WorkspaceUser>) -> Module {
     let workspace_controller = Arc::new(WorkspaceController::new(user.clone()));
     let app_controller = Arc::new(AppController::new(user.clone()));
+    let view_controller = Arc::new(ViewController::new(user.clone()));
 
     Module::new()
         .name("Flowy-Workspace")
         .data(workspace_controller)
         .data(app_controller)
+        .data(view_controller)
         .event(WorkspaceEvent::CreateWorkspace, create_workspace)
         .event(WorkspaceEvent::GetWorkspaceDetail, get_workspace_detail)
         .event(WorkspaceEvent::CreateApp, create_app)
+        .event(WorkspaceEvent::CreateView, create_view)
 }

+ 58 - 58
rust-lib/flowy-workspace/src/protobuf/model/app_create.rs

@@ -483,7 +483,7 @@ impl ::protobuf::reflect::ProtobufValue for ColorStyle {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct AppDetail {
+pub struct App {
     // message fields
     pub id: ::std::string::String,
     pub workspace_id: ::std::string::String,
@@ -494,14 +494,14 @@ pub struct AppDetail {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a AppDetail {
-    fn default() -> &'a AppDetail {
-        <AppDetail as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a App {
+    fn default() -> &'a App {
+        <App as ::protobuf::Message>::default_instance()
     }
 }
 
-impl AppDetail {
-    pub fn new() -> AppDetail {
+impl App {
+    pub fn new() -> App {
         ::std::default::Default::default()
     }
 
@@ -610,7 +610,7 @@ impl AppDetail {
     }
 }
 
-impl ::protobuf::Message for AppDetail {
+impl ::protobuf::Message for App {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -703,8 +703,8 @@ impl ::protobuf::Message for AppDetail {
         Self::descriptor_static()
     }
 
-    fn new() -> AppDetail {
-        AppDetail::new()
+    fn new() -> App {
+        App::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -713,39 +713,39 @@ impl ::protobuf::Message for AppDetail {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "id",
-                |m: &AppDetail| { &m.id },
-                |m: &mut AppDetail| { &mut m.id },
+                |m: &App| { &m.id },
+                |m: &mut App| { &mut m.id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "workspace_id",
-                |m: &AppDetail| { &m.workspace_id },
-                |m: &mut AppDetail| { &mut m.workspace_id },
+                |m: &App| { &m.workspace_id },
+                |m: &mut App| { &mut m.workspace_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "name",
-                |m: &AppDetail| { &m.name },
-                |m: &mut AppDetail| { &mut m.name },
+                |m: &App| { &m.name },
+                |m: &mut App| { &mut m.name },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "desc",
-                |m: &AppDetail| { &m.desc },
-                |m: &mut AppDetail| { &mut m.desc },
+                |m: &App| { &m.desc },
+                |m: &mut App| { &mut m.desc },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<AppDetail>(
-                "AppDetail",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<App>(
+                "App",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static AppDetail {
-        static instance: ::protobuf::rt::LazyV2<AppDetail> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(AppDetail::new)
+    fn default_instance() -> &'static App {
+        static instance: ::protobuf::rt::LazyV2<App> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(App::new)
     }
 }
 
-impl ::protobuf::Clear for AppDetail {
+impl ::protobuf::Clear for App {
     fn clear(&mut self) {
         self.id.clear();
         self.workspace_id.clear();
@@ -755,13 +755,13 @@ impl ::protobuf::Clear for AppDetail {
     }
 }
 
-impl ::std::fmt::Debug for AppDetail {
+impl ::std::fmt::Debug for App {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for AppDetail {
+impl ::protobuf::reflect::ProtobufValue for App {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -772,39 +772,39 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     ce_id\x18\x01\x20\x01(\tR\x0bworkspaceId\x12\x12\n\x04name\x18\x02\x20\
     \x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12,\n\
     \x0bcolor_style\x18\x04\x20\x01(\x0b2\x0b.ColorStyleR\ncolorStyle\"-\n\n\
-    ColorStyle\x12\x1f\n\x0btheme_color\x18\x01\x20\x01(\tR\nthemeColor\"f\n\
-    \tAppDetail\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12!\n\x0cworkspac\
-    e_id\x18\x02\x20\x01(\tR\x0bworkspaceId\x12\x12\n\x04name\x18\x03\x20\
-    \x01(\tR\x04name\x12\x12\n\x04desc\x18\x04\x20\x01(\tR\x04descJ\xc9\x04\
-    \n\x06\x12\x04\0\0\x10\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\
-    \0\x12\x04\x02\0\x07\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x18\n\x0b\n\
-    \x04\x04\0\x02\0\x12\x03\x03\x04\x1c\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\
-    \x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x17\n\x0c\n\x05\
-    \x04\0\x02\0\x03\x12\x03\x03\x1a\x1b\n\x0b\n\x04\x04\0\x02\x01\x12\x03\
-    \x04\x04\x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\
-    \x04\0\x02\x01\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\
-    \x03\x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\
-    \x05\x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\
-    \x12\x03\x05\x0b\x0f\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x12\x13\n\
-    \x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x04\x1f\n\x0c\n\x05\x04\0\x02\x03\
-    \x06\x12\x03\x06\x04\x0e\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06\x0f\
-    \x1a\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06\x1d\x1e\n\n\n\x02\x04\x01\
-    \x12\x04\x08\0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x08\x08\x12\n\x0b\n\
-    \x04\x04\x01\x02\0\x12\x03\t\x04\x1b\n\x0c\n\x05\x04\x01\x02\0\x05\x12\
-    \x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\t\x0b\x16\n\x0c\n\x05\
-    \x04\x01\x02\0\x03\x12\x03\t\x19\x1a\n\n\n\x02\x04\x02\x12\x04\x0b\0\x10\
-    \x01\n\n\n\x03\x04\x02\x01\x12\x03\x0b\x08\x11\n\x0b\n\x04\x04\x02\x02\0\
-    \x12\x03\x0c\x04\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0c\x04\n\n\
-    \x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0c\x0b\r\n\x0c\n\x05\x04\x02\x02\0\
-    \x03\x12\x03\x0c\x10\x11\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\r\x04\x1c\n\
-    \x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\r\x04\n\n\x0c\n\x05\x04\x02\x02\
-    \x01\x01\x12\x03\r\x0b\x17\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\r\x1a\
-    \x1b\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\x0e\x04\x14\n\x0c\n\x05\x04\x02\
-    \x02\x02\x05\x12\x03\x0e\x04\n\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\
-    \x0e\x0b\x0f\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x0e\x12\x13\n\x0b\n\
-    \x04\x04\x02\x02\x03\x12\x03\x0f\x04\x14\n\x0c\n\x05\x04\x02\x02\x03\x05\
-    \x12\x03\x0f\x04\n\n\x0c\n\x05\x04\x02\x02\x03\x01\x12\x03\x0f\x0b\x0f\n\
-    \x0c\n\x05\x04\x02\x02\x03\x03\x12\x03\x0f\x12\x13b\x06proto3\
+    ColorStyle\x12\x1f\n\x0btheme_color\x18\x01\x20\x01(\tR\nthemeColor\"`\n\
+    \x03App\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12!\n\x0cworkspace_id\
+    \x18\x02\x20\x01(\tR\x0bworkspaceId\x12\x12\n\x04name\x18\x03\x20\x01(\t\
+    R\x04name\x12\x12\n\x04desc\x18\x04\x20\x01(\tR\x04descJ\xc9\x04\n\x06\
+    \x12\x04\0\0\x10\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\
+    \x04\x02\0\x07\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x18\n\x0b\n\x04\
+    \x04\0\x02\0\x12\x03\x03\x04\x1c\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\
+    \x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x17\n\x0c\n\x05\x04\0\
+    \x02\0\x03\x12\x03\x03\x1a\x1b\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\
+    \x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\
+    \x02\x01\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\
+    \x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\x05\
+    \x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\
+    \x03\x05\x0b\x0f\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x12\x13\n\x0b\
+    \n\x04\x04\0\x02\x03\x12\x03\x06\x04\x1f\n\x0c\n\x05\x04\0\x02\x03\x06\
+    \x12\x03\x06\x04\x0e\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06\x0f\x1a\n\
+    \x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06\x1d\x1e\n\n\n\x02\x04\x01\x12\
+    \x04\x08\0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x08\x08\x12\n\x0b\n\x04\
+    \x04\x01\x02\0\x12\x03\t\x04\x1b\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\t\
+    \x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\t\x0b\x16\n\x0c\n\x05\x04\
+    \x01\x02\0\x03\x12\x03\t\x19\x1a\n\n\n\x02\x04\x02\x12\x04\x0b\0\x10\x01\
+    \n\n\n\x03\x04\x02\x01\x12\x03\x0b\x08\x0b\n\x0b\n\x04\x04\x02\x02\0\x12\
+    \x03\x0c\x04\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0c\x04\n\n\x0c\n\
+    \x05\x04\x02\x02\0\x01\x12\x03\x0c\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\
+    \x12\x03\x0c\x10\x11\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\r\x04\x1c\n\x0c\
+    \n\x05\x04\x02\x02\x01\x05\x12\x03\r\x04\n\n\x0c\n\x05\x04\x02\x02\x01\
+    \x01\x12\x03\r\x0b\x17\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\r\x1a\x1b\
+    \n\x0b\n\x04\x04\x02\x02\x02\x12\x03\x0e\x04\x14\n\x0c\n\x05\x04\x02\x02\
+    \x02\x05\x12\x03\x0e\x04\n\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\x0e\
+    \x0b\x0f\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x0e\x12\x13\n\x0b\n\x04\
+    \x04\x02\x02\x03\x12\x03\x0f\x04\x14\n\x0c\n\x05\x04\x02\x02\x03\x05\x12\
+    \x03\x0f\x04\n\n\x0c\n\x05\x04\x02\x02\x03\x01\x12\x03\x0f\x0b\x0f\n\x0c\
+    \n\x05\x04\x02\x02\x03\x03\x12\x03\x0f\x12\x13b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 57 - 41
rust-lib/flowy-workspace/src/protobuf/model/errors.rs

@@ -219,11 +219,14 @@ pub enum WorkspaceErrorCode {
     WorkspaceNameInvalid = 1,
     WorkspaceIdInvalid = 2,
     AppColorStyleInvalid = 3,
-    AppIdInvalid = 4,
-    DatabaseConnectionFail = 5,
-    WorkspaceDatabaseError = 6,
-    UserInternalError = 10,
-    UserNotLoginYet = 11,
+    AppIdInvalid = 10,
+    AppNameInvalid = 11,
+    ViewNameInvalid = 20,
+    ViewThumbnailName = 21,
+    DatabaseConnectionFail = 100,
+    WorkspaceDatabaseError = 101,
+    UserInternalError = 102,
+    UserNotLoginYet = 103,
 }
 
 impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
@@ -237,11 +240,14 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
             1 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceNameInvalid),
             2 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceIdInvalid),
             3 => ::std::option::Option::Some(WorkspaceErrorCode::AppColorStyleInvalid),
-            4 => ::std::option::Option::Some(WorkspaceErrorCode::AppIdInvalid),
-            5 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseConnectionFail),
-            6 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceDatabaseError),
-            10 => ::std::option::Option::Some(WorkspaceErrorCode::UserInternalError),
-            11 => ::std::option::Option::Some(WorkspaceErrorCode::UserNotLoginYet),
+            10 => ::std::option::Option::Some(WorkspaceErrorCode::AppIdInvalid),
+            11 => ::std::option::Option::Some(WorkspaceErrorCode::AppNameInvalid),
+            20 => ::std::option::Option::Some(WorkspaceErrorCode::ViewNameInvalid),
+            21 => ::std::option::Option::Some(WorkspaceErrorCode::ViewThumbnailName),
+            100 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseConnectionFail),
+            101 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceDatabaseError),
+            102 => ::std::option::Option::Some(WorkspaceErrorCode::UserInternalError),
+            103 => ::std::option::Option::Some(WorkspaceErrorCode::UserNotLoginYet),
             _ => ::std::option::Option::None
         }
     }
@@ -253,6 +259,9 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
             WorkspaceErrorCode::WorkspaceIdInvalid,
             WorkspaceErrorCode::AppColorStyleInvalid,
             WorkspaceErrorCode::AppIdInvalid,
+            WorkspaceErrorCode::AppNameInvalid,
+            WorkspaceErrorCode::ViewNameInvalid,
+            WorkspaceErrorCode::ViewThumbnailName,
             WorkspaceErrorCode::DatabaseConnectionFail,
             WorkspaceErrorCode::WorkspaceDatabaseError,
             WorkspaceErrorCode::UserInternalError,
@@ -287,39 +296,46 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceErrorCode {
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x0cerrors.proto\"K\n\x0eWorkspaceError\x12'\n\x04code\x18\x01\x20\x01\
     (\x0e2\x13.WorkspaceErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\
-    \tR\x03msg*\xe3\x01\n\x12WorkspaceErrorCode\x12\x0b\n\x07Unknown\x10\0\
+    \tR\x03msg*\xa3\x02\n\x12WorkspaceErrorCode\x12\x0b\n\x07Unknown\x10\0\
     \x12\x18\n\x14WorkspaceNameInvalid\x10\x01\x12\x16\n\x12WorkspaceIdInval\
     id\x10\x02\x12\x18\n\x14AppColorStyleInvalid\x10\x03\x12\x10\n\x0cAppIdI\
-    nvalid\x10\x04\x12\x1a\n\x16DatabaseConnectionFail\x10\x05\x12\x1a\n\x16\
-    WorkspaceDatabaseError\x10\x06\x12\x15\n\x11UserInternalError\x10\n\x12\
-    \x13\n\x0fUserNotLoginYet\x10\x0bJ\xa1\x04\n\x06\x12\x04\0\0\x10\x01\n\
-    \x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\
-    \n\x03\x04\0\x01\x12\x03\x02\x08\x16\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\
-    \x04\x20\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\
-    \0\x02\0\x01\x12\x03\x03\x17\x1b\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\
-    \x1e\x1f\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\
-    \x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\
-    \x0b\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x05\
-    \0\x12\x04\x06\0\x10\x01\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x17\n\x0b\n\
-    \x04\x05\0\x02\0\x12\x03\x07\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\
-    \x07\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\x0e\x0f\n\x0b\n\x04\
-    \x05\0\x02\x01\x12\x03\x08\x04\x1d\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\
-    \x08\x04\x18\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x08\x1b\x1c\n\x0b\n\
-    \x04\x05\0\x02\x02\x12\x03\t\x04\x1b\n\x0c\n\x05\x05\0\x02\x02\x01\x12\
-    \x03\t\x04\x16\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\x19\x1a\n\x0b\n\
-    \x04\x05\0\x02\x03\x12\x03\n\x04\x1d\n\x0c\n\x05\x05\0\x02\x03\x01\x12\
-    \x03\n\x04\x18\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\x1b\x1c\n\x0b\n\
-    \x04\x05\0\x02\x04\x12\x03\x0b\x04\x15\n\x0c\n\x05\x05\0\x02\x04\x01\x12\
-    \x03\x0b\x04\x10\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x13\x14\n\x0b\
-    \n\x04\x05\0\x02\x05\x12\x03\x0c\x04\x1f\n\x0c\n\x05\x05\0\x02\x05\x01\
-    \x12\x03\x0c\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x0c\x1d\x1e\n\
-    \x0b\n\x04\x05\0\x02\x06\x12\x03\r\x04\x1f\n\x0c\n\x05\x05\0\x02\x06\x01\
-    \x12\x03\r\x04\x1a\n\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\r\x1d\x1e\n\x0b\
-    \n\x04\x05\0\x02\x07\x12\x03\x0e\x04\x1b\n\x0c\n\x05\x05\0\x02\x07\x01\
-    \x12\x03\x0e\x04\x15\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\x0e\x18\x1a\n\
-    \x0b\n\x04\x05\0\x02\x08\x12\x03\x0f\x04\x19\n\x0c\n\x05\x05\0\x02\x08\
-    \x01\x12\x03\x0f\x04\x13\n\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\x0f\x16\
-    \x18b\x06proto3\
+    nvalid\x10\n\x12\x12\n\x0eAppNameInvalid\x10\x0b\x12\x13\n\x0fViewNameIn\
+    valid\x10\x14\x12\x15\n\x11ViewThumbnailName\x10\x15\x12\x1a\n\x16Databa\
+    seConnectionFail\x10d\x12\x1a\n\x16WorkspaceDatabaseError\x10e\x12\x15\n\
+    \x11UserInternalError\x10f\x12\x13\n\x0fUserNotLoginYet\x10gJ\x9c\x05\n\
+    \x06\x12\x04\0\0\x13\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\
+    \x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x16\n\x0b\n\
+    \x04\x04\0\x02\0\x12\x03\x03\x04\x20\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\
+    \x03\x04\x16\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x17\x1b\n\x0c\n\x05\
+    \x04\0\x02\0\x03\x12\x03\x03\x1e\x1f\n\x0b\n\x04\x04\0\x02\x01\x12\x03\
+    \x04\x04\x13\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\
+    \x04\0\x02\x01\x01\x12\x03\x04\x0b\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\
+    \x03\x04\x11\x12\n\n\n\x02\x05\0\x12\x04\x06\0\x13\x01\n\n\n\x03\x05\0\
+    \x01\x12\x03\x06\x05\x17\n\x0b\n\x04\x05\0\x02\0\x12\x03\x07\x04\x10\n\
+    \x0c\n\x05\x05\0\x02\0\x01\x12\x03\x07\x04\x0b\n\x0c\n\x05\x05\0\x02\0\
+    \x02\x12\x03\x07\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x08\x04\x1d\n\
+    \x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\x04\x18\n\x0c\n\x05\x05\0\x02\
+    \x01\x02\x12\x03\x08\x1b\x1c\n\x0b\n\x04\x05\0\x02\x02\x12\x03\t\x04\x1b\
+    \n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\x04\x16\n\x0c\n\x05\x05\0\x02\
+    \x02\x02\x12\x03\t\x19\x1a\n\x0b\n\x04\x05\0\x02\x03\x12\x03\n\x04\x1d\n\
+    \x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\x18\n\x0c\n\x05\x05\0\x02\x03\
+    \x02\x12\x03\n\x1b\x1c\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x0b\x04\x16\n\
+    \x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\x04\x10\n\x0c\n\x05\x05\0\x02\
+    \x04\x02\x12\x03\x0b\x13\x15\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x0c\x04\
+    \x18\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x0c\x04\x12\n\x0c\n\x05\x05\0\
+    \x02\x05\x02\x12\x03\x0c\x15\x17\n\x0b\n\x04\x05\0\x02\x06\x12\x03\r\x04\
+    \x19\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\r\x04\x13\n\x0c\n\x05\x05\0\
+    \x02\x06\x02\x12\x03\r\x16\x18\n\x0b\n\x04\x05\0\x02\x07\x12\x03\x0e\x04\
+    \x1b\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\x0e\x04\x15\n\x0c\n\x05\x05\0\
+    \x02\x07\x02\x12\x03\x0e\x18\x1a\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0f\
+    \x04!\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0f\x04\x1a\n\x0c\n\x05\x05\
+    \0\x02\x08\x02\x12\x03\x0f\x1d\x20\n\x0b\n\x04\x05\0\x02\t\x12\x03\x10\
+    \x04!\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x10\x04\x1a\n\x0c\n\x05\x05\0\
+    \x02\t\x02\x12\x03\x10\x1d\x20\n\x0b\n\x04\x05\0\x02\n\x12\x03\x11\x04\
+    \x1c\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\x11\x04\x15\n\x0c\n\x05\x05\0\
+    \x02\n\x02\x12\x03\x11\x18\x1b\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x12\x04\
+    \x1a\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x12\x04\x13\n\x0c\n\x05\x05\0\
+    \x02\x0b\x02\x12\x03\x12\x16\x19b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 16 - 11
rust-lib/flowy-workspace/src/protobuf/model/event.rs

@@ -28,6 +28,7 @@ pub enum WorkspaceEvent {
     CreateWorkspace = 0,
     GetWorkspaceDetail = 1,
     CreateApp = 101,
+    CreateView = 201,
 }
 
 impl ::protobuf::ProtobufEnum for WorkspaceEvent {
@@ -40,6 +41,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             0 => ::std::option::Option::Some(WorkspaceEvent::CreateWorkspace),
             1 => ::std::option::Option::Some(WorkspaceEvent::GetWorkspaceDetail),
             101 => ::std::option::Option::Some(WorkspaceEvent::CreateApp),
+            201 => ::std::option::Option::Some(WorkspaceEvent::CreateView),
             _ => ::std::option::Option::None
         }
     }
@@ -49,6 +51,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             WorkspaceEvent::CreateWorkspace,
             WorkspaceEvent::GetWorkspaceDetail,
             WorkspaceEvent::CreateApp,
+            WorkspaceEvent::CreateView,
         ];
         values
     }
@@ -77,17 +80,19 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0bevent.proto*L\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorkspace\x10\
-    \0\x12\x16\n\x12GetWorkspaceDetail\x10\x01\x12\r\n\tCreateApp\x10eJ\xa5\
-    \x01\n\x06\x12\x04\0\0\x06\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
-    \x05\0\x12\x04\x02\0\x06\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x13\n\
-    \x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x18\n\x0c\n\x05\x05\0\x02\0\x01\
-    \x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x16\x17\n\
-    \x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x1b\n\x0c\n\x05\x05\0\x02\x01\
-    \x01\x12\x03\x04\x04\x16\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\x19\
-    \x1a\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\x05\x05\0\x02\
-    \x02\x01\x12\x03\x05\x04\r\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\x10\
-    \x13b\x06proto3\
+    \n\x0bevent.proto*]\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorkspace\x10\
+    \0\x12\x16\n\x12GetWorkspaceDetail\x10\x01\x12\r\n\tCreateApp\x10e\x12\
+    \x0f\n\nCreateView\x10\xc9\x01J\xce\x01\n\x06\x12\x04\0\0\x07\x01\n\x08\
+    \n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x07\x01\n\n\n\
+    \x03\x05\0\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\
+    \x04\x18\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\
+    \0\x02\0\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\
+    \x04\x1b\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x16\n\x0c\n\x05\
+    \x05\0\x02\x01\x02\x12\x03\x04\x19\x1a\n\x0b\n\x04\x05\0\x02\x02\x12\x03\
+    \x05\x04\x14\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\r\n\x0c\n\x05\
+    \x05\0\x02\x02\x02\x12\x03\x05\x10\x13\n\x0b\n\x04\x05\0\x02\x03\x12\x03\
+    \x06\x04\x15\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x0e\n\x0c\n\
+    \x05\x05\0\x02\x03\x02\x12\x03\x06\x11\x14b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 3 - 0
rust-lib/flowy-workspace/src/protobuf/model/mod.rs

@@ -12,6 +12,9 @@ pub use app_create::*;
 mod event; 
 pub use event::*; 
 
+mod view_create; 
+pub use view_create::*; 
+
 mod workspace_user_detail; 
 pub use workspace_user_detail::*; 
 

+ 803 - 0
rust-lib/flowy-workspace/src/protobuf/model/view_create.rs

@@ -0,0 +1,803 @@
+// This file is generated by rust-protobuf 2.22.1. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `view_create.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
+
+#[derive(PartialEq,Clone,Default)]
+pub struct CreateViewRequest {
+    // message fields
+    pub app_id: ::std::string::String,
+    pub name: ::std::string::String,
+    pub desc: ::std::string::String,
+    pub view_type: ViewTypeIdentifier,
+    // message oneof groups
+    pub one_of_thumbnail: ::std::option::Option<CreateViewRequest_oneof_one_of_thumbnail>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a CreateViewRequest {
+    fn default() -> &'a CreateViewRequest {
+        <CreateViewRequest as ::protobuf::Message>::default_instance()
+    }
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum CreateViewRequest_oneof_one_of_thumbnail {
+    thumbnail(::std::string::String),
+}
+
+impl CreateViewRequest {
+    pub fn new() -> CreateViewRequest {
+        ::std::default::Default::default()
+    }
+
+    // string app_id = 1;
+
+
+    pub fn get_app_id(&self) -> &str {
+        &self.app_id
+    }
+    pub fn clear_app_id(&mut self) {
+        self.app_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_app_id(&mut self, v: ::std::string::String) {
+        self.app_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_app_id(&mut self) -> &mut ::std::string::String {
+        &mut self.app_id
+    }
+
+    // Take field
+    pub fn take_app_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.app_id, ::std::string::String::new())
+    }
+
+    // string name = 2;
+
+
+    pub fn get_name(&self) -> &str {
+        &self.name
+    }
+    pub fn clear_name(&mut self) {
+        self.name.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.name = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        &mut self.name
+    }
+
+    // Take field
+    pub fn take_name(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.name, ::std::string::String::new())
+    }
+
+    // string desc = 3;
+
+
+    pub fn get_desc(&self) -> &str {
+        &self.desc
+    }
+    pub fn clear_desc(&mut self) {
+        self.desc.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_desc(&mut self, v: ::std::string::String) {
+        self.desc = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_desc(&mut self) -> &mut ::std::string::String {
+        &mut self.desc
+    }
+
+    // Take field
+    pub fn take_desc(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.desc, ::std::string::String::new())
+    }
+
+    // string thumbnail = 4;
+
+
+    pub fn get_thumbnail(&self) -> &str {
+        match self.one_of_thumbnail {
+            ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_thumbnail(&mut self) {
+        self.one_of_thumbnail = ::std::option::Option::None;
+    }
+
+    pub fn has_thumbnail(&self) -> bool {
+        match self.one_of_thumbnail {
+            ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_thumbnail(&mut self, v: ::std::string::String) {
+        self.one_of_thumbnail = ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_thumbnail(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(_)) = self.one_of_thumbnail {
+        } else {
+            self.one_of_thumbnail = ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(::std::string::String::new()));
+        }
+        match self.one_of_thumbnail {
+            ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_thumbnail(&mut self) -> ::std::string::String {
+        if self.has_thumbnail() {
+            match self.one_of_thumbnail.take() {
+                ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // .ViewTypeIdentifier view_type = 5;
+
+
+    pub fn get_view_type(&self) -> ViewTypeIdentifier {
+        self.view_type
+    }
+    pub fn clear_view_type(&mut self) {
+        self.view_type = ViewTypeIdentifier::Docs;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_view_type(&mut self, v: ViewTypeIdentifier) {
+        self.view_type = v;
+    }
+}
+
+impl ::protobuf::Message for CreateViewRequest {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.app_id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.desc)?;
+                },
+                4 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_thumbnail = ::std::option::Option::Some(CreateViewRequest_oneof_one_of_thumbnail::thumbnail(is.read_string()?));
+                },
+                5 => {
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.view_type, 5, &mut self.unknown_fields)?
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.app_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.app_id);
+        }
+        if !self.name.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.name);
+        }
+        if !self.desc.is_empty() {
+            my_size += ::protobuf::rt::string_size(3, &self.desc);
+        }
+        if self.view_type != ViewTypeIdentifier::Docs {
+            my_size += ::protobuf::rt::enum_size(5, self.view_type);
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
+            match v {
+                &CreateViewRequest_oneof_one_of_thumbnail::thumbnail(ref v) => {
+                    my_size += ::protobuf::rt::string_size(4, &v);
+                },
+            };
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.app_id.is_empty() {
+            os.write_string(1, &self.app_id)?;
+        }
+        if !self.name.is_empty() {
+            os.write_string(2, &self.name)?;
+        }
+        if !self.desc.is_empty() {
+            os.write_string(3, &self.desc)?;
+        }
+        if self.view_type != ViewTypeIdentifier::Docs {
+            os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
+            match v {
+                &CreateViewRequest_oneof_one_of_thumbnail::thumbnail(ref v) => {
+                    os.write_string(4, v)?;
+                },
+            };
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> CreateViewRequest {
+        CreateViewRequest::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "app_id",
+                |m: &CreateViewRequest| { &m.app_id },
+                |m: &mut CreateViewRequest| { &mut m.app_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "name",
+                |m: &CreateViewRequest| { &m.name },
+                |m: &mut CreateViewRequest| { &mut m.name },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "desc",
+                |m: &CreateViewRequest| { &m.desc },
+                |m: &mut CreateViewRequest| { &mut m.desc },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "thumbnail",
+                CreateViewRequest::has_thumbnail,
+                CreateViewRequest::get_thumbnail,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewTypeIdentifier>>(
+                "view_type",
+                |m: &CreateViewRequest| { &m.view_type },
+                |m: &mut CreateViewRequest| { &mut m.view_type },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateViewRequest>(
+                "CreateViewRequest",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static CreateViewRequest {
+        static instance: ::protobuf::rt::LazyV2<CreateViewRequest> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(CreateViewRequest::new)
+    }
+}
+
+impl ::protobuf::Clear for CreateViewRequest {
+    fn clear(&mut self) {
+        self.app_id.clear();
+        self.name.clear();
+        self.desc.clear();
+        self.one_of_thumbnail = ::std::option::Option::None;
+        self.view_type = ViewTypeIdentifier::Docs;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for CreateViewRequest {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for CreateViewRequest {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct View {
+    // message fields
+    pub id: ::std::string::String,
+    pub app_id: ::std::string::String,
+    pub name: ::std::string::String,
+    pub desc: ::std::string::String,
+    pub view_type: ViewTypeIdentifier,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a View {
+    fn default() -> &'a View {
+        <View as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl View {
+    pub fn new() -> View {
+        ::std::default::Default::default()
+    }
+
+    // string id = 1;
+
+
+    pub fn get_id(&self) -> &str {
+        &self.id
+    }
+    pub fn clear_id(&mut self) {
+        self.id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_id(&mut self, v: ::std::string::String) {
+        self.id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_id(&mut self) -> &mut ::std::string::String {
+        &mut self.id
+    }
+
+    // Take field
+    pub fn take_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.id, ::std::string::String::new())
+    }
+
+    // string app_id = 2;
+
+
+    pub fn get_app_id(&self) -> &str {
+        &self.app_id
+    }
+    pub fn clear_app_id(&mut self) {
+        self.app_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_app_id(&mut self, v: ::std::string::String) {
+        self.app_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_app_id(&mut self) -> &mut ::std::string::String {
+        &mut self.app_id
+    }
+
+    // Take field
+    pub fn take_app_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.app_id, ::std::string::String::new())
+    }
+
+    // string name = 3;
+
+
+    pub fn get_name(&self) -> &str {
+        &self.name
+    }
+    pub fn clear_name(&mut self) {
+        self.name.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.name = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        &mut self.name
+    }
+
+    // Take field
+    pub fn take_name(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.name, ::std::string::String::new())
+    }
+
+    // string desc = 4;
+
+
+    pub fn get_desc(&self) -> &str {
+        &self.desc
+    }
+    pub fn clear_desc(&mut self) {
+        self.desc.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_desc(&mut self, v: ::std::string::String) {
+        self.desc = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_desc(&mut self) -> &mut ::std::string::String {
+        &mut self.desc
+    }
+
+    // Take field
+    pub fn take_desc(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.desc, ::std::string::String::new())
+    }
+
+    // .ViewTypeIdentifier view_type = 5;
+
+
+    pub fn get_view_type(&self) -> ViewTypeIdentifier {
+        self.view_type
+    }
+    pub fn clear_view_type(&mut self) {
+        self.view_type = ViewTypeIdentifier::Docs;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_view_type(&mut self, v: ViewTypeIdentifier) {
+        self.view_type = v;
+    }
+}
+
+impl ::protobuf::Message for View {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.app_id)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
+                },
+                4 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.desc)?;
+                },
+                5 => {
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.view_type, 5, &mut self.unknown_fields)?
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.id);
+        }
+        if !self.app_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.app_id);
+        }
+        if !self.name.is_empty() {
+            my_size += ::protobuf::rt::string_size(3, &self.name);
+        }
+        if !self.desc.is_empty() {
+            my_size += ::protobuf::rt::string_size(4, &self.desc);
+        }
+        if self.view_type != ViewTypeIdentifier::Docs {
+            my_size += ::protobuf::rt::enum_size(5, self.view_type);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.id.is_empty() {
+            os.write_string(1, &self.id)?;
+        }
+        if !self.app_id.is_empty() {
+            os.write_string(2, &self.app_id)?;
+        }
+        if !self.name.is_empty() {
+            os.write_string(3, &self.name)?;
+        }
+        if !self.desc.is_empty() {
+            os.write_string(4, &self.desc)?;
+        }
+        if self.view_type != ViewTypeIdentifier::Docs {
+            os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> View {
+        View::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "id",
+                |m: &View| { &m.id },
+                |m: &mut View| { &mut m.id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "app_id",
+                |m: &View| { &m.app_id },
+                |m: &mut View| { &mut m.app_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "name",
+                |m: &View| { &m.name },
+                |m: &mut View| { &mut m.name },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "desc",
+                |m: &View| { &m.desc },
+                |m: &mut View| { &mut m.desc },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<ViewTypeIdentifier>>(
+                "view_type",
+                |m: &View| { &m.view_type },
+                |m: &mut View| { &mut m.view_type },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<View>(
+                "View",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static View {
+        static instance: ::protobuf::rt::LazyV2<View> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(View::new)
+    }
+}
+
+impl ::protobuf::Clear for View {
+    fn clear(&mut self) {
+        self.id.clear();
+        self.app_id.clear();
+        self.name.clear();
+        self.desc.clear();
+        self.view_type = ViewTypeIdentifier::Docs;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for View {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for View {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(Clone,PartialEq,Eq,Debug,Hash)]
+pub enum ViewTypeIdentifier {
+    Docs = 0,
+}
+
+impl ::protobuf::ProtobufEnum for ViewTypeIdentifier {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<ViewTypeIdentifier> {
+        match value {
+            0 => ::std::option::Option::Some(ViewTypeIdentifier::Docs),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [ViewTypeIdentifier] = &[
+            ViewTypeIdentifier::Docs,
+        ];
+        values
+    }
+
+    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<ViewTypeIdentifier>("ViewTypeIdentifier", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for ViewTypeIdentifier {
+}
+
+impl ::std::default::Default for ViewTypeIdentifier {
+    fn default() -> Self {
+        ViewTypeIdentifier::Docs
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for ViewTypeIdentifier {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x11view_create.proto\"\xb8\x01\n\x11CreateViewRequest\x12\x15\n\x06ap\
+    p_id\x18\x01\x20\x01(\tR\x05appId\x12\x12\n\x04name\x18\x02\x20\x01(\tR\
+    \x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12\x1e\n\tthumbn\
+    ail\x18\x04\x20\x01(\tH\0R\tthumbnail\x120\n\tview_type\x18\x05\x20\x01(\
+    \x0e2\x13.ViewTypeIdentifierR\x08viewTypeB\x12\n\x10one_of_thumbnail\"\
+    \x87\x01\n\x04View\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x15\n\
+    \x06app_id\x18\x02\x20\x01(\tR\x05appId\x12\x12\n\x04name\x18\x03\x20\
+    \x01(\tR\x04name\x12\x12\n\x04desc\x18\x04\x20\x01(\tR\x04desc\x120\n\tv\
+    iew_type\x18\x05\x20\x01(\x0e2\x13.ViewTypeIdentifierR\x08viewType*\x1e\
+    \n\x12ViewTypeIdentifier\x12\x08\n\x04Docs\x10\0J\xc4\x05\n\x06\x12\x04\
+    \0\0\x12\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\
+    \0\x08\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x19\n\x0b\n\x04\x04\0\x02\
+    \0\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\
+    \x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x11\n\x0c\n\x05\x04\0\x02\0\
+    \x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x14\n\
+    \x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\
+    \x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x12\
+    \x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\x05\x04\0\x02\
+    \x02\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x05\x0b\
+    \x0f\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x12\x13\n\x0b\n\x04\x04\0\
+    \x08\0\x12\x03\x06\x044\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\x06\n\x1a\n\
+    \x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x1d2\n\x0c\n\x05\x04\0\x02\x03\x05\
+    \x12\x03\x06\x1d#\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06$-\n\x0c\n\
+    \x05\x04\0\x02\x03\x03\x12\x03\x0601\n\x0b\n\x04\x04\0\x02\x04\x12\x03\
+    \x07\x04%\n\x0c\n\x05\x04\0\x02\x04\x06\x12\x03\x07\x04\x16\n\x0c\n\x05\
+    \x04\0\x02\x04\x01\x12\x03\x07\x17\x20\n\x0c\n\x05\x04\0\x02\x04\x03\x12\
+    \x03\x07#$\n\n\n\x02\x04\x01\x12\x04\t\0\x0f\x01\n\n\n\x03\x04\x01\x01\
+    \x12\x03\t\x08\x0c\n\x0b\n\x04\x04\x01\x02\0\x12\x03\n\x04\x12\n\x0c\n\
+    \x05\x04\x01\x02\0\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\
+    \x03\n\x0b\r\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\n\x10\x11\n\x0b\n\x04\
+    \x04\x01\x02\x01\x12\x03\x0b\x04\x16\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\
+    \x03\x0b\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x0b\x0b\x11\n\x0c\
+    \n\x05\x04\x01\x02\x01\x03\x12\x03\x0b\x14\x15\n\x0b\n\x04\x04\x01\x02\
+    \x02\x12\x03\x0c\x04\x14\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x0c\x04\
+    \n\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\x0c\x0b\x0f\n\x0c\n\x05\x04\
+    \x01\x02\x02\x03\x12\x03\x0c\x12\x13\n\x0b\n\x04\x04\x01\x02\x03\x12\x03\
+    \r\x04\x14\n\x0c\n\x05\x04\x01\x02\x03\x05\x12\x03\r\x04\n\n\x0c\n\x05\
+    \x04\x01\x02\x03\x01\x12\x03\r\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x03\x03\
+    \x12\x03\r\x12\x13\n\x0b\n\x04\x04\x01\x02\x04\x12\x03\x0e\x04%\n\x0c\n\
+    \x05\x04\x01\x02\x04\x06\x12\x03\x0e\x04\x16\n\x0c\n\x05\x04\x01\x02\x04\
+    \x01\x12\x03\x0e\x17\x20\n\x0c\n\x05\x04\x01\x02\x04\x03\x12\x03\x0e#$\n\
+    \n\n\x02\x05\0\x12\x04\x10\0\x12\x01\n\n\n\x03\x05\0\x01\x12\x03\x10\x05\
+    \x17\n\x0b\n\x04\x05\0\x02\0\x12\x03\x11\x04\r\n\x0c\n\x05\x05\0\x02\0\
+    \x01\x12\x03\x11\x04\x08\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x11\x0b\x0c\
+    b\x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

+ 43 - 43
rust-lib/flowy-workspace/src/protobuf/model/workspace_create.rs

@@ -225,7 +225,7 @@ impl ::protobuf::reflect::ProtobufValue for CreateWorkspaceRequest {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct WorkspaceDetail {
+pub struct Workspace {
     // message fields
     pub id: ::std::string::String,
     pub name: ::std::string::String,
@@ -235,14 +235,14 @@ pub struct WorkspaceDetail {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a WorkspaceDetail {
-    fn default() -> &'a WorkspaceDetail {
-        <WorkspaceDetail as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a Workspace {
+    fn default() -> &'a Workspace {
+        <Workspace as ::protobuf::Message>::default_instance()
     }
 }
 
-impl WorkspaceDetail {
-    pub fn new() -> WorkspaceDetail {
+impl Workspace {
+    pub fn new() -> Workspace {
         ::std::default::Default::default()
     }
 
@@ -325,7 +325,7 @@ impl WorkspaceDetail {
     }
 }
 
-impl ::protobuf::Message for WorkspaceDetail {
+impl ::protobuf::Message for Workspace {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -409,8 +409,8 @@ impl ::protobuf::Message for WorkspaceDetail {
         Self::descriptor_static()
     }
 
-    fn new() -> WorkspaceDetail {
-        WorkspaceDetail::new()
+    fn new() -> Workspace {
+        Workspace::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -419,34 +419,34 @@ impl ::protobuf::Message for WorkspaceDetail {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "id",
-                |m: &WorkspaceDetail| { &m.id },
-                |m: &mut WorkspaceDetail| { &mut m.id },
+                |m: &Workspace| { &m.id },
+                |m: &mut Workspace| { &mut m.id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "name",
-                |m: &WorkspaceDetail| { &m.name },
-                |m: &mut WorkspaceDetail| { &mut m.name },
+                |m: &Workspace| { &m.name },
+                |m: &mut Workspace| { &mut m.name },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "desc",
-                |m: &WorkspaceDetail| { &m.desc },
-                |m: &mut WorkspaceDetail| { &mut m.desc },
+                |m: &Workspace| { &m.desc },
+                |m: &mut Workspace| { &mut m.desc },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<WorkspaceDetail>(
-                "WorkspaceDetail",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<Workspace>(
+                "Workspace",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static WorkspaceDetail {
-        static instance: ::protobuf::rt::LazyV2<WorkspaceDetail> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(WorkspaceDetail::new)
+    fn default_instance() -> &'static Workspace {
+        static instance: ::protobuf::rt::LazyV2<Workspace> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(Workspace::new)
     }
 }
 
-impl ::protobuf::Clear for WorkspaceDetail {
+impl ::protobuf::Clear for Workspace {
     fn clear(&mut self) {
         self.id.clear();
         self.name.clear();
@@ -455,13 +455,13 @@ impl ::protobuf::Clear for WorkspaceDetail {
     }
 }
 
-impl ::std::fmt::Debug for WorkspaceDetail {
+impl ::std::fmt::Debug for Workspace {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for WorkspaceDetail {
+impl ::protobuf::reflect::ProtobufValue for Workspace {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -470,26 +470,26 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceDetail {
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x16workspace_create.proto\"@\n\x16CreateWorkspaceRequest\x12\x12\n\
     \x04name\x18\x01\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x02\x20\x01(\
-    \tR\x04desc\"I\n\x0fWorkspaceDetail\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
-    \x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\
-    \x18\x03\x20\x01(\tR\x04descJ\xd5\x02\n\x06\x12\x04\0\0\n\x01\n\x08\n\
-    \x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\
-    \x04\0\x01\x12\x03\x02\x08\x1e\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\
-    \x14\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\
-    \0\x01\x12\x03\x03\x0b\x0f\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x12\
-    \x13\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x04\0\x02\
-    \x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\
-    \x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x12\x13\n\n\n\x02\x04\x01\
-    \x12\x04\x06\0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x17\n\x0b\n\
-    \x04\x04\x01\x02\0\x12\x03\x07\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\
-    \x03\x07\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\
-    \x05\x04\x01\x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\
-    \x12\x03\x08\x04\x14\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\n\n\
-    \x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x08\x0b\x0f\n\x0c\n\x05\x04\x01\
-    \x02\x01\x03\x12\x03\x08\x12\x13\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\t\
-    \x04\x14\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\
-    \x01\x02\x02\x01\x12\x03\t\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\
-    \x03\t\x12\x13b\x06proto3\
+    \tR\x04desc\"C\n\tWorkspace\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\
+    \x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\
+    \x20\x01(\tR\x04descJ\xd5\x02\n\x06\x12\x04\0\0\n\x01\n\x08\n\x01\x0c\
+    \x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\
+    \x01\x12\x03\x02\x08\x1e\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x14\n\
+    \x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\
+    \x12\x03\x03\x0b\x0f\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x12\x13\n\
+    \x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x04\0\x02\x01\
+    \x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\x0f\
+    \n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x12\x13\n\n\n\x02\x04\x01\x12\
+    \x04\x06\0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x11\n\x0b\n\x04\
+    \x04\x01\x02\0\x12\x03\x07\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\
+    \x07\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\x05\
+    \x04\x01\x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\
+    \x03\x08\x04\x14\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\n\n\x0c\
+    \n\x05\x04\x01\x02\x01\x01\x12\x03\x08\x0b\x0f\n\x0c\n\x05\x04\x01\x02\
+    \x01\x03\x12\x03\x08\x12\x13\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\t\x04\
+    \x14\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\x01\
+    \x02\x02\x01\x12\x03\t\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\t\
+    \x12\x13b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 27 - 27
rust-lib/flowy-workspace/src/protobuf/model/workspace_user_detail.rs

@@ -228,7 +228,7 @@ impl ::protobuf::reflect::ProtobufValue for UserWorkspace {
 pub struct UserWorkspaceDetail {
     // message fields
     pub owner: ::std::string::String,
-    pub workspace: ::protobuf::SingularPtrField<super::workspace_create::WorkspaceDetail>,
+    pub workspace: ::protobuf::SingularPtrField<super::workspace_create::Workspace>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -271,11 +271,11 @@ impl UserWorkspaceDetail {
         ::std::mem::replace(&mut self.owner, ::std::string::String::new())
     }
 
-    // .WorkspaceDetail workspace = 2;
+    // .Workspace workspace = 2;
 
 
-    pub fn get_workspace(&self) -> &super::workspace_create::WorkspaceDetail {
-        self.workspace.as_ref().unwrap_or_else(|| <super::workspace_create::WorkspaceDetail as ::protobuf::Message>::default_instance())
+    pub fn get_workspace(&self) -> &super::workspace_create::Workspace {
+        self.workspace.as_ref().unwrap_or_else(|| <super::workspace_create::Workspace as ::protobuf::Message>::default_instance())
     }
     pub fn clear_workspace(&mut self) {
         self.workspace.clear();
@@ -286,13 +286,13 @@ impl UserWorkspaceDetail {
     }
 
     // Param is passed by value, moved
-    pub fn set_workspace(&mut self, v: super::workspace_create::WorkspaceDetail) {
+    pub fn set_workspace(&mut self, v: super::workspace_create::Workspace) {
         self.workspace = ::protobuf::SingularPtrField::some(v);
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_workspace(&mut self) -> &mut super::workspace_create::WorkspaceDetail {
+    pub fn mut_workspace(&mut self) -> &mut super::workspace_create::Workspace {
         if self.workspace.is_none() {
             self.workspace.set_default();
         }
@@ -300,8 +300,8 @@ impl UserWorkspaceDetail {
     }
 
     // Take field
-    pub fn take_workspace(&mut self) -> super::workspace_create::WorkspaceDetail {
-        self.workspace.take().unwrap_or_else(|| super::workspace_create::WorkspaceDetail::new())
+    pub fn take_workspace(&mut self) -> super::workspace_create::Workspace {
+        self.workspace.take().unwrap_or_else(|| super::workspace_create::Workspace::new())
     }
 }
 
@@ -401,7 +401,7 @@ impl ::protobuf::Message for UserWorkspaceDetail {
                 |m: &UserWorkspaceDetail| { &m.owner },
                 |m: &mut UserWorkspaceDetail| { &mut m.owner },
             ));
-            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::workspace_create::WorkspaceDetail>>(
+            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::workspace_create::Workspace>>(
                 "workspace",
                 |m: &UserWorkspaceDetail| { &m.workspace },
                 |m: &mut UserWorkspaceDetail| { &mut m.workspace },
@@ -443,24 +443,24 @@ impl ::protobuf::reflect::ProtobufValue for UserWorkspaceDetail {
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x1bworkspace_user_detail.proto\x1a\x16workspace_create.proto\"H\n\rUs\
     erWorkspace\x12\x14\n\x05owner\x18\x01\x20\x01(\tR\x05owner\x12!\n\x0cwo\
-    rkspace_id\x18\x02\x20\x01(\tR\x0bworkspaceId\"[\n\x13UserWorkspaceDetai\
-    l\x12\x14\n\x05owner\x18\x01\x20\x01(\tR\x05owner\x12.\n\tworkspace\x18\
-    \x02\x20\x01(\x0b2\x10.WorkspaceDetailR\tworkspaceJ\xa9\x02\n\x06\x12\
-    \x04\0\0\n\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\t\n\x02\x03\0\x12\x03\
-    \x01\0\x20\n\n\n\x02\x04\0\x12\x04\x03\0\x06\x01\n\n\n\x03\x04\0\x01\x12\
-    \x03\x03\x08\x15\n\x0b\n\x04\x04\0\x02\0\x12\x03\x04\x04\x15\n\x0c\n\x05\
-    \x04\0\x02\0\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\
-    \x04\x0b\x10\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x04\x13\x14\n\x0b\n\x04\
-    \x04\0\x02\x01\x12\x03\x05\x04\x1c\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\
-    \x05\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x05\x0b\x17\n\x0c\n\x05\
-    \x04\0\x02\x01\x03\x12\x03\x05\x1a\x1b\n\n\n\x02\x04\x01\x12\x04\x07\0\n\
-    \x01\n\n\n\x03\x04\x01\x01\x12\x03\x07\x08\x1b\n\x0b\n\x04\x04\x01\x02\0\
-    \x12\x03\x08\x04\x15\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x08\x04\n\n\
-    \x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x08\x0b\x10\n\x0c\n\x05\x04\x01\x02\
-    \0\x03\x12\x03\x08\x13\x14\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\t\x04\"\n\
-    \x0c\n\x05\x04\x01\x02\x01\x06\x12\x03\t\x04\x13\n\x0c\n\x05\x04\x01\x02\
-    \x01\x01\x12\x03\t\x14\x1d\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\t\x20\
-    !b\x06proto3\
+    rkspace_id\x18\x02\x20\x01(\tR\x0bworkspaceId\"U\n\x13UserWorkspaceDetai\
+    l\x12\x14\n\x05owner\x18\x01\x20\x01(\tR\x05owner\x12(\n\tworkspace\x18\
+    \x02\x20\x01(\x0b2\n.WorkspaceR\tworkspaceJ\xa9\x02\n\x06\x12\x04\0\0\n\
+    \x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\t\n\x02\x03\0\x12\x03\x01\0\x20\n\
+    \n\n\x02\x04\0\x12\x04\x03\0\x06\x01\n\n\n\x03\x04\0\x01\x12\x03\x03\x08\
+    \x15\n\x0b\n\x04\x04\0\x02\0\x12\x03\x04\x04\x15\n\x0c\n\x05\x04\0\x02\0\
+    \x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x04\x0b\x10\n\
+    \x0c\n\x05\x04\0\x02\0\x03\x12\x03\x04\x13\x14\n\x0b\n\x04\x04\0\x02\x01\
+    \x12\x03\x05\x04\x1c\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x05\x04\n\n\
+    \x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x05\x0b\x17\n\x0c\n\x05\x04\0\x02\
+    \x01\x03\x12\x03\x05\x1a\x1b\n\n\n\x02\x04\x01\x12\x04\x07\0\n\x01\n\n\n\
+    \x03\x04\x01\x01\x12\x03\x07\x08\x1b\n\x0b\n\x04\x04\x01\x02\0\x12\x03\
+    \x08\x04\x15\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x08\x04\n\n\x0c\n\x05\
+    \x04\x01\x02\0\x01\x12\x03\x08\x0b\x10\n\x0c\n\x05\x04\x01\x02\0\x03\x12\
+    \x03\x08\x13\x14\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\t\x04\x1c\n\x0c\n\
+    \x05\x04\x01\x02\x01\x06\x12\x03\t\x04\r\n\x0c\n\x05\x04\x01\x02\x01\x01\
+    \x12\x03\t\x0e\x17\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\t\x1a\x1bb\
+    \x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 1 - 1
rust-lib/flowy-workspace/src/protobuf/proto/app_create.proto

@@ -9,7 +9,7 @@ message CreateAppRequest {
 message ColorStyle {
     string theme_color = 1;
 }
-message AppDetail {
+message App {
     string id = 1;
     string workspace_id = 2;
     string name = 3;

+ 8 - 5
rust-lib/flowy-workspace/src/protobuf/proto/errors.proto

@@ -9,9 +9,12 @@ enum WorkspaceErrorCode {
     WorkspaceNameInvalid = 1;
     WorkspaceIdInvalid = 2;
     AppColorStyleInvalid = 3;
-    AppIdInvalid = 4;
-    DatabaseConnectionFail = 5;
-    WorkspaceDatabaseError = 6;
-    UserInternalError = 10;
-    UserNotLoginYet = 11;
+    AppIdInvalid = 10;
+    AppNameInvalid = 11;
+    ViewNameInvalid = 20;
+    ViewThumbnailName = 21;
+    DatabaseConnectionFail = 100;
+    WorkspaceDatabaseError = 101;
+    UserInternalError = 102;
+    UserNotLoginYet = 103;
 }

+ 1 - 0
rust-lib/flowy-workspace/src/protobuf/proto/event.proto

@@ -4,4 +4,5 @@ enum WorkspaceEvent {
     CreateWorkspace = 0;
     GetWorkspaceDetail = 1;
     CreateApp = 101;
+    CreateView = 201;
 }

+ 19 - 0
rust-lib/flowy-workspace/src/protobuf/proto/view_create.proto

@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+message CreateViewRequest {
+    string app_id = 1;
+    string name = 2;
+    string desc = 3;
+    oneof one_of_thumbnail { string thumbnail = 4; };
+    ViewTypeIdentifier view_type = 5;
+}
+message View {
+    string id = 1;
+    string app_id = 2;
+    string name = 3;
+    string desc = 4;
+    ViewTypeIdentifier view_type = 5;
+}
+enum ViewTypeIdentifier {
+    Docs = 0;
+}

+ 1 - 1
rust-lib/flowy-workspace/src/protobuf/proto/workspace_create.proto

@@ -4,7 +4,7 @@ message CreateWorkspaceRequest {
     string name = 1;
     string desc = 2;
 }
-message WorkspaceDetail {
+message Workspace {
     string id = 1;
     string name = 2;
     string desc = 3;

+ 1 - 1
rust-lib/flowy-workspace/src/protobuf/proto/workspace_user_detail.proto

@@ -7,5 +7,5 @@ message UserWorkspace {
 }
 message UserWorkspaceDetail {
     string owner = 1;
-    WorkspaceDetail workspace = 2;
+    Workspace workspace = 2;
 }

+ 6 - 6
rust-lib/flowy-workspace/src/services/app_controller.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::app::{AppDetail, CreateAppParams, *},
+    entities::app::{App, CreateAppParams, *},
     errors::*,
     module::WorkspaceUser,
     sql_tables::app::*,
@@ -14,15 +14,15 @@ pub struct AppController {
 impl AppController {
     pub fn new(user: Arc<dyn WorkspaceUser>) -> Self { Self { user } }
 
-    pub fn save_app(&self, params: CreateAppParams) -> Result<AppDetail, WorkspaceError> {
-        let app = AppTable::new(params);
+    pub fn save_app(&self, params: CreateAppParams) -> Result<App, WorkspaceError> {
+        let app_table = AppTable::new(params);
         let conn = self.user.db_connection()?;
 
-        let detail: AppDetail = app.clone().into();
+        let app: App = app_table.clone().into();
         let _ = diesel::insert_into(app_table::table)
-            .values(app)
+            .values(app_table)
             .execute(&*conn)?;
-        Ok(detail)
+        Ok(app)
     }
 
     pub fn update_app(&self, params: UpdateAppParams) -> Result<(), WorkspaceError> {

+ 2 - 0
rust-lib/flowy-workspace/src/services/mod.rs

@@ -1,7 +1,9 @@
 mod app_controller;
 mod database;
 mod helper;
+mod view_controller;
 mod workspace_controller;
 
 pub use app_controller::*;
+pub use view_controller::*;
 pub use workspace_controller::*;

+ 28 - 0
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -0,0 +1,28 @@
+use crate::{
+    entities::view::{CreateViewParams, View},
+    errors::WorkspaceError,
+    module::WorkspaceUser,
+    sql_tables::view::ViewTable,
+};
+use flowy_database::{prelude::*, schema::view_table};
+use std::sync::Arc;
+
+pub struct ViewController {
+    user: Arc<dyn WorkspaceUser>,
+}
+
+impl ViewController {
+    pub fn new(user: Arc<dyn WorkspaceUser>) -> Self { Self { user } }
+
+    pub async fn save_view(&self, params: CreateViewParams) -> Result<View, WorkspaceError> {
+        let view_table = ViewTable::new(params);
+        let conn = self.user.db_connection()?;
+        let view: View = view_table.clone().into();
+
+        let _ = diesel::insert_into(view_table::table)
+            .values(view_table)
+            .execute(&*conn)?;
+
+        Ok(view)
+    }
+}

+ 4 - 4
rust-lib/flowy-workspace/src/services/workspace_controller.rs

@@ -15,12 +15,12 @@ impl WorkspaceController {
     pub async fn save_workspace(
         &self,
         params: CreateWorkspaceParams,
-    ) -> Result<WorkspaceDetail, WorkspaceError> {
-        let workspace = WorkspaceTable::new(params);
-        let detail: WorkspaceDetail = workspace.clone().into();
+    ) -> Result<Workspace, WorkspaceError> {
+        let workspace_table = WorkspaceTable::new(params);
+        let detail: Workspace = workspace_table.clone().into();
 
         let _ = diesel::insert_into(workspace_table::table)
-            .values(workspace)
+            .values(workspace_table)
             .execute(&*(self.user.db_connection()?))?;
 
         let _ = self.user.set_cur_workspace_id(&detail.id).await?;

+ 5 - 16
rust-lib/flowy-workspace/src/sql_tables/app/app.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::app::{AppDetail, ColorStyle, CreateAppParams, UpdateAppParams},
+    entities::app::{App, ColorStyle, CreateAppParams, UpdateAppParams},
     impl_sql_binary_expression,
     sql_tables::workspace::WorkspaceTable,
 };
@@ -9,20 +9,9 @@ use flowy_infra::{timestamp, uuid};
 use serde::{Deserialize, Serialize, __private::TryFrom};
 use std::convert::TryInto;
 
-#[derive(
-    PartialEq,
-    Serialize,
-    Deserialize,
-    Clone,
-    Debug,
-    Queryable,
-    Identifiable,
-    Insertable,
-    Associations,
-)]
+#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
 #[belongs_to(WorkspaceTable, foreign_key = "workspace_id")]
 #[table_name = "app_table"]
-#[serde(tag = "type")]
 pub(crate) struct AppTable {
     pub id: String,
     pub workspace_id: String, // equal to #[belongs_to(Workspace, foreign_key = "workspace_id")].
@@ -105,9 +94,9 @@ impl AppTableChangeset {
     }
 }
 
-impl std::convert::Into<AppDetail> for AppTable {
-    fn into(self) -> AppDetail {
-        AppDetail {
+impl std::convert::Into<App> for AppTable {
+    fn into(self) -> App {
+        App {
             id: self.id,
             workspace_id: self.workspace_id,
             name: self.name,

+ 106 - 0
rust-lib/flowy-workspace/src/sql_tables/view/view.rs

@@ -0,0 +1,106 @@
+use crate::{
+    entities::view::{CreateViewParams, View, ViewTypeIdentifier},
+    impl_sql_integer_expression,
+    sql_tables::app::AppTable,
+};
+use diesel::sql_types::Integer;
+use flowy_database::schema::{view_table, view_table::dsl};
+use flowy_infra::{timestamp, uuid};
+
+#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
+#[belongs_to(AppTable, foreign_key = "app_id")]
+#[table_name = "view_table"]
+pub(crate) struct ViewTable {
+    pub id: String,
+    pub app_id: String,
+    pub name: String,
+    pub desc: String,
+    pub modified_time: i64,
+    pub create_time: i64,
+    pub thumbnail: String,
+    pub view_type: ViewType,
+    pub version: i64,
+}
+
+impl ViewTable {
+    pub fn new(params: CreateViewParams) -> Self {
+        let view_id = uuid();
+        let time = timestamp();
+        ViewTable {
+            id: view_id,
+            app_id: params.app_id,
+            name: params.name,
+            desc: params.desc,
+            modified_time: time,
+            create_time: time,
+            thumbnail: params.thumbnail,
+            view_type: params.view_type,
+            version: 0,
+        }
+    }
+}
+
+impl std::convert::Into<View> for ViewTable {
+    fn into(self) -> View {
+        let view_type = match self.view_type {
+            ViewType::Docs => ViewTypeIdentifier::Docs,
+        };
+
+        View {
+            id: self.id,
+            app_id: self.app_id,
+            name: self.name,
+            desc: self.desc,
+            view_type,
+        }
+    }
+}
+
+#[derive(AsChangeset, Identifiable, Clone, Default, Debug)]
+#[table_name = "view_table"]
+pub struct ViewTableChangeset {
+    pub id: String,
+    pub name: Option<String>,
+    pub desc: Option<String>,
+    pub modified_time: i64,
+}
+
+impl ViewTableChangeset {
+    pub fn new(id: &str) -> Self {
+        ViewTableChangeset {
+            id: id.to_string(),
+            name: None,
+            desc: None,
+            modified_time: timestamp(),
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, FromSqlRow, AsExpression)]
+#[repr(i32)]
+#[sql_type = "Integer"]
+pub enum ViewType {
+    Docs = 0,
+}
+
+impl std::default::Default for ViewType {
+    fn default() -> Self { ViewType::Docs }
+}
+
+impl std::convert::From<i32> for ViewType {
+    fn from(value: i32) -> Self {
+        match value {
+            0 => ViewType::Docs,
+            o => {
+                log::error!("Unsupported view type {}, fallback to ViewType::Docs", o);
+                ViewType::Docs
+            },
+        }
+    }
+}
+
+impl ViewType {
+    pub fn value(&self) -> i32 { *self as i32 }
+}
+
+impl_sql_integer_expression!(ViewType);

+ 5 - 7
rust-lib/flowy-workspace/src/sql_tables/workspace/workspace.rs

@@ -1,11 +1,9 @@
-use crate::entities::workspace::{CreateWorkspaceParams, UpdateWorkspaceParams, WorkspaceDetail};
+use crate::entities::workspace::{CreateWorkspaceParams, UpdateWorkspaceParams, Workspace};
 use flowy_database::schema::workspace_table;
 use flowy_infra::{timestamp, uuid};
-use serde::{Deserialize, Serialize};
 
-#[derive(PartialEq, Clone, Serialize, Deserialize, Debug, Queryable, Identifiable, Insertable)]
+#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable)]
 #[table_name = "workspace_table"]
-#[serde(tag = "type")]
 pub struct WorkspaceTable {
     pub id: String,
     pub name: String,
@@ -59,9 +57,9 @@ impl WorkspaceTableChangeset {
     }
 }
 
-impl std::convert::Into<WorkspaceDetail> for WorkspaceTable {
-    fn into(self) -> WorkspaceDetail {
-        WorkspaceDetail {
+impl std::convert::Into<Workspace> for WorkspaceTable {
+    fn into(self) -> Workspace {
+        Workspace {
             id: self.id,
             name: self.name,
             desc: self.desc,

+ 2 - 2
rust-lib/flowy-workspace/tests/event/app_test.rs

@@ -1,7 +1,7 @@
 use flowy_test::builder::WorkspaceTestBuilder;
 use flowy_workspace::{
     entities::{
-        app::{AppDetail, CreateAppRequest},
+        app::{App, CreateAppRequest},
         workspace::UserWorkspaceDetail,
     },
     event::WorkspaceEvent::{CreateApp, GetWorkspaceDetail},
@@ -25,7 +25,7 @@ fn app_create_success() {
         .event(CreateApp)
         .request(request)
         .sync_send()
-        .parse::<AppDetail>();
+        .parse::<App>();
 
     dbg!(&app_detail);
 }

+ 3 - 3
rust-lib/flowy-workspace/tests/event/workspace_test.rs

@@ -1,6 +1,6 @@
 use crate::helper::*;
 use flowy_workspace::{
-    entities::workspace::{CreateWorkspaceRequest, UserWorkspaceDetail, WorkspaceDetail},
+    entities::workspace::{CreateWorkspaceRequest, UserWorkspaceDetail, Workspace},
     event::WorkspaceEvent::*,
     prelude::*,
 };
@@ -16,7 +16,7 @@ fn workspace_create_success() {
         .event(CreateWorkspace)
         .request(request)
         .sync_send()
-        .parse::<WorkspaceDetail>();
+        .parse::<Workspace>();
     dbg!(&response);
 }
 
@@ -41,7 +41,7 @@ fn workspace_create_and_then_get_detail_success() {
         .event(CreateWorkspace)
         .request(request)
         .sync_send()
-        .parse::<WorkspaceDetail>();
+        .parse::<Workspace>();
 
     let user_workspace = WorkspaceTestBuilder::new()
         .event(GetWorkspaceDetail)