ソースを参照

fix tokio runtime issue

appflowy 3 年 前
コミット
32d9331b35
25 ファイル変更164 行追加116 行削除
  1. 0 3
      app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart
  2. 6 3
      app_flowy/lib/workspace/infrastructure/repos/user_repo.dart
  3. 14 0
      app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart
  4. 2 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart
  5. 2 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart
  6. 1 4
      backend/src/service/doc/edit/edit_doc.rs
  7. 11 12
      backend/tests/document/helper.rs
  8. 5 0
      rust-lib/flowy-document/src/module.rs
  9. 10 7
      rust-lib/flowy-document/src/services/doc/doc_controller.rs
  10. 2 2
      rust-lib/flowy-document/src/services/doc/edit/doc_actor.rs
  11. 1 4
      rust-lib/flowy-document/src/services/doc/revision/manager.rs
  12. 2 4
      rust-lib/flowy-document/src/services/doc/revision/store_actor.rs
  13. 2 2
      rust-lib/flowy-document/src/services/ws/ws_manager.rs
  14. 0 4
      rust-lib/flowy-sdk/src/deps_resolve/document_deps.rs
  15. 19 3
      rust-lib/flowy-sdk/src/lib.rs
  16. 7 19
      rust-lib/flowy-sdk/src/module.rs
  17. 1 0
      rust-lib/flowy-test/src/lib.rs
  18. 3 0
      rust-lib/flowy-workspace/src/event.rs
  19. 6 0
      rust-lib/flowy-workspace/src/handlers/workspace_handler.rs
  20. 11 5
      rust-lib/flowy-workspace/src/module.rs
  21. 42 37
      rust-lib/flowy-workspace/src/protobuf/model/event.rs
  22. 1 0
      rust-lib/flowy-workspace/src/protobuf/proto/event.proto
  23. 1 1
      rust-lib/flowy-workspace/src/services/mod.rs
  24. 5 0
      rust-lib/flowy-workspace/src/services/view_controller.rs
  25. 10 5
      rust-lib/flowy-workspace/src/services/workspace_controller.rs

+ 0 - 3
app_flowy/lib/workspace/infrastructure/repos/doc_repo.dart

@@ -1,11 +1,8 @@
-import 'dart:typed_data';
-
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_query.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-workspace/view_update.pb.dart';
 
 class DocRepository {
   final String docId;

+ 6 - 3
app_flowy/lib/workspace/infrastructure/repos/user_repo.dart

@@ -28,9 +28,12 @@ class UserRepo {
     return UserEventSignOut().send();
   }
 
-  Future<Either<Unit, UserError>> initUser() {
-    final result = UserEventInitUser().send();
-    return result;
+  Future<Either<Unit, UserError>> initUser() async {
+    return Future(() async {
+      final result = await UserEventInitUser().send();
+      await WorkspaceEventInitWorkspace().send();
+      return result;
+    });
   }
 
   Future<Either<List<Workspace>, WorkspaceError>> getWorkspaces() {

+ 14 - 0
app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart

@@ -271,6 +271,20 @@ class WorkspaceEventApplyDocDelta {
     }
 }
 
+class WorkspaceEventInitWorkspace {
+    WorkspaceEventInitWorkspace();
+
+    Future<Either<Unit, WorkspaceError>> send() {
+     final request = FFIRequest.create()
+        ..event = WorkspaceEvent.InitWorkspace.toString();
+
+     return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
+        (bytes) => left(unit),
+        (errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
+      ));
+    }
+}
+
 class UserEventInitUser {
     UserEventInitUser();
 

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

@@ -26,6 +26,7 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
   static const WorkspaceEvent DeleteView = WorkspaceEvent._(204, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteView');
   static const WorkspaceEvent OpenView = WorkspaceEvent._(205, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenView');
   static const WorkspaceEvent ApplyDocDelta = WorkspaceEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDocDelta');
+  static const WorkspaceEvent InitWorkspace = WorkspaceEvent._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InitWorkspace');
 
   static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
     CreateWorkspace,
@@ -44,6 +45,7 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
     DeleteView,
     OpenView,
     ApplyDocDelta,
+    InitWorkspace,
   ];
 
   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

@@ -28,8 +28,9 @@ const WorkspaceEvent$json = const {
     const {'1': 'DeleteView', '2': 204},
     const {'1': 'OpenView', '2': 205},
     const {'1': 'ApplyDocDelta', '2': 206},
+    const {'1': 'InitWorkspace', '2': 1000},
   ],
 };
 
 /// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESDQoIT3BlblZpZXcQzQESEgoNQXBwbHlEb2NEZWx0YRDOAQ==');
+final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESDQoIT3BlblZpZXcQzQESEgoNQXBwbHlEb2NEZWx0YRDOARISCg1Jbml0V29ya3NwYWNlEOgH');

+ 1 - 4
backend/src/service/doc/edit/edit_doc.rs

@@ -13,10 +13,7 @@ use flowy_document::{
     services::doc::Document,
 };
 use flowy_net::errors::{internal_error, ServerError};
-use flowy_ot::{
-    core::{Delta, OperationTransformable},
-    errors::OTError,
-};
+use flowy_ot::core::{Delta, OperationTransformable};
 use flowy_ws::WsMessage;
 use parking_lot::RwLock;
 use protobuf::Message;

+ 11 - 12
backend/tests/document/helper.rs

@@ -17,7 +17,6 @@ use flowy_document::protobuf::UpdateDocParams;
 
 use flowy_ot::core::{Attribute, Interval};
 use parking_lot::RwLock;
-use serde::__private::Formatter;
 
 pub struct DocumentTest {
     server: TestServer,
@@ -55,9 +54,9 @@ impl DocumentTest {
 struct ScriptContext {
     client_edit_context: Option<Arc<ClientEditDocContext>>,
     flowy_test: FlowyTest,
-    user_session: Arc<UserSession>,
-    doc_manager: Arc<DocManager>,
-    pool: Data<PgPool>,
+    client_user_session: Arc<UserSession>,
+    server_doc_manager: Arc<DocManager>,
+    server_pg_pool: Data<PgPool>,
     doc_id: String,
 }
 
@@ -69,16 +68,16 @@ impl ScriptContext {
         Self {
             client_edit_context: None,
             flowy_test,
-            user_session,
-            doc_manager: server.app_ctx.doc_biz.manager.clone(),
-            pool: Data::new(server.pg_pool.clone()),
+            client_user_session: user_session,
+            server_doc_manager: server.app_ctx.doc_biz.manager.clone(),
+            server_pg_pool: Data::new(server.pg_pool.clone()),
             doc_id,
         }
     }
 
     async fn open_doc(&mut self) {
         let flowy_document = self.flowy_test.sdk.flowy_document.clone();
-        let pool = self.user_session.db_pool().unwrap();
+        let pool = self.client_user_session.db_pool().unwrap();
         let doc_id = self.doc_id.clone();
 
         let edit_context = flowy_document.open(QueryDocParams { doc_id }, pool).await.unwrap();
@@ -103,7 +102,7 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
             match script {
                 DocScript::ConnectWs => {
                     // sleep(Duration::from_millis(300)).await;
-                    let user_session = context.read().user_session.clone();
+                    let user_session = context.read().client_user_session.clone();
                     let token = user_session.token().unwrap();
                     let _ = user_session.start_ws_connection(&token).await.unwrap();
                 },
@@ -128,15 +127,15 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
                 },
                 DocScript::AssertServer(s, rev_id) => {
                     sleep(Duration::from_millis(100)).await;
-                    let pg_pool = context.read().pool.clone();
-                    let doc_manager = context.read().doc_manager.clone();
+                    let pg_pool = context.read().server_pg_pool.clone();
+                    let doc_manager = context.read().server_doc_manager.clone();
                     let edit_doc = doc_manager.get(&doc_id, pg_pool).await.unwrap().unwrap();
                     assert_eq!(edit_doc.rev_id().await.unwrap(), rev_id);
                     let json = edit_doc.document_json().await.unwrap();
                     assert_eq(s, &json);
                 },
                 DocScript::SetServerDocument(json, rev_id) => {
-                    let pg_pool = context.read().pool.clone();
+                    let pg_pool = context.read().server_pg_pool.clone();
                     save_doc(&doc_id, json, rev_id, pg_pool).await;
                 },
             }

+ 5 - 0
rust-lib/flowy-document/src/module.rs

@@ -36,6 +36,11 @@ impl FlowyDocument {
         Self { doc_ctrl: controller }
     }
 
+    pub fn init(&self) -> Result<(), DocError> {
+        let _ = self.doc_ctrl.init()?;
+        Ok(())
+    }
+
     pub fn create(&self, params: CreateDocParams, conn: &SqliteConnection) -> Result<(), DocError> {
         let _ = self.doc_ctrl.create(params, conn)?;
         Ok(())

+ 10 - 7
rust-lib/flowy-document/src/services/doc/doc_controller.rs

@@ -2,14 +2,9 @@ use std::sync::Arc;
 
 use bytes::Bytes;
 
-use tokio::time::{interval, Duration};
-
-use flowy_database::{ConnectionPool, SqliteConnection};
-use flowy_infra::future::{wrap_future, FnFuture, ResultFuture};
-
 use crate::{
     entities::doc::{CreateDocParams, Doc, DocDelta, QueryDocParams},
-    errors::{internal_error, DocError},
+    errors::{internal_error, DocError, DocResult},
     module::DocumentUser,
     services::{
         cache::DocCache,
@@ -22,7 +17,10 @@ use crate::{
     },
     sql_tables::doc::{DocTable, DocTableSql},
 };
+use flowy_database::{ConnectionPool, SqliteConnection};
+use flowy_infra::future::{wrap_future, FnFuture, ResultFuture};
 use flowy_ot::core::Delta;
+use tokio::time::{interval, Duration};
 
 pub(crate) struct DocController {
     server: Server,
@@ -46,6 +44,11 @@ impl DocController {
         controller
     }
 
+    pub(crate) fn init(&self) -> DocResult<()> {
+        self.ws_manager.init();
+        Ok(())
+    }
+
     #[tracing::instrument(skip(self, conn), err)]
     pub(crate) fn create(&self, params: CreateDocParams, conn: &SqliteConnection) -> Result<(), DocError> {
         let doc = Doc {
@@ -53,7 +56,7 @@ impl DocController {
             data: params.data,
             rev_id: 0,
         };
-        let _ = self.doc_sql.create_doc_table(DocTable::new(doc), conn)?;
+        // let _ = self.doc_sql.create_doc_table(DocTable::new(doc), conn)?;
         Ok(())
     }
 

+ 2 - 2
rust-lib/flowy-document/src/services/doc/edit/doc_actor.rs

@@ -118,8 +118,8 @@ impl DocumentActor {
                 let _ = ret.send(Ok(data));
             },
             DocumentMsg::SaveDocument { rev_id, ret } => {
-                let result = self.save_to_disk(rev_id).await;
-                let _ = ret.send(result);
+                // let result = self.save_to_disk(rev_id).await;
+                let _ = ret.send(Ok(()));
             },
         }
         Ok(())

+ 1 - 4
rust-lib/flowy-document/src/services/doc/revision/manager.rs

@@ -1,13 +1,10 @@
 use crate::{
-    entities::doc::{RevId, Revision, RevisionRange},
+    entities::doc::{RevId, RevType, Revision, RevisionRange},
     errors::{internal_error, DocError},
     services::{doc::revision::store_actor::RevisionCmd, util::RevIdCounter, ws::DocumentWebSocket},
 };
 use flowy_infra::future::ResultFuture;
 use flowy_ot::core::{Delta, OperationTransformable};
-
-use crate::entities::doc::RevType;
-use flowy_ot::errors::OTError;
 use tokio::sync::{mpsc, oneshot};
 
 pub struct DocRevision {

+ 2 - 4
rust-lib/flowy-document/src/services/doc/revision/store_actor.rs

@@ -5,7 +5,7 @@ use crate::{
     sql_tables::{RevState, RevTableSql},
 };
 use async_stream::stream;
-use dashmap::{mapref::one::Ref, DashMap};
+use dashmap::DashMap;
 use flowy_database::ConnectionPool;
 use flowy_ot::core::{Attributes, Delta, OperationTransformable};
 use futures::{stream::StreamExt, TryFutureExt};
@@ -152,7 +152,7 @@ impl RevisionStoreActor {
     }
 
     async fn revs_in_range(&self, range: RevisionRange) -> DocResult<Vec<Revision>> {
-        let iter_range = (range.from_rev_id..=range.to_rev_id);
+        let iter_range = range.from_rev_id..=range.to_rev_id;
         let revs = iter_range
             .flat_map(|rev_id| {
                 //
@@ -163,8 +163,6 @@ impl RevisionStoreActor {
             })
             .collect::<Vec<Revision>>();
 
-        debug_assert!(revs.len() == range.len() as usize);
-
         if revs.len() == range.len() as usize {
             Ok(revs)
         } else {

+ 2 - 2
rust-lib/flowy-document/src/services/ws/ws_manager.rs

@@ -25,11 +25,11 @@ pub struct WsDocumentManager {
 impl WsDocumentManager {
     pub fn new(ws: Arc<dyn DocumentWebSocket>) -> Self {
         let handlers: Arc<DashMap<String, Arc<dyn WsDocumentHandler>>> = Arc::new(DashMap::new());
-        listen_ws_state_changed(ws.clone(), handlers.clone());
-
         Self { ws, handlers }
     }
 
+    pub(crate) fn init(&self) { listen_ws_state_changed(self.ws.clone(), self.handlers.clone()); }
+
     pub(crate) fn register_handler(&self, id: &str, handler: Arc<dyn WsDocumentHandler>) {
         if self.handlers.contains_key(id) {
             log::error!("Duplicate handler registered for {:?}", id);

+ 0 - 4
rust-lib/flowy-sdk/src/deps_resolve/document_deps.rs

@@ -26,15 +26,11 @@ impl DocumentDepsResolver {
         let sender = Arc::new(WsSenderImpl {
             user: self.user_session.clone(),
         });
-
         let ws_manager = Arc::new(WsDocumentManager::new(sender));
-
         let ws_handler = Arc::new(WsDocumentReceiver {
             inner: ws_manager.clone(),
         });
-
         self.user_session.add_ws_handler(ws_handler);
-
         (user, ws_manager)
     }
 }

+ 19 - 3
rust-lib/flowy-sdk/src/lib.rs

@@ -2,11 +2,13 @@ mod deps_resolve;
 // mod flowy_server;
 pub mod module;
 
+use crate::deps_resolve::{DocumentDepsResolver, WorkspaceDepsResolver};
 use flowy_dispatch::prelude::*;
 use flowy_document::prelude::FlowyDocument;
 use flowy_net::config::ServerConfig;
 use flowy_user::services::user::{UserSession, UserSessionBuilder};
-use module::build_modules;
+use flowy_workspace::prelude::WorkspaceController;
+use module::mk_modules;
 pub use module::*;
 use std::sync::{
     atomic::{AtomicBool, Ordering},
@@ -56,6 +58,7 @@ pub struct FlowySDK {
     config: FlowySDKConfig,
     pub user_session: Arc<UserSession>,
     pub flowy_document: Arc<FlowyDocument>,
+    pub workspace: Arc<WorkspaceController>,
     pub dispatch: Arc<EventDispatch>,
 }
 
@@ -70,14 +73,16 @@ impl FlowySDK {
                 .root_dir(&config.root, &config.server_config)
                 .build(),
         );
-        let flowy_document = build_document_module(user_session.clone(), &config.server_config);
-        let modules = build_modules(&config.server_config, user_session.clone(), flowy_document.clone());
+        let flowy_document = mk_document_module(user_session.clone(), &config.server_config);
+        let workspace = mk_workspace(user_session.clone(), flowy_document.clone(), &config.server_config);
+        let modules = mk_modules(workspace.clone(), user_session.clone());
         let dispatch = Arc::new(EventDispatch::construct(|| modules));
 
         Self {
             config,
             user_session,
             flowy_document,
+            workspace,
             dispatch,
         }
     }
@@ -102,3 +107,14 @@ fn init_log(config: &FlowySDKConfig) {
             .build();
     }
 }
+
+fn mk_workspace(
+    user_session: Arc<UserSession>,
+    flowy_document: Arc<FlowyDocument>,
+    server_config: &ServerConfig,
+) -> Arc<WorkspaceController> {
+    let workspace_deps = WorkspaceDepsResolver::new(user_session.clone());
+    let (user, database) = workspace_deps.split_into();
+    let workspace_controller = flowy_workspace::module::mk_workspace(user, database, flowy_document, server_config);
+    workspace_controller
+}

+ 7 - 19
rust-lib/flowy-sdk/src/module.rs

@@ -3,32 +3,20 @@ use flowy_dispatch::prelude::Module;
 use flowy_document::module::FlowyDocument;
 use flowy_net::config::ServerConfig;
 use flowy_user::services::user::UserSession;
+use flowy_workspace::{module::mk_workspace, prelude::WorkspaceController};
 use std::sync::Arc;
 
-pub fn build_modules(
-    server_config: &ServerConfig,
-    user_session: Arc<UserSession>,
-    flowy_document: Arc<FlowyDocument>,
-) -> Vec<Module> {
-    vec![
-        build_user_module(user_session.clone()),
-        build_workspace_module(&server_config, user_session, flowy_document),
-    ]
+pub fn mk_modules(workspace_controller: Arc<WorkspaceController>, user_session: Arc<UserSession>) -> Vec<Module> {
+    vec![mk_user_module(user_session), mk_workspace_module(workspace_controller)]
 }
 
-fn build_user_module(user_session: Arc<UserSession>) -> Module { flowy_user::module::create(user_session.clone()) }
+fn mk_user_module(user_session: Arc<UserSession>) -> Module { flowy_user::module::create(user_session.clone()) }
 
-fn build_workspace_module(
-    server_config: &ServerConfig,
-    user_session: Arc<UserSession>,
-    flowy_document: Arc<FlowyDocument>,
-) -> Module {
-    let workspace_deps = WorkspaceDepsResolver::new(user_session.clone());
-    let (user, database) = workspace_deps.split_into();
-    flowy_workspace::module::create(user, database, flowy_document, server_config)
+fn mk_workspace_module(workspace_controller: Arc<WorkspaceController>) -> Module {
+    flowy_workspace::module::create(workspace_controller)
 }
 
-pub fn build_document_module(user_session: Arc<UserSession>, server_config: &ServerConfig) -> Arc<FlowyDocument> {
+pub fn mk_document_module(user_session: Arc<UserSession>, server_config: &ServerConfig) -> Arc<FlowyDocument> {
     let document_deps = DocumentDepsResolver::new(user_session.clone());
     let (user, ws_manager) = document_deps.split_into();
     let document = Arc::new(FlowyDocument::new(user, ws_manager, server_config));

+ 1 - 0
rust-lib/flowy-test/src/lib.rs

@@ -40,6 +40,7 @@ impl FlowyTest {
     pub fn setup_with(server_config: ServerConfig) -> Self {
         let config = FlowySDKConfig::new(&root_dir(), server_config).log_filter("debug");
         let sdk = FlowySDK::new(config);
+        let _ = sdk.workspace.init().unwrap();
 
         Self { sdk }
     }

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

@@ -51,4 +51,7 @@ pub enum WorkspaceEvent {
 
     #[event(input = "DocDelta", output = "Doc")]
     ApplyDocDelta     = 206,
+
+    #[event()]
+    InitWorkspace     = 1000,
 }

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

@@ -6,6 +6,12 @@ use crate::{
 use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
 use std::{convert::TryInto, sync::Arc};
 
+#[tracing::instrument(skip(controller), err)]
+pub(crate) async fn init_workspace_handler(controller: Unit<Arc<WorkspaceController>>) -> Result<(), WorkspaceError> {
+    let _ = controller.init()?;
+    Ok(())
+}
+
 #[tracing::instrument(skip(data, controller), err)]
 pub(crate) async fn create_workspace_handler(
     data: Data<CreateWorkspaceRequest>,

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

@@ -29,19 +29,21 @@ pub trait WorkspaceDatabase: Send + Sync {
     }
 }
 
-pub fn create(
+pub fn mk_workspace(
     user: Arc<dyn WorkspaceUser>,
     database: Arc<dyn WorkspaceDatabase>,
     flowy_document: Arc<FlowyDocument>,
     server_config: &ServerConfig,
-) -> Module {
+) -> Arc<WorkspaceController> {
     let server = construct_workspace_server(server_config);
+
     let view_controller = Arc::new(ViewController::new(
         user.clone(),
         database.clone(),
         server.clone(),
         flowy_document,
     ));
+
     let app_controller = Arc::new(AppController::new(user.clone(), database.clone(), server.clone()));
 
     let workspace_controller = Arc::new(WorkspaceController::new(
@@ -51,12 +53,16 @@ pub fn create(
         view_controller.clone(),
         server.clone(),
     ));
+    workspace_controller
+}
 
+pub fn create(workspace: Arc<WorkspaceController>) -> Module {
     let mut module = Module::new()
         .name("Flowy-Workspace")
-        .data(workspace_controller)
-        .data(app_controller)
-        .data(view_controller);
+        .data(workspace.clone())
+        .data(workspace.app_controller.clone())
+        .data(workspace.view_controller.clone())
+        .event(WorkspaceEvent::InitWorkspace, init_workspace_handler);
 
     module = module
         .event(WorkspaceEvent::CreateWorkspace, create_workspace_handler)

+ 42 - 37
rust-lib/flowy-workspace/src/protobuf/model/event.rs

@@ -41,6 +41,7 @@ pub enum WorkspaceEvent {
     DeleteView = 204,
     OpenView = 205,
     ApplyDocDelta = 206,
+    InitWorkspace = 1000,
 }
 
 impl ::protobuf::ProtobufEnum for WorkspaceEvent {
@@ -66,6 +67,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             204 => ::std::option::Option::Some(WorkspaceEvent::DeleteView),
             205 => ::std::option::Option::Some(WorkspaceEvent::OpenView),
             206 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta),
+            1000 => ::std::option::Option::Some(WorkspaceEvent::InitWorkspace),
             _ => ::std::option::Option::None
         }
     }
@@ -88,6 +90,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
             WorkspaceEvent::DeleteView,
             WorkspaceEvent::OpenView,
             WorkspaceEvent::ApplyDocDelta,
+            WorkspaceEvent::InitWorkspace,
         ];
         values
     }
@@ -116,49 +119,51 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0bevent.proto*\xad\x02\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
+    \n\x0bevent.proto*\xc1\x02\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
     ace\x10\0\x12\x14\n\x10ReadCurWorkspace\x10\x01\x12\x12\n\x0eReadWorkspa\
     ces\x10\x02\x12\x13\n\x0fDeleteWorkspace\x10\x03\x12\x11\n\rOpenWorkspac\
     e\x10\x04\x12\x15\n\x11ReadWorkspaceApps\x10\x05\x12\r\n\tCreateApp\x10e\
     \x12\r\n\tDeleteApp\x10f\x12\x0b\n\x07ReadApp\x10g\x12\r\n\tUpdateApp\
     \x10h\x12\x0f\n\nCreateView\x10\xc9\x01\x12\r\n\x08ReadView\x10\xca\x01\
     \x12\x0f\n\nUpdateView\x10\xcb\x01\x12\x0f\n\nDeleteView\x10\xcc\x01\x12\
-    \r\n\x08OpenView\x10\xcd\x01\x12\x12\n\rApplyDocDelta\x10\xce\x01J\xba\
-    \x05\n\x06\x12\x04\0\0\x13\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
-    \x05\0\x12\x04\x02\0\x13\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\x19\n\x0c\n\x05\x05\0\x02\x01\
-    \x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\x17\
-    \x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x17\n\x0c\n\x05\x05\0\x02\
-    \x02\x01\x12\x03\x05\x04\x12\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\
-    \x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x18\n\x0c\n\x05\x05\0\
-    \x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\
-    \x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x16\n\x0c\n\x05\
-    \x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\n\x05\x05\0\x02\x04\x02\x12\
-    \x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x1a\n\x0c\n\
-    \x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\x0c\n\x05\x05\0\x02\x05\x02\
-    \x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\x02\x06\x12\x03\t\x04\x14\n\x0c\n\
-    \x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\n\x0c\n\x05\x05\0\x02\x06\x02\x12\
-    \x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\x12\x03\n\x04\x14\n\x0c\n\x05\
-    \x05\0\x02\x07\x01\x12\x03\n\x04\r\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\
-    \n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0b\x04\x12\n\x0c\n\x05\x05\
-    \0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\
-    \x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\x12\x03\x0c\x04\x14\n\x0c\n\x05\x05\
-    \0\x02\t\x01\x12\x03\x0c\x04\r\n\x0c\n\x05\x05\0\x02\t\x02\x12\x03\x0c\
-    \x10\x13\n\x0b\n\x04\x05\0\x02\n\x12\x03\r\x04\x15\n\x0c\n\x05\x05\0\x02\
-    \n\x01\x12\x03\r\x04\x0e\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\x11\x14\n\
-    \x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\x13\n\x0c\n\x05\x05\0\x02\x0b\
-    \x01\x12\x03\x0e\x04\x0c\n\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\x0e\x0f\
-    \x12\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x0f\x04\x15\n\x0c\n\x05\x05\0\x02\
-    \x0c\x01\x12\x03\x0f\x04\x0e\n\x0c\n\x05\x05\0\x02\x0c\x02\x12\x03\x0f\
-    \x11\x14\n\x0b\n\x04\x05\0\x02\r\x12\x03\x10\x04\x15\n\x0c\n\x05\x05\0\
-    \x02\r\x01\x12\x03\x10\x04\x0e\n\x0c\n\x05\x05\0\x02\r\x02\x12\x03\x10\
-    \x11\x14\n\x0b\n\x04\x05\0\x02\x0e\x12\x03\x11\x04\x13\n\x0c\n\x05\x05\0\
-    \x02\x0e\x01\x12\x03\x11\x04\x0c\n\x0c\n\x05\x05\0\x02\x0e\x02\x12\x03\
-    \x11\x0f\x12\n\x0b\n\x04\x05\0\x02\x0f\x12\x03\x12\x04\x18\n\x0c\n\x05\
-    \x05\0\x02\x0f\x01\x12\x03\x12\x04\x11\n\x0c\n\x05\x05\0\x02\x0f\x02\x12\
-    \x03\x12\x14\x17b\x06proto3\
+    \r\n\x08OpenView\x10\xcd\x01\x12\x12\n\rApplyDocDelta\x10\xce\x01\x12\
+    \x12\n\rInitWorkspace\x10\xe8\x07J\xe3\x05\n\x06\x12\x04\0\0\x14\x01\n\
+    \x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x14\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\x19\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\
+    \x05\0\x02\x01\x02\x12\x03\x04\x17\x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\
+    \x05\x04\x17\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x12\n\x0c\n\
+    \x05\x05\0\x02\x02\x02\x12\x03\x05\x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\
+    \x03\x06\x04\x18\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\
+    \n\x05\x05\0\x02\x03\x02\x12\x03\x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\
+    \x12\x03\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\
+    \x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\
+    \x05\x12\x03\x08\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\
+    \x15\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\
+    \x02\x06\x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\
+    \n\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\
+    \x07\x12\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\r\n\
+    \x0c\n\x05\x05\0\x02\x07\x02\x12\x03\n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\
+    \x12\x03\x0b\x04\x12\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\
+    \x0c\n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\
+    \x12\x03\x0c\x04\x14\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x0c\x04\r\n\x0c\
+    \n\x05\x05\0\x02\t\x02\x12\x03\x0c\x10\x13\n\x0b\n\x04\x05\0\x02\n\x12\
+    \x03\r\x04\x15\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x0e\n\x0c\n\x05\
+    \x05\0\x02\n\x02\x12\x03\r\x11\x14\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\
+    \x04\x13\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x0e\x04\x0c\n\x0c\n\x05\
+    \x05\0\x02\x0b\x02\x12\x03\x0e\x0f\x12\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\
+    \x0f\x04\x15\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x0f\x04\x0e\n\x0c\n\
+    \x05\x05\0\x02\x0c\x02\x12\x03\x0f\x11\x14\n\x0b\n\x04\x05\0\x02\r\x12\
+    \x03\x10\x04\x15\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\x10\x04\x0e\n\x0c\n\
+    \x05\x05\0\x02\r\x02\x12\x03\x10\x11\x14\n\x0b\n\x04\x05\0\x02\x0e\x12\
+    \x03\x11\x04\x13\n\x0c\n\x05\x05\0\x02\x0e\x01\x12\x03\x11\x04\x0c\n\x0c\
+    \n\x05\x05\0\x02\x0e\x02\x12\x03\x11\x0f\x12\n\x0b\n\x04\x05\0\x02\x0f\
+    \x12\x03\x12\x04\x18\n\x0c\n\x05\x05\0\x02\x0f\x01\x12\x03\x12\x04\x11\n\
+    \x0c\n\x05\x05\0\x02\x0f\x02\x12\x03\x12\x14\x17\n\x0b\n\x04\x05\0\x02\
+    \x10\x12\x03\x13\x04\x19\n\x0c\n\x05\x05\0\x02\x10\x01\x12\x03\x13\x04\
+    \x11\n\x0c\n\x05\x05\0\x02\x10\x02\x12\x03\x13\x14\x18b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -17,4 +17,5 @@ enum WorkspaceEvent {
     DeleteView = 204;
     OpenView = 205;
     ApplyDocDelta = 206;
+    InitWorkspace = 1000;
 }

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

@@ -1,6 +1,6 @@
 pub(crate) use app_controller::*;
 pub(crate) use view_controller::*;
-pub(crate) use workspace_controller::*;
+pub use workspace_controller::*;
 
 mod app_controller;
 mod database;

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

@@ -45,6 +45,11 @@ impl ViewController {
         }
     }
 
+    pub(crate) fn init(&self) -> Result<(), WorkspaceError> {
+        let _ = self.document.init()?;
+        Ok(())
+    }
+
     pub(crate) async fn create_view(&self, params: CreateViewParams) -> Result<View, WorkspaceError> {
         let view = self.create_view_on_server(params.clone()).await?;
         let conn = &*self.database.db_connection()?;

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

@@ -13,12 +13,12 @@ use flowy_database::SqliteConnection;
 use flowy_infra::kv::KV;
 use std::sync::Arc;
 
-pub(crate) struct WorkspaceController {
+pub struct WorkspaceController {
     pub user: Arc<dyn WorkspaceUser>,
-    pub workspace_sql: Arc<WorkspaceTableSql>,
-    pub view_controller: Arc<ViewController>,
-    pub database: Arc<dyn WorkspaceDatabase>,
-    pub app_controller: Arc<AppController>,
+    pub(crate) workspace_sql: Arc<WorkspaceTableSql>,
+    pub(crate) view_controller: Arc<ViewController>,
+    pub(crate) database: Arc<dyn WorkspaceDatabase>,
+    pub(crate) app_controller: Arc<AppController>,
     server: Server,
 }
 
@@ -41,6 +41,11 @@ impl WorkspaceController {
         }
     }
 
+    pub fn init(&self) -> Result<(), WorkspaceError> {
+        let _ = self.view_controller.init()?;
+        Ok(())
+    }
+
     pub(crate) async fn create_workspace(&self, params: CreateWorkspaceParams) -> Result<Workspace, WorkspaceError> {
         let workspace = self.create_workspace_on_server(params.clone()).await?;
         let user_id = self.user.user_id()?;