123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- use crate::entities::view::ViewDataFormatPB;
- use crate::entities::{ViewLayoutTypePB, ViewPB, WorkspacePB};
- use crate::services::folder_editor::FolderRevisionMergeable;
- use crate::{
- entities::workspace::RepeatedWorkspacePB,
- errors::FlowyResult,
- event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
- notification::{send_notification, FolderNotification},
- services::{
- folder_editor::FolderEditor, persistence::FolderPersistence, set_current_workspace,
- AppController, TrashController, ViewController, WorkspaceController,
- },
- };
- use bytes::Bytes;
- use flowy_document::editor::initial_read_me;
- use flowy_error::FlowyError;
- use flowy_revision::{
- RevisionManager, RevisionPersistence, RevisionPersistenceConfiguration, RevisionWebSocket,
- };
- use folder_model::user_default;
- use lazy_static::lazy_static;
- use lib_infra::future::FutureResult;
- use crate::services::persistence::rev_sqlite::{
- SQLiteFolderRevisionPersistence, SQLiteFolderRevisionSnapshotPersistence,
- };
- use crate::services::{clear_current_workspace, get_current_workspace};
- use flowy_client_sync::client_folder::FolderPad;
- use std::convert::TryFrom;
- use std::{collections::HashMap, fmt::Formatter, sync::Arc};
- use tokio::sync::RwLock as TokioRwLock;
- use ws_model::ws_revision::ServerRevisionWSData;
- lazy_static! {
- static ref INIT_FOLDER_FLAG: TokioRwLock<HashMap<String, bool>> =
- TokioRwLock::new(HashMap::new());
- }
- const FOLDER_ID: &str = "folder";
- const FOLDER_ID_SPLIT: &str = ":";
- #[derive(Clone)]
- pub struct FolderId(String);
- impl FolderId {
- pub fn new(user_id: &str) -> Self {
- Self(format!("{}{}{}", user_id, FOLDER_ID_SPLIT, FOLDER_ID))
- }
- }
- impl std::fmt::Display for FolderId {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- f.write_str(FOLDER_ID)
- }
- }
- impl std::fmt::Debug for FolderId {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- f.write_str(FOLDER_ID)
- }
- }
- impl AsRef<str> for FolderId {
- fn as_ref(&self) -> &str {
- &self.0
- }
- }
- pub struct FolderManager {
- pub user: Arc<dyn WorkspaceUser>,
- pub(crate) persistence: Arc<FolderPersistence>,
- pub(crate) workspace_controller: Arc<WorkspaceController>,
- pub(crate) app_controller: Arc<AppController>,
- pub(crate) view_controller: Arc<ViewController>,
- pub(crate) trash_controller: Arc<TrashController>,
- web_socket: Arc<dyn RevisionWebSocket>,
- pub(crate) folder_editor: Arc<TokioRwLock<Option<Arc<FolderEditor>>>>,
- }
- impl FolderManager {
- pub async fn new(
- user: Arc<dyn WorkspaceUser>,
- cloud_service: Arc<dyn FolderCouldServiceV1>,
- database: Arc<dyn WorkspaceDatabase>,
- data_processors: ViewDataProcessorMap,
- web_socket: Arc<dyn RevisionWebSocket>,
- ) -> Self {
- if let Ok(user_id) = user.user_id() {
- // Reset the flag if the folder manager gets initialized, otherwise,
- // the folder_editor will not be initialized after flutter hot reload.
- INIT_FOLDER_FLAG
- .write()
- .await
- .insert(user_id.to_owned(), false);
- }
- let folder_editor = Arc::new(TokioRwLock::new(None));
- let persistence = Arc::new(FolderPersistence::new(
- database.clone(),
- folder_editor.clone(),
- ));
- let trash_controller = Arc::new(TrashController::new(
- persistence.clone(),
- cloud_service.clone(),
- user.clone(),
- ));
- let view_controller = Arc::new(ViewController::new(
- user.clone(),
- persistence.clone(),
- cloud_service.clone(),
- trash_controller.clone(),
- data_processors,
- ));
- let app_controller = Arc::new(AppController::new(
- user.clone(),
- persistence.clone(),
- trash_controller.clone(),
- cloud_service.clone(),
- ));
- let workspace_controller = Arc::new(WorkspaceController::new(
- user.clone(),
- persistence.clone(),
- trash_controller.clone(),
- cloud_service.clone(),
- ));
- Self {
- user,
- persistence,
- workspace_controller,
- app_controller,
- view_controller,
- trash_controller,
- web_socket,
- folder_editor,
- }
- }
- // pub fn network_state_changed(&self, new_type: NetworkType) {
- // match new_type {
- // NetworkType::UnknownNetworkType => {},
- // NetworkType::Wifi => {},
- // NetworkType::Cell => {},
- // NetworkType::Ethernet => {},
- // }
- // }
- pub async fn did_receive_ws_data(&self, data: Bytes) {
- let result = ServerRevisionWSData::try_from(data);
- match result {
- Ok(data) => match self.folder_editor.read().await.clone() {
- None => {},
- Some(editor) => match editor.receive_ws_data(data).await {
- Ok(_) => {},
- Err(e) => tracing::error!("Folder receive data error: {:?}", e),
- },
- },
- Err(e) => {
- tracing::error!("Folder ws data parser failed: {:?}", e);
- },
- }
- }
- /// Called immediately after the application launched with the user sign in/sign up.
- #[tracing::instrument(level = "trace", skip(self), err)]
- pub async fn initialize(&self, user_id: &str, token: &str) -> FlowyResult<()> {
- let mut write_guard = INIT_FOLDER_FLAG.write().await;
- if let Some(is_init) = write_guard.get(user_id) {
- if *is_init {
- return Ok(());
- }
- }
- tracing::debug!("Initialize folder editor");
- let folder_id = FolderId::new(user_id);
- self.persistence.initialize(user_id, &folder_id).await?;
- let pool = self.persistence.db_pool()?;
- let object_id = folder_id.as_ref();
- let disk_cache = SQLiteFolderRevisionPersistence::new(user_id, pool.clone());
- let configuration = RevisionPersistenceConfiguration::new(200, false);
- let rev_persistence = RevisionPersistence::new(user_id, object_id, disk_cache, configuration);
- let rev_compactor = FolderRevisionMergeable();
- const FOLDER_SP_PREFIX: &str = "folder";
- let snapshot_object_id = format!("{}:{}", FOLDER_SP_PREFIX, object_id);
- let snapshot_persistence =
- SQLiteFolderRevisionSnapshotPersistence::new(&snapshot_object_id, pool);
- let rev_manager = RevisionManager::new(
- user_id,
- folder_id.as_ref(),
- rev_persistence,
- rev_compactor,
- snapshot_persistence,
- );
- let folder_editor = FolderEditor::new(
- user_id,
- &folder_id,
- token,
- rev_manager,
- self.web_socket.clone(),
- )
- .await?;
- *self.folder_editor.write().await = Some(Arc::new(folder_editor));
- self.app_controller.initialize()?;
- self.view_controller.initialize()?;
- write_guard.insert(user_id.to_owned(), true);
- Ok(())
- }
- pub async fn get_current_workspace(&self) -> FlowyResult<WorkspacePB> {
- let user_id = self.user.user_id()?;
- let workspace_id = get_current_workspace(&user_id)?;
- let workspace = self
- .persistence
- .begin_transaction(|transaction| {
- self
- .workspace_controller
- .read_workspace(workspace_id, &user_id, &transaction)
- })
- .await?;
- Ok(workspace)
- }
- pub async fn initialize_with_new_user(
- &self,
- user_id: &str,
- token: &str,
- view_data_format: ViewDataFormatPB,
- ) -> FlowyResult<()> {
- DefaultFolderBuilder::build(
- token,
- user_id,
- self.persistence.clone(),
- self.view_controller.clone(),
- || (view_data_format.clone(), Bytes::from(initial_read_me())),
- )
- .await?;
- self.initialize(user_id, token).await
- }
- /// Called when the current user logout
- ///
- pub async fn clear(&self, user_id: &str) {
- self.view_controller.clear_latest_view();
- clear_current_workspace(user_id);
- *self.folder_editor.write().await = None;
- }
- }
- struct DefaultFolderBuilder();
- impl DefaultFolderBuilder {
- async fn build<F: Fn() -> (ViewDataFormatPB, Bytes)>(
- token: &str,
- user_id: &str,
- persistence: Arc<FolderPersistence>,
- view_controller: Arc<ViewController>,
- create_view_fn: F,
- ) -> FlowyResult<()> {
- let workspace_rev = user_default::create_default_workspace();
- tracing::debug!(
- "Create user:{} default workspace:{}",
- user_id,
- workspace_rev.id
- );
- set_current_workspace(user_id, &workspace_rev.id);
- for app in workspace_rev.apps.iter() {
- for (index, view) in app.belongings.iter().enumerate() {
- let (view_data_type, view_data) = create_view_fn();
- if index == 0 {
- let _ = view_controller.set_latest_view(&view.id);
- let layout_type = ViewLayoutTypePB::from(view.layout.clone());
- view_controller
- .create_view(&view.id, &view.name, view_data_type, layout_type, view_data)
- .await?;
- }
- }
- }
- let folder = FolderPad::new(vec![workspace_rev.clone()], vec![])?;
- let folder_id = FolderId::new(user_id);
- persistence.save_folder(user_id, &folder_id, folder).await?;
- let repeated_workspace = RepeatedWorkspacePB {
- items: vec![workspace_rev.into()],
- };
- send_notification(token, FolderNotification::DidCreateWorkspace)
- .payload(repeated_workspace)
- .send();
- Ok(())
- }
- }
- pub trait ViewDataProcessor {
- /// Closes the view and releases the resources that this view has in
- /// the backend
- fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError>;
- /// Gets the data of the this view.
- /// For example, the data can be used to duplicate the view.
- fn get_view_data(&self, view: &ViewPB) -> FutureResult<Bytes, FlowyError>;
- /// Create a view with the pre-defined data.
- /// For example, the initial data of the grid/calendar/kanban board when
- /// you create a new view.
- fn create_view_with_build_in_data(
- &self,
- user_id: &str,
- view_id: &str,
- name: &str,
- layout: ViewLayoutTypePB,
- data_format: ViewDataFormatPB,
- ext: HashMap<String, String>,
- ) -> FutureResult<(), FlowyError>;
- /// Create a view with custom data
- fn create_view_with_custom_data(
- &self,
- user_id: &str,
- view_id: &str,
- name: &str,
- data: Vec<u8>,
- layout: ViewLayoutTypePB,
- ext: HashMap<String, String>,
- ) -> FutureResult<(), FlowyError>;
- fn data_types(&self) -> Vec<ViewDataFormatPB>;
- }
- pub type ViewDataProcessorMap =
- Arc<HashMap<ViewDataFormatPB, Arc<dyn ViewDataProcessor + Send + Sync>>>;
|