Browse Source

config view's update and query handle

appflowy 3 years ago
parent
commit
bfdc0b6ee9
38 changed files with 1317 additions and 62 deletions
  1. 5 0
      app_flowy/lib/workspace/domain/i_view.dart
  2. 30 0
      app_flowy/lib/workspace/infrastructure/repos/view_repo.dart
  3. 5 5
      app_flowy/lib/workspace/presentation/doc/doc_widget.dart
  4. 6 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbenum.dart
  5. 4 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbjson.dart
  6. 4 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart
  7. 3 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart
  8. 2 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/protobuf.dart
  9. 58 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pb.dart
  10. 7 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbenum.dart
  11. 20 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbjson.dart
  12. 9 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbserver.dart
  13. 139 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pb.dart
  14. 7 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pbenum.dart
  15. 28 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pbjson.dart
  16. 9 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pbserver.dart
  17. 2 0
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  18. 4 0
      rust-lib/flowy-workspace/src/entities/view/mod.rs
  19. 2 0
      rust-lib/flowy-workspace/src/entities/view/parser/mod.rs
  20. 12 0
      rust-lib/flowy-workspace/src/entities/view/parser/view_id.rs
  21. 1 1
      rust-lib/flowy-workspace/src/entities/view/view_create.rs
  22. 32 0
      rust-lib/flowy-workspace/src/entities/view/view_query.rs
  23. 88 0
      rust-lib/flowy-workspace/src/entities/view/view_update.rs
  24. 11 5
      rust-lib/flowy-workspace/src/errors.rs
  25. 8 0
      rust-lib/flowy-workspace/src/event.rs
  26. 29 1
      rust-lib/flowy-workspace/src/handlers/view_handler.rs
  27. 31 20
      rust-lib/flowy-workspace/src/protobuf/model/errors.rs
  28. 25 14
      rust-lib/flowy-workspace/src/protobuf/model/event.rs
  29. 6 0
      rust-lib/flowy-workspace/src/protobuf/model/mod.rs
  30. 205 0
      rust-lib/flowy-workspace/src/protobuf/model/view_query.rs
  31. 463 0
      rust-lib/flowy-workspace/src/protobuf/model/view_update.rs
  32. 3 1
      rust-lib/flowy-workspace/src/protobuf/proto/errors.proto
  33. 2 0
      rust-lib/flowy-workspace/src/protobuf/proto/event.proto
  34. 5 0
      rust-lib/flowy-workspace/src/protobuf/proto/view_query.proto
  35. 8 0
      rust-lib/flowy-workspace/src/protobuf/proto/view_update.proto
  36. 18 3
      rust-lib/flowy-workspace/src/services/view_controller.rs
  37. 19 2
      rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs
  38. 7 5
      rust-lib/flowy-workspace/src/sql_tables/view/view_table.rs

+ 5 - 0
app_flowy/lib/workspace/domain/i_view.dart

@@ -0,0 +1,5 @@
+abstract class IViewWatch {
+  void startWatching({ViewUpdatedCallback? updatedCallback});
+
+  Future<void> stopWatching();
+}

+ 30 - 0
app_flowy/lib/workspace/infrastructure/repos/view_repo.dart

@@ -0,0 +1,30 @@
+import 'package:app_flowy/workspace/domain/i_view.dart';
+import 'package:flowy_sdk/protobuf/flowy-observable/subject.pb.dart';
+import 'package:dartz/dartz.dart';
+import 'dart:async';
+
+class ViewWatchRepository {
+  StreamSubscription<ObservableSubject>? _subscription;
+  ViewUpdatedCallback? _updatedCallback;
+  String viewId;
+  ViewWatchRepository({
+    required this.viewId,
+  });
+
+  void startWatching({
+    ViewUpdatedCallback? updatedCallback,
+  }) {
+    _addViewCallback = addViewCallback;
+    _updatedCallback = updatedCallback;
+    _subscription = RustStreamReceiver.listen((observable) {
+      if (observable.subjectId != appId) {
+        return;
+      }
+
+      final ty = WorkspaceObservable.valueOf(observable.ty);
+      if (ty != null) {
+        _handleObservableType(ty);
+      }
+    });
+  }
+}

+ 5 - 5
app_flowy/lib/workspace/presentation/doc/doc_widget.dart

@@ -2,9 +2,9 @@ import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flutter/material.dart';
 
-class DocPageContext extends HomeStackView {
+class DocPageStackView extends HomeStackView {
   final View view;
-  DocPageContext(this.view)
+  DocPageStackView(this.view)
       : super(
           type: view.viewType,
           title: view.name,
@@ -15,7 +15,7 @@ class DocPageContext extends HomeStackView {
 }
 
 class DocPage extends HomeStackWidget {
-  const DocPage({Key? key, required DocPageContext stackView})
+  const DocPage({Key? key, required DocPageStackView stackView})
       : super(key: key, stackView: stackView);
 
   @override
@@ -25,9 +25,9 @@ class DocPage extends HomeStackWidget {
 class _DocPageState extends State<DocPage> {
   @override
   Widget build(BuildContext context) {
-    assert(widget.stackView is DocPageContext);
+    assert(widget.stackView is DocPageStackView);
 
-    final context = widget.stackView as DocPageContext;
+    final context = widget.stackView as DocPageStackView;
     final filename = _extractFilename(context.view.id);
     return Container();
   }

+ 6 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbenum.dart

@@ -17,7 +17,9 @@ class WorkspaceErrorCode extends $pb.ProtobufEnum {
   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 ViewThumbnailInvalid = WorkspaceErrorCode._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewThumbnailInvalid');
+  static const WorkspaceErrorCode ViewIdInvalid = WorkspaceErrorCode._(22, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewIdInvalid');
+  static const WorkspaceErrorCode ViewDescInvalid = WorkspaceErrorCode._(23, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewDescInvalid');
   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');
@@ -31,7 +33,9 @@ class WorkspaceErrorCode extends $pb.ProtobufEnum {
     AppIdInvalid,
     AppNameInvalid,
     ViewNameInvalid,
-    ViewThumbnailName,
+    ViewThumbnailInvalid,
+    ViewIdInvalid,
+    ViewDescInvalid,
     DatabaseConnectionFail,
     WorkspaceDatabaseError,
     UserInternalError,

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

@@ -19,7 +19,9 @@ const WorkspaceErrorCode$json = const {
     const {'1': 'AppIdInvalid', '2': 10},
     const {'1': 'AppNameInvalid', '2': 11},
     const {'1': 'ViewNameInvalid', '2': 20},
-    const {'1': 'ViewThumbnailName', '2': 21},
+    const {'1': 'ViewThumbnailInvalid', '2': 21},
+    const {'1': 'ViewIdInvalid', '2': 22},
+    const {'1': 'ViewDescInvalid', '2': 23},
     const {'1': 'DatabaseConnectionFail', '2': 100},
     const {'1': 'WorkspaceDatabaseError', '2': 101},
     const {'1': 'UserInternalError', '2': 102},
@@ -28,7 +30,7 @@ const WorkspaceErrorCode$json = const {
 };
 
 /// Descriptor for `WorkspaceErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceErrorCodeDescriptor = $convert.base64Decode('ChJXb3Jrc3BhY2VFcnJvckNvZGUSCwoHVW5rbm93bhAAEhgKFFdvcmtzcGFjZU5hbWVJbnZhbGlkEAESFgoSV29ya3NwYWNlSWRJbnZhbGlkEAISGAoUQXBwQ29sb3JTdHlsZUludmFsaWQQAxIQCgxBcHBJZEludmFsaWQQChISCg5BcHBOYW1lSW52YWxpZBALEhMKD1ZpZXdOYW1lSW52YWxpZBAUEhUKEVZpZXdUaHVtYm5haWxOYW1lEBUSGgoWRGF0YWJhc2VDb25uZWN0aW9uRmFpbBBkEhoKFldvcmtzcGFjZURhdGFiYXNlRXJyb3IQZRIVChFVc2VySW50ZXJuYWxFcnJvchBmEhMKD1VzZXJOb3RMb2dpbllldBBn');
+final $typed_data.Uint8List workspaceErrorCodeDescriptor = $convert.base64Decode('ChJXb3Jrc3BhY2VFcnJvckNvZGUSCwoHVW5rbm93bhAAEhgKFFdvcmtzcGFjZU5hbWVJbnZhbGlkEAESFgoSV29ya3NwYWNlSWRJbnZhbGlkEAISGAoUQXBwQ29sb3JTdHlsZUludmFsaWQQAxIQCgxBcHBJZEludmFsaWQQChISCg5BcHBOYW1lSW52YWxpZBALEhMKD1ZpZXdOYW1lSW52YWxpZBAUEhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEBUSEQoNVmlld0lkSW52YWxpZBAWEhMKD1ZpZXdEZXNjSW52YWxpZBAXEhoKFkRhdGFiYXNlQ29ubmVjdGlvbkZhaWwQZBIaChZXb3Jrc3BhY2VEYXRhYmFzZUVycm9yEGUSFQoRVXNlckludGVybmFsRXJyb3IQZhITCg9Vc2VyTm90TG9naW5ZZXQQZw==');
 @$core.Deprecated('Use workspaceErrorDescriptor instead')
 const WorkspaceError$json = const {
   '1': 'WorkspaceError',

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

@@ -16,6 +16,8 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
   static const WorkspaceEvent CreateApp = WorkspaceEvent._(101, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateApp');
   static const WorkspaceEvent GetApp = WorkspaceEvent._(102, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetApp');
   static const WorkspaceEvent CreateView = WorkspaceEvent._(201, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateView');
+  static const WorkspaceEvent ReadView = WorkspaceEvent._(202, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadView');
+  static const WorkspaceEvent UpdateView = WorkspaceEvent._(203, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateView');
 
   static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
     CreateWorkspace,
@@ -24,6 +26,8 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
     CreateApp,
     GetApp,
     CreateView,
+    ReadView,
+    UpdateView,
   ];
 
   static final $core.Map<$core.int, WorkspaceEvent> _byValue = $pb.ProtobufEnum.initByValue(values);

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

@@ -18,8 +18,10 @@ const WorkspaceEvent$json = const {
     const {'1': 'CreateApp', '2': 101},
     const {'1': 'GetApp', '2': 102},
     const {'1': 'CreateView', '2': 201},
+    const {'1': 'ReadView', '2': 202},
+    const {'1': 'UpdateView', '2': 203},
   ],
 };
 
 /// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABITCg9HZXRDdXJXb3Jrc3BhY2UQARIQCgxHZXRXb3Jrc3BhY2UQAhINCglDcmVhdGVBcHAQZRIKCgZHZXRBcHAQZhIPCgpDcmVhdGVWaWV3EMkB');
+final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABITCg9HZXRDdXJXb3Jrc3BhY2UQARIQCgxHZXRXb3Jrc3BhY2UQAhINCglDcmVhdGVBcHAQZRIKCgZHZXRBcHAQZhIPCgpDcmVhdGVWaWV3EMkBEg0KCFJlYWRWaWV3EMoBEg8KClVwZGF0ZVZpZXcQywE=');

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

@@ -1,4 +1,5 @@
 // Auto-generated, do not edit 
+export './view_update.pb.dart';
 export './app_query.pb.dart';
 export './observable.pb.dart';
 export './errors.pb.dart';
@@ -10,3 +11,4 @@ export './view_create.pb.dart';
 export './workspace_user_detail.pb.dart';
 export './workspace_create.pb.dart';
 export './app_update.pb.dart';
+export './view_query.pb.dart';

+ 58 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pb.dart

@@ -0,0 +1,58 @@
+///
+//  Generated code. Do not modify.
+//  source: view_query.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class QueryViewRequest extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryViewRequest', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
+    ..hasRequiredFields = false
+  ;
+
+  QueryViewRequest._() : super();
+  factory QueryViewRequest({
+    $core.String? viewId,
+  }) {
+    final _result = create();
+    if (viewId != null) {
+      _result.viewId = viewId;
+    }
+    return _result;
+  }
+  factory QueryViewRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory QueryViewRequest.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')
+  QueryViewRequest clone() => QueryViewRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  QueryViewRequest copyWith(void Function(QueryViewRequest) updates) => super.copyWith((message) => updates(message as QueryViewRequest)) as QueryViewRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static QueryViewRequest create() => QueryViewRequest._();
+  QueryViewRequest createEmptyInstance() => create();
+  static $pb.PbList<QueryViewRequest> createRepeated() => $pb.PbList<QueryViewRequest>();
+  @$core.pragma('dart2js:noInline')
+  static QueryViewRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<QueryViewRequest>(create);
+  static QueryViewRequest? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get viewId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set viewId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasViewId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearViewId() => clearField(1);
+}
+

+ 7 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: view_query.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
+

+ 20 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_query.pbjson.dart

@@ -0,0 +1,20 @@
+///
+//  Generated code. Do not modify.
+//  source: view_query.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 queryViewRequestDescriptor instead')
+const QueryViewRequest$json = const {
+  '1': 'QueryViewRequest',
+  '2': const [
+    const {'1': 'view_id', '3': 1, '4': 1, '5': 9, '10': 'viewId'},
+  ],
+};
+
+/// Descriptor for `QueryViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List queryViewRequestDescriptor = $convert.base64Decode('ChBRdWVyeVZpZXdSZXF1ZXN0EhcKB3ZpZXdfaWQYASABKAlSBnZpZXdJZA==');

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

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

+ 139 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pb.dart

@@ -0,0 +1,139 @@
+///
+//  Generated code. Do not modify.
+//  source: view_update.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;
+
+enum UpdateViewRequest_OneOfName {
+  name, 
+  notSet
+}
+
+enum UpdateViewRequest_OneOfDesc {
+  desc, 
+  notSet
+}
+
+enum UpdateViewRequest_OneOfThumbnail {
+  thumbnail, 
+  notSet
+}
+
+class UpdateViewRequest extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, UpdateViewRequest_OneOfName> _UpdateViewRequest_OneOfNameByTag = {
+    2 : UpdateViewRequest_OneOfName.name,
+    0 : UpdateViewRequest_OneOfName.notSet
+  };
+  static const $core.Map<$core.int, UpdateViewRequest_OneOfDesc> _UpdateViewRequest_OneOfDescByTag = {
+    3 : UpdateViewRequest_OneOfDesc.desc,
+    0 : UpdateViewRequest_OneOfDesc.notSet
+  };
+  static const $core.Map<$core.int, UpdateViewRequest_OneOfThumbnail> _UpdateViewRequest_OneOfThumbnailByTag = {
+    4 : UpdateViewRequest_OneOfThumbnail.thumbnail,
+    0 : UpdateViewRequest_OneOfThumbnail.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateViewRequest', createEmptyInstance: create)
+    ..oo(0, [2])
+    ..oo(1, [3])
+    ..oo(2, [4])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
+    ..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')
+    ..hasRequiredFields = false
+  ;
+
+  UpdateViewRequest._() : super();
+  factory UpdateViewRequest({
+    $core.String? viewId,
+    $core.String? name,
+    $core.String? desc,
+    $core.String? thumbnail,
+  }) {
+    final _result = create();
+    if (viewId != null) {
+      _result.viewId = viewId;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (desc != null) {
+      _result.desc = desc;
+    }
+    if (thumbnail != null) {
+      _result.thumbnail = thumbnail;
+    }
+    return _result;
+  }
+  factory UpdateViewRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory UpdateViewRequest.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')
+  UpdateViewRequest clone() => UpdateViewRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  UpdateViewRequest copyWith(void Function(UpdateViewRequest) updates) => super.copyWith((message) => updates(message as UpdateViewRequest)) as UpdateViewRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static UpdateViewRequest create() => UpdateViewRequest._();
+  UpdateViewRequest createEmptyInstance() => create();
+  static $pb.PbList<UpdateViewRequest> createRepeated() => $pb.PbList<UpdateViewRequest>();
+  @$core.pragma('dart2js:noInline')
+  static UpdateViewRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UpdateViewRequest>(create);
+  static UpdateViewRequest? _defaultInstance;
+
+  UpdateViewRequest_OneOfName whichOneOfName() => _UpdateViewRequest_OneOfNameByTag[$_whichOneof(0)]!;
+  void clearOneOfName() => clearField($_whichOneof(0));
+
+  UpdateViewRequest_OneOfDesc whichOneOfDesc() => _UpdateViewRequest_OneOfDescByTag[$_whichOneof(1)]!;
+  void clearOneOfDesc() => clearField($_whichOneof(1));
+
+  UpdateViewRequest_OneOfThumbnail whichOneOfThumbnail() => _UpdateViewRequest_OneOfThumbnailByTag[$_whichOneof(2)]!;
+  void clearOneOfThumbnail() => clearField($_whichOneof(2));
+
+  @$pb.TagNumber(1)
+  $core.String get viewId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set viewId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasViewId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearViewId() => 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);
+}
+

+ 7 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: view_update.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
+

+ 28 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/view_update.pbjson.dart

@@ -0,0 +1,28 @@
+///
+//  Generated code. Do not modify.
+//  source: view_update.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 updateViewRequestDescriptor instead')
+const UpdateViewRequest$json = const {
+  '1': 'UpdateViewRequest',
+  '2': const [
+    const {'1': 'view_id', '3': 1, '4': 1, '5': 9, '10': 'viewId'},
+    const {'1': 'name', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'name'},
+    const {'1': 'desc', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'desc'},
+    const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '9': 2, '10': 'thumbnail'},
+  ],
+  '8': const [
+    const {'1': 'one_of_name'},
+    const {'1': 'one_of_desc'},
+    const {'1': 'one_of_thumbnail'},
+  ],
+};
+
+/// Descriptor for `UpdateViewRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List updateViewRequestDescriptor = $convert.base64Decode('ChFVcGRhdGVWaWV3UmVxdWVzdBIXCgd2aWV3X2lkGAEgASgJUgZ2aWV3SWQSFAoEbmFtZRgCIAEoCUgAUgRuYW1lEhQKBGRlc2MYAyABKAlIAVIEZGVzYxIeCgl0aHVtYm5haWwYBCABKAlIAlIJdGh1bWJuYWlsQg0KC29uZV9vZl9uYW1lQg0KC29uZV9vZl9kZXNjQhIKEG9uZV9vZl90aHVtYm5haWw=');

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

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

+ 2 - 0
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -34,6 +34,8 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "Workspace"
         | "QueryWorkspaceRequest"
         | "CurrentWorkspace"
+        | "UpdateViewRequest"
+        | "QueryViewRequest"
         | "CreateViewRequest"
         | "View"
         | "RepeatedView"

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

@@ -1,4 +1,8 @@
 mod parser;
 mod view_create;
+mod view_query;
+mod view_update;
 
 pub use view_create::*;
+pub use view_query::*;
+pub use view_update::*;

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

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

+ 12 - 0
rust-lib/flowy-workspace/src/entities/view/parser/view_id.rs

@@ -0,0 +1,12 @@
+#[derive(Debug)]
+pub struct ViewId(pub String);
+
+impl ViewId {
+    pub fn parse(s: String) -> Result<ViewId, String> {
+        if s.trim().is_empty() {
+            return Err(format!("View id can not be empty or whitespace"));
+        }
+
+        Ok(Self(s))
+    }
+}

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

@@ -68,7 +68,7 @@ impl TryInto<CreateViewParams> for CreateViewRequest {
             Some(thumbnail) => {
                 ViewThumbnail::parse(thumbnail)
                     .map_err(|e| {
-                        ErrorBuilder::new(WorkspaceErrorCode::ViewThumbnailName)
+                        ErrorBuilder::new(WorkspaceErrorCode::ViewThumbnailInvalid)
                             .msg(e)
                             .build()
                     })?

+ 32 - 0
rust-lib/flowy-workspace/src/entities/view/view_query.rs

@@ -0,0 +1,32 @@
+use crate::{
+    entities::view::parser::ViewId,
+    errors::{ErrorBuilder, WorkspaceError, WorkspaceErrorCode},
+};
+use flowy_derive::ProtoBuf;
+use std::convert::TryInto;
+
+#[derive(Default, ProtoBuf)]
+pub struct QueryViewRequest {
+    #[pb(index = 1)]
+    pub view_id: String,
+}
+
+pub struct QueryViewParams {
+    pub view_id: String,
+}
+
+impl TryInto<QueryViewParams> for QueryViewRequest {
+    type Error = WorkspaceError;
+
+    fn try_into(self) -> Result<QueryViewParams, Self::Error> {
+        let view_id = ViewId::parse(self.view_id)
+            .map_err(|e| {
+                ErrorBuilder::new(WorkspaceErrorCode::ViewIdInvalid)
+                    .msg(e)
+                    .build()
+            })?
+            .0;
+
+        Ok(QueryViewParams { view_id })
+    }
+}

+ 88 - 0
rust-lib/flowy-workspace/src/entities/view/view_update.rs

@@ -0,0 +1,88 @@
+use crate::{
+    entities::view::parser::{ViewId, *},
+    errors::{ErrorBuilder, WorkspaceError, WorkspaceErrorCode},
+};
+use flowy_derive::ProtoBuf;
+use std::convert::TryInto;
+
+#[derive(Default, ProtoBuf)]
+pub struct UpdateViewRequest {
+    #[pb(index = 1)]
+    pub view_id: String,
+
+    #[pb(index = 2, one_of)]
+    pub name: Option<String>,
+
+    #[pb(index = 3, one_of)]
+    pub desc: Option<String>,
+
+    #[pb(index = 4, one_of)]
+    pub thumbnail: Option<String>,
+}
+
+pub struct UpdateViewParams {
+    pub view_id: String,
+    pub name: Option<String>,
+    pub desc: Option<String>,
+    pub thumbnail: Option<String>,
+}
+
+impl TryInto<UpdateViewParams> for UpdateViewRequest {
+    type Error = WorkspaceError;
+
+    fn try_into(self) -> Result<UpdateViewParams, Self::Error> {
+        let view_id = ViewId::parse(self.view_id)
+            .map_err(|e| {
+                ErrorBuilder::new(WorkspaceErrorCode::ViewIdInvalid)
+                    .msg(e)
+                    .build()
+            })?
+            .0;
+
+        let name = match self.name {
+            None => None,
+            Some(name) => Some(
+                ViewName::parse(name)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::ViewNameInvalid)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0,
+            ),
+        };
+
+        let desc = match self.desc {
+            None => None,
+            Some(desc) => Some(
+                ViewDesc::parse(desc)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::ViewDescInvalid)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0,
+            ),
+        };
+
+        let thumbnail = match self.thumbnail {
+            None => None,
+            Some(thumbnail) => Some(
+                ViewThumbnail::parse(thumbnail)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::ViewThumbnailInvalid)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0,
+            ),
+        };
+
+        Ok(UpdateViewParams {
+            view_id,
+            name,
+            desc,
+            thumbnail,
+        })
+    }
+}

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

@@ -32,20 +32,26 @@ pub enum WorkspaceErrorCode {
     #[display(fmt = "Workspace id is invalid")]
     WorkspaceIdInvalid   = 2,
 
-    #[display(fmt = "App color style format error")]
+    #[display(fmt = "Color style of the App is invalid")]
     AppColorStyleInvalid = 3,
 
-    #[display(fmt = "App id is invalid")]
+    #[display(fmt = "Id of the App  is invalid")]
     AppIdInvalid         = 10,
 
-    #[display(fmt = "App name is invalid")]
+    #[display(fmt = "Name of the App  is invalid")]
     AppNameInvalid       = 11,
 
-    #[display(fmt = "View name is invalid")]
+    #[display(fmt = "Name of the View  is invalid")]
     ViewNameInvalid      = 20,
 
     #[display(fmt = "Thumbnail of the view is invalid")]
-    ViewThumbnailName    = 21,
+    ViewThumbnailInvalid = 21,
+
+    #[display(fmt = "Id of the View is invalid")]
+    ViewIdInvalid        = 22,
+
+    #[display(fmt = "Description of the View is invalid")]
+    ViewDescInvalid      = 23,
 
     #[display(fmt = "Get database connection failed")]
     DatabaseConnectionFail = 100,

+ 8 - 0
rust-lib/flowy-workspace/src/event.rs

@@ -27,4 +27,12 @@ pub enum WorkspaceEvent {
     #[display(fmt = "CreateView")]
     #[event(input = "CreateViewRequest", output = "View")]
     CreateView      = 201,
+
+    #[display(fmt = "ReadView")]
+    #[event(input = "QueryViewRequest", output = "View")]
+    ReadView        = 202,
+
+    #[display(fmt = "UpdateView")]
+    #[event(input = "UpdateViewRequest", output = "View")]
+    UpdateView      = 203,
 }

+ 29 - 1
rust-lib/flowy-workspace/src/handlers/view_handler.rs

@@ -1,5 +1,13 @@
 use crate::{
-    entities::view::{CreateViewParams, CreateViewRequest, View},
+    entities::view::{
+        CreateViewParams,
+        CreateViewRequest,
+        QueryViewParams,
+        QueryViewRequest,
+        UpdateViewParams,
+        UpdateViewRequest,
+        View,
+    },
     errors::WorkspaceError,
     services::ViewController,
 };
@@ -14,3 +22,23 @@ pub async fn create_view(
     let view = controller.create_view(params).await?;
     response_ok(view)
 }
+
+pub async fn read_view(
+    data: Data<QueryViewRequest>,
+    controller: Unit<Arc<ViewController>>,
+) -> ResponseResult<View, WorkspaceError> {
+    let params: QueryViewParams = data.into_inner().try_into()?;
+    let view = controller.read_view(&params.view_id).await?;
+
+    response_ok(view)
+}
+
+pub async fn update_view(
+    data: Data<UpdateViewRequest>,
+    controller: Unit<Arc<ViewController>>,
+) -> Result<(), WorkspaceError> {
+    let params: UpdateViewParams = data.into_inner().try_into()?;
+    let _ = controller.update_view(params).await?;
+
+    Ok(())
+}

+ 31 - 20
rust-lib/flowy-workspace/src/protobuf/model/errors.rs

@@ -222,7 +222,9 @@ pub enum WorkspaceErrorCode {
     AppIdInvalid = 10,
     AppNameInvalid = 11,
     ViewNameInvalid = 20,
-    ViewThumbnailName = 21,
+    ViewThumbnailInvalid = 21,
+    ViewIdInvalid = 22,
+    ViewDescInvalid = 23,
     DatabaseConnectionFail = 100,
     WorkspaceDatabaseError = 101,
     UserInternalError = 102,
@@ -243,7 +245,9 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
             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),
+            21 => ::std::option::Option::Some(WorkspaceErrorCode::ViewThumbnailInvalid),
+            22 => ::std::option::Option::Some(WorkspaceErrorCode::ViewIdInvalid),
+            23 => ::std::option::Option::Some(WorkspaceErrorCode::ViewDescInvalid),
             100 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseConnectionFail),
             101 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceDatabaseError),
             102 => ::std::option::Option::Some(WorkspaceErrorCode::UserInternalError),
@@ -261,7 +265,9 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
             WorkspaceErrorCode::AppIdInvalid,
             WorkspaceErrorCode::AppNameInvalid,
             WorkspaceErrorCode::ViewNameInvalid,
-            WorkspaceErrorCode::ViewThumbnailName,
+            WorkspaceErrorCode::ViewThumbnailInvalid,
+            WorkspaceErrorCode::ViewIdInvalid,
+            WorkspaceErrorCode::ViewDescInvalid,
             WorkspaceErrorCode::DatabaseConnectionFail,
             WorkspaceErrorCode::WorkspaceDatabaseError,
             WorkspaceErrorCode::UserInternalError,
@@ -296,21 +302,22 @@ 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*\xa3\x02\n\x12WorkspaceErrorCode\x12\x0b\n\x07Unknown\x10\0\
+    \tR\x03msg*\xce\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\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\
+    valid\x10\x14\x12\x18\n\x14ViewThumbnailInvalid\x10\x15\x12\x11\n\rViewI\
+    dInvalid\x10\x16\x12\x13\n\x0fViewDescInvalid\x10\x17\x12\x1a\n\x16Datab\
+    aseConnectionFail\x10d\x12\x1a\n\x16WorkspaceDatabaseError\x10e\x12\x15\
+    \n\x11UserInternalError\x10f\x12\x13\n\x0fUserNotLoginYet\x10gJ\xee\x05\
+    \n\x06\x12\x04\0\0\x15\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\
+    \x03\x04\x11\x12\n\n\n\x02\x05\0\x12\x04\x06\0\x15\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\
@@ -326,16 +333,20 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \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\
+    \x1e\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\x0e\x04\x18\n\x0c\n\x05\x05\0\
+    \x02\x07\x02\x12\x03\x0e\x1b\x1d\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0f\
+    \x04\x17\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0f\x04\x11\n\x0c\n\x05\
+    \x05\0\x02\x08\x02\x12\x03\x0f\x14\x16\n\x0b\n\x04\x05\0\x02\t\x12\x03\
+    \x10\x04\x19\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x10\x04\x13\n\x0c\n\x05\
+    \x05\0\x02\t\x02\x12\x03\x10\x16\x18\n\x0b\n\x04\x05\0\x02\n\x12\x03\x11\
+    \x04!\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\x11\x04\x1a\n\x0c\n\x05\x05\0\
+    \x02\n\x02\x12\x03\x11\x1d\x20\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x12\x04\
+    !\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x12\x04\x1a\n\x0c\n\x05\x05\0\
+    \x02\x0b\x02\x12\x03\x12\x1d\x20\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x13\
+    \x04\x1c\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x13\x04\x15\n\x0c\n\x05\
+    \x05\0\x02\x0c\x02\x12\x03\x13\x18\x1b\n\x0b\n\x04\x05\0\x02\r\x12\x03\
+    \x14\x04\x1a\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\x14\x04\x13\n\x0c\n\x05\
+    \x05\0\x02\r\x02\x12\x03\x14\x16\x19b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 25 - 14
rust-lib/flowy-workspace/src/protobuf/model/event.rs

@@ -31,6 +31,8 @@ pub enum WorkspaceEvent {
     CreateApp = 101,
     GetApp = 102,
     CreateView = 201,
+    ReadView = 202,
+    UpdateView = 203,
 }
 
 impl ::protobuf::ProtobufEnum for WorkspaceEvent {
@@ -46,6 +48,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             101 => ::std::option::Option::Some(WorkspaceEvent::CreateApp),
             102 => ::std::option::Option::Some(WorkspaceEvent::GetApp),
             201 => ::std::option::Option::Some(WorkspaceEvent::CreateView),
+            202 => ::std::option::Option::Some(WorkspaceEvent::ReadView),
+            203 => ::std::option::Option::Some(WorkspaceEvent::UpdateView),
             _ => ::std::option::Option::None
         }
     }
@@ -58,6 +62,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             WorkspaceEvent::CreateApp,
             WorkspaceEvent::GetApp,
             WorkspaceEvent::CreateView,
+            WorkspaceEvent::ReadView,
+            WorkspaceEvent::UpdateView,
         ];
         values
     }
@@ -86,24 +92,29 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0bevent.proto*x\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorkspace\x10\
-    \0\x12\x13\n\x0fGetCurWorkspace\x10\x01\x12\x10\n\x0cGetWorkspace\x10\
-    \x02\x12\r\n\tCreateApp\x10e\x12\n\n\x06GetApp\x10f\x12\x0f\n\nCreateVie\
-    w\x10\xc9\x01J\xa0\x02\n\x06\x12\x04\0\0\t\x01\n\x08\n\x01\x0c\x12\x03\0\
-    \0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\t\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\x18\n\x0c\n\x05\x05\0\
-    \x02\x01\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\
-    \x04\x16\x17\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x15\n\x0c\n\x05\
-    \x05\0\x02\x02\x01\x12\x03\x05\x04\x10\n\x0c\n\x05\x05\0\x02\x02\x02\x12\
-    \x03\x05\x13\x14\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x14\n\x0c\n\
-    \x05\x05\0\x02\x03\x01\x12\x03\x06\x04\r\n\x0c\n\x05\x05\0\x02\x03\x02\
+    \n\x0bevent.proto*\x98\x01\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
+    ace\x10\0\x12\x13\n\x0fGetCurWorkspace\x10\x01\x12\x10\n\x0cGetWorkspace\
+    \x10\x02\x12\r\n\tCreateApp\x10e\x12\n\n\x06GetApp\x10f\x12\x0f\n\nCreat\
+    eView\x10\xc9\x01\x12\r\n\x08ReadView\x10\xca\x01\x12\x0f\n\nUpdateView\
+    \x10\xcb\x01J\xf2\x02\n\x06\x12\x04\0\0\x0b\x01\n\x08\n\x01\x0c\x12\x03\
+    \0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x0b\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\x18\n\x0c\n\x05\
+    \x05\0\x02\x01\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x05\0\x02\x01\x02\x12\
+    \x03\x04\x16\x17\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x15\n\x0c\n\
+    \x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x10\n\x0c\n\x05\x05\0\x02\x02\x02\
+    \x12\x03\x05\x13\x14\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x14\n\x0c\
+    \n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\r\n\x0c\n\x05\x05\0\x02\x03\x02\
     \x12\x03\x06\x10\x13\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x11\n\x0c\
     \n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\n\n\x0c\n\x05\x05\0\x02\x04\x02\
     \x12\x03\x07\r\x10\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x15\n\x0c\n\
     \x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x0e\n\x0c\n\x05\x05\0\x02\x05\x02\
-    \x12\x03\x08\x11\x14b\x06proto3\
+    \x12\x03\x08\x11\x14\n\x0b\n\x04\x05\0\x02\x06\x12\x03\t\x04\x13\n\x0c\n\
+    \x05\x05\0\x02\x06\x01\x12\x03\t\x04\x0c\n\x0c\n\x05\x05\0\x02\x06\x02\
+    \x12\x03\t\x0f\x12\n\x0b\n\x04\x05\0\x02\x07\x12\x03\n\x04\x15\n\x0c\n\
+    \x05\x05\0\x02\x07\x01\x12\x03\n\x04\x0e\n\x0c\n\x05\x05\0\x02\x07\x02\
+    \x12\x03\n\x11\x14b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -1,5 +1,8 @@
 // Auto-generated, do not edit 
 
+mod view_update; 
+pub use view_update::*; 
+
 mod app_query; 
 pub use app_query::*; 
 
@@ -32,3 +35,6 @@ pub use workspace_create::*;
 
 mod app_update; 
 pub use app_update::*; 
+
+mod view_query; 
+pub use view_query::*; 

+ 205 - 0
rust-lib/flowy-workspace/src/protobuf/model/view_query.rs

@@ -0,0 +1,205 @@
+// 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_query.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 QueryViewRequest {
+    // message fields
+    pub view_id: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a QueryViewRequest {
+    fn default() -> &'a QueryViewRequest {
+        <QueryViewRequest as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl QueryViewRequest {
+    pub fn new() -> QueryViewRequest {
+        ::std::default::Default::default()
+    }
+
+    // string view_id = 1;
+
+
+    pub fn get_view_id(&self) -> &str {
+        &self.view_id
+    }
+    pub fn clear_view_id(&mut self) {
+        self.view_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_view_id(&mut self, v: ::std::string::String) {
+        self.view_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_view_id(&mut self) -> &mut ::std::string::String {
+        &mut self.view_id
+    }
+
+    // Take field
+    pub fn take_view_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.view_id, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for QueryViewRequest {
+    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.view_id)?;
+                },
+                _ => {
+                    ::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.view_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.view_id);
+        }
+        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.view_id.is_empty() {
+            os.write_string(1, &self.view_id)?;
+        }
+        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() -> QueryViewRequest {
+        QueryViewRequest::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>(
+                "view_id",
+                |m: &QueryViewRequest| { &m.view_id },
+                |m: &mut QueryViewRequest| { &mut m.view_id },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<QueryViewRequest>(
+                "QueryViewRequest",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static QueryViewRequest {
+        static instance: ::protobuf::rt::LazyV2<QueryViewRequest> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(QueryViewRequest::new)
+    }
+}
+
+impl ::protobuf::Clear for QueryViewRequest {
+    fn clear(&mut self) {
+        self.view_id.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for QueryViewRequest {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for QueryViewRequest {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x10view_query.proto\"+\n\x10QueryViewRequest\x12\x17\n\x07view_id\x18\
+    \x01\x20\x01(\tR\x06viewIdJa\n\x06\x12\x04\0\0\x04\x01\n\x08\n\x01\x0c\
+    \x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x04\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\x17\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\x12\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x15\x16b\
+    \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()
+    })
+}

+ 463 - 0
rust-lib/flowy-workspace/src/protobuf/model/view_update.rs

@@ -0,0 +1,463 @@
+// 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_update.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 UpdateViewRequest {
+    // message fields
+    pub view_id: ::std::string::String,
+    // message oneof groups
+    pub one_of_name: ::std::option::Option<UpdateViewRequest_oneof_one_of_name>,
+    pub one_of_desc: ::std::option::Option<UpdateViewRequest_oneof_one_of_desc>,
+    pub one_of_thumbnail: ::std::option::Option<UpdateViewRequest_oneof_one_of_thumbnail>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a UpdateViewRequest {
+    fn default() -> &'a UpdateViewRequest {
+        <UpdateViewRequest as ::protobuf::Message>::default_instance()
+    }
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateViewRequest_oneof_one_of_name {
+    name(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateViewRequest_oneof_one_of_desc {
+    desc(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateViewRequest_oneof_one_of_thumbnail {
+    thumbnail(::std::string::String),
+}
+
+impl UpdateViewRequest {
+    pub fn new() -> UpdateViewRequest {
+        ::std::default::Default::default()
+    }
+
+    // string view_id = 1;
+
+
+    pub fn get_view_id(&self) -> &str {
+        &self.view_id
+    }
+    pub fn clear_view_id(&mut self) {
+        self.view_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_view_id(&mut self, v: ::std::string::String) {
+        self.view_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_view_id(&mut self) -> &mut ::std::string::String {
+        &mut self.view_id
+    }
+
+    // Take field
+    pub fn take_view_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.view_id, ::std::string::String::new())
+    }
+
+    // string name = 2;
+
+
+    pub fn get_name(&self) -> &str {
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_name(&mut self) {
+        self.one_of_name = ::std::option::Option::None;
+    }
+
+    pub fn has_name(&self) -> bool {
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.one_of_name = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(_)) = self.one_of_name {
+        } else {
+            self.one_of_name = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(::std::string::String::new()));
+        }
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_name(&mut self) -> ::std::string::String {
+        if self.has_name() {
+            match self.one_of_name.take() {
+                ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // string desc = 3;
+
+
+    pub fn get_desc(&self) -> &str {
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_desc(&mut self) {
+        self.one_of_desc = ::std::option::Option::None;
+    }
+
+    pub fn has_desc(&self) -> bool {
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_desc(&mut self, v: ::std::string::String) {
+        self.one_of_desc = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_desc(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(_)) = self.one_of_desc {
+        } else {
+            self.one_of_desc = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(::std::string::String::new()));
+        }
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_desc(&mut self) -> ::std::string::String {
+        if self.has_desc() {
+            match self.one_of_desc.take() {
+                ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // string thumbnail = 4;
+
+
+    pub fn get_thumbnail(&self) -> &str {
+        match self.one_of_thumbnail {
+            ::std::option::Option::Some(UpdateViewRequest_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(UpdateViewRequest_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(UpdateViewRequest_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(UpdateViewRequest_oneof_one_of_thumbnail::thumbnail(_)) = self.one_of_thumbnail {
+        } else {
+            self.one_of_thumbnail = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_thumbnail::thumbnail(::std::string::String::new()));
+        }
+        match self.one_of_thumbnail {
+            ::std::option::Option::Some(UpdateViewRequest_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(UpdateViewRequest_oneof_one_of_thumbnail::thumbnail(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+}
+
+impl ::protobuf::Message for UpdateViewRequest {
+    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.view_id)?;
+                },
+                2 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_name = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_name::name(is.read_string()?));
+                },
+                3 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_desc = ::std::option::Option::Some(UpdateViewRequest_oneof_one_of_desc::desc(is.read_string()?));
+                },
+                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(UpdateViewRequest_oneof_one_of_thumbnail::thumbnail(is.read_string()?));
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.view_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.view_id);
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_name {
+            match v {
+                &UpdateViewRequest_oneof_one_of_name::name(ref v) => {
+                    my_size += ::protobuf::rt::string_size(2, &v);
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_desc {
+            match v {
+                &UpdateViewRequest_oneof_one_of_desc::desc(ref v) => {
+                    my_size += ::protobuf::rt::string_size(3, &v);
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
+            match v {
+                &UpdateViewRequest_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.view_id.is_empty() {
+            os.write_string(1, &self.view_id)?;
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_name {
+            match v {
+                &UpdateViewRequest_oneof_one_of_name::name(ref v) => {
+                    os.write_string(2, v)?;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_desc {
+            match v {
+                &UpdateViewRequest_oneof_one_of_desc::desc(ref v) => {
+                    os.write_string(3, v)?;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_thumbnail {
+            match v {
+                &UpdateViewRequest_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() -> UpdateViewRequest {
+        UpdateViewRequest::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>(
+                "view_id",
+                |m: &UpdateViewRequest| { &m.view_id },
+                |m: &mut UpdateViewRequest| { &mut m.view_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "name",
+                UpdateViewRequest::has_name,
+                UpdateViewRequest::get_name,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "desc",
+                UpdateViewRequest::has_desc,
+                UpdateViewRequest::get_desc,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "thumbnail",
+                UpdateViewRequest::has_thumbnail,
+                UpdateViewRequest::get_thumbnail,
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdateViewRequest>(
+                "UpdateViewRequest",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static UpdateViewRequest {
+        static instance: ::protobuf::rt::LazyV2<UpdateViewRequest> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(UpdateViewRequest::new)
+    }
+}
+
+impl ::protobuf::Clear for UpdateViewRequest {
+    fn clear(&mut self) {
+        self.view_id.clear();
+        self.one_of_name = ::std::option::Option::None;
+        self.one_of_desc = ::std::option::Option::None;
+        self.one_of_thumbnail = ::std::option::Option::None;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for UpdateViewRequest {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for UpdateViewRequest {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x11view_update.proto\"\xaa\x01\n\x11UpdateViewRequest\x12\x17\n\x07vi\
+    ew_id\x18\x01\x20\x01(\tR\x06viewId\x12\x14\n\x04name\x18\x02\x20\x01(\t\
+    H\0R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\x04desc\x12\x1e\
+    \n\tthumbnail\x18\x04\x20\x01(\tH\x02R\tthumbnailB\r\n\x0bone_of_nameB\r\
+    \n\x0bone_of_descB\x12\n\x10one_of_thumbnailJ\xd7\x02\n\x06\x12\x04\0\0\
+    \x07\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\x19\n\x0b\n\x04\x04\0\x02\0\
+    \x12\x03\x03\x04\x17\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\x12\n\x0c\n\x05\x04\0\x02\0\x03\
+    \x12\x03\x03\x15\x16\n\x0b\n\x04\x04\0\x08\0\x12\x03\x04\x04*\n\x0c\n\
+    \x05\x04\0\x08\0\x01\x12\x03\x04\n\x15\n\x0b\n\x04\x04\0\x02\x01\x12\x03\
+    \x04\x18(\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x18\x1e\n\x0c\n\x05\
+    \x04\0\x02\x01\x01\x12\x03\x04\x1f#\n\x0c\n\x05\x04\0\x02\x01\x03\x12\
+    \x03\x04&'\n\x0b\n\x04\x04\0\x08\x01\x12\x03\x05\x04*\n\x0c\n\x05\x04\0\
+    \x08\x01\x01\x12\x03\x05\n\x15\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x18\
+    (\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x18\x1e\n\x0c\n\x05\x04\0\
+    \x02\x02\x01\x12\x03\x05\x1f#\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05&\
+    '\n\x0b\n\x04\x04\0\x08\x02\x12\x03\x06\x044\n\x0c\n\x05\x04\0\x08\x02\
+    \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\x0601b\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()
+    })
+}

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

@@ -12,7 +12,9 @@ enum WorkspaceErrorCode {
     AppIdInvalid = 10;
     AppNameInvalid = 11;
     ViewNameInvalid = 20;
-    ViewThumbnailName = 21;
+    ViewThumbnailInvalid = 21;
+    ViewIdInvalid = 22;
+    ViewDescInvalid = 23;
     DatabaseConnectionFail = 100;
     WorkspaceDatabaseError = 101;
     UserInternalError = 102;

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

@@ -7,4 +7,6 @@ enum WorkspaceEvent {
     CreateApp = 101;
     GetApp = 102;
     CreateView = 201;
+    ReadView = 202;
+    UpdateView = 203;
 }

+ 5 - 0
rust-lib/flowy-workspace/src/protobuf/proto/view_query.proto

@@ -0,0 +1,5 @@
+syntax = "proto3";
+
+message QueryViewRequest {
+    string view_id = 1;
+}

+ 8 - 0
rust-lib/flowy-workspace/src/protobuf/proto/view_update.proto

@@ -0,0 +1,8 @@
+syntax = "proto3";
+
+message UpdateViewRequest {
+    string view_id = 1;
+    oneof one_of_name { string name = 2; };
+    oneof one_of_desc { string desc = 3; };
+    oneof one_of_thumbnail { string thumbnail = 4; };
+}

+ 18 - 3
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -1,9 +1,9 @@
 use crate::{
-    entities::view::{CreateViewParams, View},
+    entities::view::{CreateViewParams, UpdateViewParams, View},
     errors::WorkspaceError,
     module::WorkspaceDatabase,
     observable::{send_observable, WorkspaceObservable},
-    sql_tables::view::{ViewTable, ViewTableSql},
+    sql_tables::view::{ViewTable, ViewTableChangeset, ViewTableSql},
 };
 use std::sync::Arc;
 
@@ -22,7 +22,22 @@ impl ViewController {
         let view: View = view_table.clone().into();
         let _ = self.sql.create_view(view_table)?;
 
-        send_observable(&view.id, WorkspaceObservable::AppAddView);
+        send_observable(&view.app_id, WorkspaceObservable::AppAddView);
         Ok(view)
     }
+
+    pub async fn read_view(&self, view_id: &str) -> Result<View, WorkspaceError> {
+        let view_table = self.sql.read_view(view_id)?;
+        let view: View = view_table.into();
+        Ok(view)
+    }
+
+    pub async fn update_view(&self, params: UpdateViewParams) -> Result<(), WorkspaceError> {
+        let changeset = ViewTableChangeset::new(params);
+        let view_id = changeset.id.clone();
+        let _ = self.sql.update_view(changeset)?;
+        send_observable(&view_id, WorkspaceObservable::ViewUpdateDesc);
+
+        Ok(())
+    }
 }

+ 19 - 2
rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs

@@ -1,9 +1,12 @@
 use crate::{
     errors::WorkspaceError,
     module::WorkspaceDatabase,
-    sql_tables::{app::AppTable, view::ViewTable},
+    sql_tables::view::{ViewTable, ViewTableChangeset},
+};
+use flowy_database::{
+    prelude::*,
+    schema::{view_table, view_table::dsl},
 };
-use flowy_database::{prelude::*, schema::view_table};
 use std::sync::Arc;
 
 pub struct ViewTableSql {
@@ -19,5 +22,19 @@ impl ViewTableSql {
         Ok(())
     }
 
+    pub(crate) fn read_view(&self, view_id: &str) -> Result<ViewTable, WorkspaceError> {
+        let view_table = dsl::view_table
+            .filter(view_table::id.eq(view_id))
+            .first::<ViewTable>(&*(self.database.db_connection()?))?;
+
+        Ok(view_table)
+    }
+
+    pub(crate) fn update_view(&self, changeset: ViewTableChangeset) -> Result<(), WorkspaceError> {
+        let conn = self.database.db_connection()?;
+        diesel_update_table!(view_table, changeset, conn);
+        Ok(())
+    }
+
     pub fn delete_view(&self, view_id: &str) -> Result<(), WorkspaceError> { unimplemented!() }
 }

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

@@ -1,5 +1,5 @@
 use crate::{
-    entities::view::{CreateViewParams, View, ViewType},
+    entities::view::{CreateViewParams, UpdateViewParams, View, ViewType},
     impl_sql_integer_expression,
     sql_tables::app::AppTable,
 };
@@ -62,15 +62,17 @@ pub struct ViewTableChangeset {
     pub id: String,
     pub name: Option<String>,
     pub desc: Option<String>,
+    pub thumbnail: Option<String>,
     pub modified_time: i64,
 }
 
 impl ViewTableChangeset {
-    pub fn new(id: &str) -> Self {
+    pub fn new(params: UpdateViewParams) -> Self {
         ViewTableChangeset {
-            id: id.to_string(),
-            name: None,
-            desc: None,
+            id: params.view_id,
+            name: params.name,
+            desc: params.desc,
+            thumbnail: params.thumbnail,
             modified_time: timestamp(),
         }
     }