mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: checking workspace state consistent after switching workspace (#5201)
* refactor: getting workspace id * refactor: check workspace id is match for http response * refactor: check http repsonse in valid by checing the workspace id * chore: update log * chore: fix test * chore: fix test * chore: add test * chore: update test
This commit is contained in:
@ -11,6 +11,9 @@ pub mod view_operation;
|
||||
|
||||
mod manager_init;
|
||||
mod manager_observer;
|
||||
#[cfg(debug_assertions)]
|
||||
pub mod manager_test_util;
|
||||
|
||||
pub mod share;
|
||||
#[cfg(feature = "test_helper")]
|
||||
mod test_helper;
|
||||
|
@ -21,34 +21,28 @@ use collab::core::collab::{DataSource, MutexCollab};
|
||||
use collab_entity::CollabType;
|
||||
use collab_folder::error::FolderError;
|
||||
use collab_folder::{
|
||||
Folder, FolderData, FolderNotify, Section, SectionItem, TrashInfo, UserId, View, ViewLayout,
|
||||
ViewUpdate, Workspace,
|
||||
Folder, FolderNotify, Section, SectionItem, TrashInfo, UserId, View, ViewLayout, ViewUpdate,
|
||||
Workspace,
|
||||
};
|
||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
|
||||
use collab_integrate::{CollabKVDB, CollabPersistenceConfig};
|
||||
use collab_integrate::CollabKVDB;
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_folder_pub::cloud::{gen_view_id, FolderCloudService};
|
||||
use flowy_folder_pub::folder_builder::ParentChildViews;
|
||||
use flowy_search_pub::entities::FolderIndexManager;
|
||||
use lib_infra::conditional_send_sync_trait;
|
||||
use parking_lot::RwLock;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{error, info, instrument};
|
||||
|
||||
conditional_send_sync_trait! {
|
||||
"[crate::manager::FolderUser] represents the user for folder.";
|
||||
FolderUser {
|
||||
fn user_id(&self) -> Result<i64, FlowyError>;
|
||||
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
|
||||
}
|
||||
pub trait FolderUser: Send + Sync {
|
||||
fn user_id(&self) -> Result<i64, FlowyError>;
|
||||
fn workspace_id(&self) -> Result<String, FlowyError>;
|
||||
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
|
||||
}
|
||||
|
||||
pub struct FolderManager {
|
||||
/// workspace_id represents as the id of the Folder.
|
||||
pub(crate) workspace_id: RwLock<Option<String>>,
|
||||
|
||||
/// MutexFolder is the folder that is used to store the data.
|
||||
pub(crate) mutex_folder: Arc<MutexFolder>,
|
||||
pub(crate) collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||
@ -73,7 +67,6 @@ impl FolderManager {
|
||||
collab_builder,
|
||||
operation_handlers,
|
||||
cloud_service,
|
||||
workspace_id: Default::default(),
|
||||
folder_indexer,
|
||||
};
|
||||
|
||||
@ -82,25 +75,20 @@ impl FolderManager {
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub async fn get_current_workspace(&self) -> FlowyResult<WorkspacePB> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
self.with_folder(
|
||||
|| {
|
||||
let uid = self.user.user_id()?;
|
||||
let workspace_id = self
|
||||
.workspace_id
|
||||
.read()
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.ok_or_else(|| FlowyError::from(ErrorCode::WorkspaceInitializeError))?;
|
||||
Err(workspace_data_not_sync_error(uid, &workspace_id))
|
||||
},
|
||||
|folder| {
|
||||
let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| {
|
||||
let views = get_workspace_public_view_pbs(folder);
|
||||
let views = get_workspace_public_view_pbs(&workspace_id, folder);
|
||||
let workspace: WorkspacePB = (workspace, views).into();
|
||||
Ok::<WorkspacePB, FlowyError>(workspace)
|
||||
};
|
||||
|
||||
match folder.get_current_workspace() {
|
||||
match folder.get_workspace_info(&workspace_id) {
|
||||
None => Err(FlowyError::record_not_found().with_context("Can not find the workspace")),
|
||||
Some(workspace) => workspace_pb_from_workspace(workspace, folder),
|
||||
}
|
||||
@ -111,28 +99,25 @@ impl FolderManager {
|
||||
/// Return a list of views of the current workspace.
|
||||
/// Only the first level of child views are included.
|
||||
pub async fn get_current_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> {
|
||||
let workspace_id = self
|
||||
.mutex_folder
|
||||
.read()
|
||||
.as_ref()
|
||||
.map(|folder| folder.get_workspace_id());
|
||||
|
||||
if workspace_id.is_some() {
|
||||
self.get_workspace_public_views().await
|
||||
} else {
|
||||
tracing::warn!("Can't get the workspace id from the folder. Return empty list.");
|
||||
Ok(vec![])
|
||||
}
|
||||
let views = self.get_workspace_public_views().await?;
|
||||
Ok(views)
|
||||
}
|
||||
|
||||
pub async fn get_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> {
|
||||
Ok(self.with_folder(Vec::new, get_workspace_public_view_pbs))
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
Ok(self.with_folder(Vec::new, |folder| {
|
||||
get_workspace_public_view_pbs(&workspace_id, folder)
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn get_workspace_private_views(&self) -> FlowyResult<Vec<ViewPB>> {
|
||||
Ok(self.with_folder(Vec::new, get_workspace_private_view_pbs))
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
Ok(self.with_folder(Vec::new, |folder| {
|
||||
get_workspace_private_view_pbs(&workspace_id, folder)
|
||||
}))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all, err)]
|
||||
pub(crate) async fn make_folder<T: Into<Option<FolderNotify>>>(
|
||||
&self,
|
||||
uid: i64,
|
||||
@ -142,11 +127,6 @@ impl FolderManager {
|
||||
folder_notifier: T,
|
||||
) -> Result<Folder, FlowyError> {
|
||||
let folder_notifier = folder_notifier.into();
|
||||
// snapshot_config will be deprecated in the future.
|
||||
let snapshot_config = CollabPersistenceConfig::new()
|
||||
.enable_snapshot(true)
|
||||
.snapshot_per_update(50);
|
||||
|
||||
// only need the check the workspace id when the doc state is not from the disk.
|
||||
let should_check_workspace_id = !matches!(doc_state, DataSource::Disk);
|
||||
let should_auto_initialize = !should_check_workspace_id;
|
||||
@ -154,13 +134,14 @@ impl FolderManager {
|
||||
.sync_enable(true)
|
||||
.auto_initialize(should_auto_initialize);
|
||||
|
||||
let object_id = workspace_id;
|
||||
let collab = self.collab_builder.build_with_config(
|
||||
uid,
|
||||
workspace_id,
|
||||
uid,
|
||||
object_id,
|
||||
CollabType::Folder,
|
||||
collab_db,
|
||||
doc_state,
|
||||
snapshot_config,
|
||||
config,
|
||||
)?;
|
||||
let (should_clear, err) = match Folder::open(UserId::from(uid), collab.clone(), folder_notifier)
|
||||
@ -172,7 +153,7 @@ impl FolderManager {
|
||||
let folder_workspace_id = folder.get_workspace_id();
|
||||
if folder_workspace_id != workspace_id {
|
||||
error!(
|
||||
"expected workspace id: {}, actual workspace id: {}",
|
||||
"expect workspace_id: {}, actual workspace_id: {}",
|
||||
workspace_id, folder_workspace_id
|
||||
);
|
||||
return Err(FlowyError::workspace_data_not_match());
|
||||
@ -203,15 +184,14 @@ impl FolderManager {
|
||||
workspace_id: &str,
|
||||
collab_db: Weak<CollabKVDB>,
|
||||
) -> Result<Arc<MutexCollab>, FlowyError> {
|
||||
let object_id = workspace_id;
|
||||
let collab = self.collab_builder.build_with_config(
|
||||
uid,
|
||||
workspace_id,
|
||||
uid,
|
||||
object_id,
|
||||
CollabType::Folder,
|
||||
collab_db,
|
||||
DataSource::Disk,
|
||||
CollabPersistenceConfig::new()
|
||||
.enable_snapshot(true)
|
||||
.snapshot_per_update(50),
|
||||
CollabBuilderConfig::default().sync_enable(true),
|
||||
)?;
|
||||
Ok(collab)
|
||||
@ -220,19 +200,17 @@ impl FolderManager {
|
||||
/// Initialize the folder with the given workspace id.
|
||||
/// Fetch the folder updates from the cloud service and initialize the folder.
|
||||
#[tracing::instrument(skip(self, user_id), err)]
|
||||
pub async fn initialize_with_workspace_id(
|
||||
&self,
|
||||
user_id: i64,
|
||||
workspace_id: &str,
|
||||
) -> FlowyResult<()> {
|
||||
pub async fn initialize_with_workspace_id(&self, user_id: i64) -> FlowyResult<()> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let object_id = &workspace_id;
|
||||
let folder_doc_state = self
|
||||
.cloud_service
|
||||
.get_folder_doc_state(workspace_id, user_id, CollabType::Folder, workspace_id)
|
||||
.get_folder_doc_state(&workspace_id, user_id, CollabType::Folder, object_id)
|
||||
.await?;
|
||||
if let Err(err) = self
|
||||
.initialize(
|
||||
user_id,
|
||||
workspace_id,
|
||||
&workspace_id,
|
||||
FolderInitDataSource::Cloud(folder_doc_state),
|
||||
)
|
||||
.await
|
||||
@ -243,7 +221,7 @@ impl FolderManager {
|
||||
self
|
||||
.initialize(
|
||||
user_id,
|
||||
workspace_id,
|
||||
&workspace_id,
|
||||
FolderInitDataSource::LocalDisk {
|
||||
create_if_not_exist: false,
|
||||
},
|
||||
@ -278,17 +256,17 @@ impl FolderManager {
|
||||
.map_err(FlowyError::from);
|
||||
|
||||
match result {
|
||||
Ok(folder_updates) => {
|
||||
Ok(folder_doc_state) => {
|
||||
info!(
|
||||
"Get folder updates via {}, doc state len: {}",
|
||||
self.cloud_service.service_name(),
|
||||
folder_updates.len()
|
||||
folder_doc_state.len()
|
||||
);
|
||||
self
|
||||
.initialize(
|
||||
user_id,
|
||||
workspace_id,
|
||||
FolderInitDataSource::Cloud(folder_updates),
|
||||
FolderInitDataSource::Cloud(folder_doc_state),
|
||||
)
|
||||
.await?;
|
||||
},
|
||||
@ -318,12 +296,8 @@ impl FolderManager {
|
||||
Ok(new_workspace)
|
||||
}
|
||||
|
||||
pub async fn get_workspace(&self, _workspace_id: &str) -> Option<Workspace> {
|
||||
self.with_folder(|| None, |folder| folder.get_current_workspace())
|
||||
}
|
||||
|
||||
pub async fn get_workspace_setting_pb(&self) -> FlowyResult<WorkspaceSettingPB> {
|
||||
let workspace_id = self.get_current_workspace_id().await?;
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let latest_view = self.get_current_view().await;
|
||||
Ok(WorkspaceSettingPB {
|
||||
workspace_id,
|
||||
@ -349,40 +323,29 @@ impl FolderManager {
|
||||
}
|
||||
|
||||
pub async fn get_workspace_pb(&self) -> FlowyResult<WorkspacePB> {
|
||||
let workspace_pb = {
|
||||
let guard = self.mutex_folder.read();
|
||||
let folder = guard
|
||||
.as_ref()
|
||||
.ok_or(FlowyError::internal().with_context("folder is not initialized"))?;
|
||||
let workspace = folder.get_current_workspace().ok_or(
|
||||
FlowyError::record_not_found().with_context("Can't find the current workspace id "),
|
||||
)?;
|
||||
|
||||
let views = folder
|
||||
.views
|
||||
.get_views_belong_to(&workspace.id)
|
||||
.into_iter()
|
||||
.map(|view| view_pb_without_child_views(view.as_ref().clone()))
|
||||
.collect::<Vec<ViewPB>>();
|
||||
|
||||
WorkspacePB {
|
||||
id: workspace.id,
|
||||
name: workspace.name,
|
||||
views,
|
||||
create_time: workspace.created_at,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(workspace_pb)
|
||||
}
|
||||
|
||||
async fn get_current_workspace_id(&self) -> FlowyResult<String> {
|
||||
self
|
||||
.mutex_folder
|
||||
.read()
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let guard = self.mutex_folder.read();
|
||||
let folder = guard
|
||||
.as_ref()
|
||||
.map(|folder| folder.get_workspace_id())
|
||||
.ok_or(FlowyError::internal().with_context("Unexpected empty workspace id"))
|
||||
.ok_or(FlowyError::internal().with_context("folder is not initialized"))?;
|
||||
let workspace = folder
|
||||
.get_workspace_info(&workspace_id)
|
||||
.ok_or_else(|| FlowyError::record_not_found().with_context("Can not find the workspace"))?;
|
||||
|
||||
let views = folder
|
||||
.views
|
||||
.get_views_belong_to(&workspace.id)
|
||||
.into_iter()
|
||||
.map(|view| view_pb_without_child_views(view.as_ref().clone()))
|
||||
.collect::<Vec<ViewPB>>();
|
||||
drop(guard);
|
||||
|
||||
Ok(WorkspacePB {
|
||||
id: workspace.id,
|
||||
name: workspace.name,
|
||||
views,
|
||||
create_time: workspace.created_at,
|
||||
})
|
||||
}
|
||||
|
||||
/// This function acquires a lock on the `mutex_folder` and checks its state.
|
||||
@ -404,17 +367,8 @@ impl FolderManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_all_workspaces(&self) -> Vec<Workspace> {
|
||||
self.with_folder(Vec::new, |folder| {
|
||||
let mut workspaces = vec![];
|
||||
if let Some(workspace) = folder.get_current_workspace() {
|
||||
workspaces.push(workspace);
|
||||
}
|
||||
workspaces
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create_view_with_params(&self, params: CreateViewParams) -> FlowyResult<View> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let view_layout: ViewLayout = params.layout.clone().into();
|
||||
let handler = self.get_handler(&view_layout)?;
|
||||
let user_id = self.user.user_id()?;
|
||||
@ -453,11 +407,9 @@ impl FolderManager {
|
||||
},
|
||||
);
|
||||
|
||||
if let Ok(workspace_id) = self.get_current_workspace_id().await {
|
||||
let folder = &self.mutex_folder.read();
|
||||
if let Some(folder) = folder.as_ref() {
|
||||
notify_did_update_workspace(&workspace_id, folder);
|
||||
}
|
||||
let folder = &self.mutex_folder.read();
|
||||
if let Some(folder) = folder.as_ref() {
|
||||
notify_did_update_workspace(&workspace_id, folder);
|
||||
}
|
||||
|
||||
Ok(view)
|
||||
@ -659,6 +611,7 @@ impl FolderManager {
|
||||
///
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub async fn move_nested_view(&self, params: MoveNestedViewParams) -> FlowyResult<()> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let view_id = params.view_id;
|
||||
let new_parent_id = params.new_parent_id;
|
||||
let prev_view_id = params.prev_view_id;
|
||||
@ -681,6 +634,7 @@ impl FolderManager {
|
||||
},
|
||||
);
|
||||
notify_parent_view_did_change(
|
||||
&workspace_id,
|
||||
self.mutex_folder.clone(),
|
||||
vec![new_parent_id, old_parent_id],
|
||||
);
|
||||
@ -693,6 +647,7 @@ impl FolderManager {
|
||||
/// We need to convert the index to the real index of the view in the parent view.
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub async fn move_view(&self, view_id: &str, from: usize, to: usize) -> FlowyResult<()> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
if let Some((is_workspace, parent_view_id, child_views)) = self.get_view_relation(view_id).await
|
||||
{
|
||||
// The display parent view is the view that is displayed in the UI
|
||||
@ -729,7 +684,11 @@ impl FolderManager {
|
||||
folder.move_view(view_id, actual_from_index as u32, actual_to_index as u32);
|
||||
},
|
||||
);
|
||||
notify_parent_view_did_change(self.mutex_folder.clone(), vec![parent_view_id]);
|
||||
notify_parent_view_did_change(
|
||||
&workspace_id,
|
||||
self.mutex_folder.clone(),
|
||||
vec![parent_view_id],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -819,12 +778,12 @@ impl FolderManager {
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub(crate) async fn set_current_view(&self, view_id: &str) -> Result<(), FlowyError> {
|
||||
let workspace_id = self.with_folder(
|
||||
self.with_folder(
|
||||
|| Err(FlowyError::record_not_found()),
|
||||
|folder| {
|
||||
folder.set_current_view(view_id);
|
||||
folder.add_recent_view_ids(vec![view_id.to_string()]);
|
||||
Ok(folder.get_workspace_id())
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
@ -836,6 +795,7 @@ impl FolderManager {
|
||||
}
|
||||
}
|
||||
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
send_workspace_setting_notification(workspace_id, view);
|
||||
Ok(())
|
||||
}
|
||||
@ -992,6 +952,7 @@ impl FolderManager {
|
||||
}
|
||||
|
||||
pub(crate) async fn import(&self, import_data: ImportParams) -> FlowyResult<View> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
if import_data.data.is_none() && import_data.file_path.is_none() {
|
||||
return Err(FlowyError::new(
|
||||
ErrorCode::InvalidParams,
|
||||
@ -1040,7 +1001,11 @@ impl FolderManager {
|
||||
folder.insert_view(view.clone(), None);
|
||||
},
|
||||
);
|
||||
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.parent_view_id.clone()]);
|
||||
notify_parent_view_did_change(
|
||||
&workspace_id,
|
||||
self.mutex_folder.clone(),
|
||||
vec![view.parent_view_id.clone()],
|
||||
);
|
||||
Ok(view)
|
||||
}
|
||||
|
||||
@ -1049,6 +1014,7 @@ impl FolderManager {
|
||||
where
|
||||
F: FnOnce(ViewUpdate) -> Option<View>,
|
||||
{
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let value = self.with_folder(
|
||||
|| None,
|
||||
|folder| {
|
||||
@ -1070,11 +1036,9 @@ impl FolderManager {
|
||||
.payload(view_pb)
|
||||
.send();
|
||||
|
||||
if let Ok(workspace_id) = self.get_current_workspace_id().await {
|
||||
let folder = &self.mutex_folder.read();
|
||||
if let Some(folder) = folder.as_ref() {
|
||||
notify_did_update_workspace(&workspace_id, folder);
|
||||
}
|
||||
let folder = &self.mutex_folder.read();
|
||||
if let Some(folder) = folder.as_ref() {
|
||||
notify_did_update_workspace(&workspace_id, folder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1100,12 +1064,13 @@ impl FolderManager {
|
||||
/// Otherwise, the parent_view_id is the parent view id of the view. The child_view_ids is the
|
||||
/// child view ids of the view.
|
||||
async fn get_view_relation(&self, view_id: &str) -> Option<(bool, String, Vec<String>)> {
|
||||
let workspace_id = self.user.workspace_id().ok()?;
|
||||
self.with_folder(
|
||||
|| None,
|
||||
|folder| {
|
||||
let view = folder.views.get_view(view_id)?;
|
||||
match folder.views.get_view(&view.parent_view_id) {
|
||||
None => folder.get_current_workspace().map(|workspace| {
|
||||
None => folder.get_workspace_info(&workspace_id).map(|workspace| {
|
||||
(
|
||||
true,
|
||||
workspace.id,
|
||||
@ -1154,18 +1119,6 @@ impl FolderManager {
|
||||
Ok(snapshots)
|
||||
}
|
||||
|
||||
/// Only expose this method for testing
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn get_mutex_folder(&self) -> &Arc<MutexFolder> {
|
||||
&self.mutex_folder
|
||||
}
|
||||
|
||||
/// Only expose this method for testing
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn get_cloud_service(&self) -> &Arc<dyn FolderCloudService> {
|
||||
&self.cloud_service
|
||||
}
|
||||
|
||||
pub fn set_views_visibility(&self, view_ids: Vec<String>, is_public: bool) {
|
||||
self.with_folder(
|
||||
|| (),
|
||||
@ -1239,7 +1192,7 @@ impl FolderManager {
|
||||
}
|
||||
|
||||
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.
|
||||
pub(crate) fn get_workspace_public_view_pbs(folder: &Folder) -> Vec<ViewPB> {
|
||||
pub(crate) fn get_workspace_public_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
||||
// get the trash ids
|
||||
let trash_ids = folder
|
||||
.get_all_trash_sections()
|
||||
@ -1254,8 +1207,7 @@ pub(crate) fn get_workspace_public_view_pbs(folder: &Folder) -> Vec<ViewPB> {
|
||||
.map(|view| view.id)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let mut views = folder.get_workspace_views();
|
||||
|
||||
let mut views = folder.views.get_views_belong_to(workspace_id);
|
||||
// filter the views that are in the trash and all the private views
|
||||
views.retain(|view| !trash_ids.contains(&view.id) && !private_view_ids.contains(&view.id));
|
||||
|
||||
@ -1289,7 +1241,7 @@ fn get_all_child_view_ids(folder: &Folder, view_id: &str) -> Vec<String> {
|
||||
}
|
||||
|
||||
/// Get the current private views of the user.
|
||||
pub(crate) fn get_workspace_private_view_pbs(folder: &Folder) -> Vec<ViewPB> {
|
||||
pub(crate) fn get_workspace_private_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
||||
// get the trash ids
|
||||
let trash_ids = folder
|
||||
.get_all_trash_sections()
|
||||
@ -1304,8 +1256,7 @@ pub(crate) fn get_workspace_private_view_pbs(folder: &Folder) -> Vec<ViewPB> {
|
||||
.map(|view| view.id)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let mut views = folder.get_workspace_views();
|
||||
|
||||
let mut views = folder.views.get_views_belong_to(workspace_id);
|
||||
// filter the views that are in the trash and not in the private view ids
|
||||
views.retain(|view| !trash_ids.contains(&view.id) && private_view_ids.contains(&view.id));
|
||||
|
||||
@ -1342,8 +1293,6 @@ pub enum FolderInitDataSource {
|
||||
LocalDisk { create_if_not_exist: bool },
|
||||
/// If there is no data stored on local disk, we will use the data from the server to initialize the folder
|
||||
Cloud(Vec<u8>),
|
||||
/// If the user is new, we use the [DefaultFolderBuilder] to create the default folder.
|
||||
FolderData(FolderData),
|
||||
}
|
||||
|
||||
impl Display for FolderInitDataSource {
|
||||
@ -1351,7 +1300,6 @@ impl Display for FolderInitDataSource {
|
||||
match self {
|
||||
FolderInitDataSource::LocalDisk { .. } => f.write_fmt(format_args!("LocalDisk")),
|
||||
FolderInitDataSource::Cloud(_) => f.write_fmt(format_args!("Cloud")),
|
||||
FolderInitDataSource::FolderData(_) => f.write_fmt(format_args!("Custom FolderData")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,14 @@
|
||||
use collab_entity::CollabType;
|
||||
|
||||
use collab_folder::{Folder, FolderNotify, UserId};
|
||||
use tokio::task::spawn_blocking;
|
||||
use tracing::{event, Level};
|
||||
|
||||
use collab_integrate::CollabKVDB;
|
||||
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
|
||||
use collab::core::collab::DataSource;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use crate::manager::{FolderInitDataSource, FolderManager};
|
||||
use crate::manager_observer::{
|
||||
subscribe_folder_snapshot_state_changed, subscribe_folder_sync_state_changed,
|
||||
subscribe_folder_trash_changed, subscribe_folder_view_changed,
|
||||
};
|
||||
use crate::manager_observer::*;
|
||||
use crate::user_default::DefaultFolderBuilder;
|
||||
use collab::core::collab::DataSource;
|
||||
use collab_entity::CollabType;
|
||||
use collab_folder::{Folder, FolderNotify, UserId};
|
||||
use collab_integrate::CollabKVDB;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::task::spawn_blocking;
|
||||
use tracing::{event, info, Level};
|
||||
|
||||
impl FolderManager {
|
||||
/// Called immediately after the application launched if the user already sign in/sign up.
|
||||
@ -34,9 +26,13 @@ impl FolderManager {
|
||||
workspace_id,
|
||||
initial_data
|
||||
);
|
||||
*self.workspace_id.write() = Some(workspace_id.to_string());
|
||||
let workspace_id = workspace_id.to_string();
|
||||
|
||||
if let Some(old_folder) = self.mutex_folder.write().take() {
|
||||
old_folder.close();
|
||||
info!("remove old folder: {}", old_folder.get_workspace_id());
|
||||
}
|
||||
|
||||
let workspace_id = workspace_id.to_string();
|
||||
// Get the collab db for the user with given user id.
|
||||
let collab_db = self.user.collab_db(uid)?;
|
||||
|
||||
@ -115,22 +111,6 @@ impl FolderManager {
|
||||
.await?
|
||||
}
|
||||
},
|
||||
FolderInitDataSource::FolderData(folder_data) => {
|
||||
if folder_data.workspace.id != workspace_id {
|
||||
return Err(FlowyError::workspace_data_not_match());
|
||||
}
|
||||
|
||||
event!(Level::INFO, "Restore folder with passed-in folder data");
|
||||
let collab = self
|
||||
.create_empty_collab(uid, &workspace_id, collab_db)
|
||||
.await?;
|
||||
Folder::create(
|
||||
UserId::from(uid),
|
||||
collab,
|
||||
Some(folder_notifier),
|
||||
folder_data,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
let folder_state_rx = folder.subscribe_sync_state();
|
||||
@ -154,10 +134,28 @@ impl FolderManager {
|
||||
*self.mutex_folder.write() = Some(folder);
|
||||
|
||||
let weak_mutex_folder = Arc::downgrade(&self.mutex_folder);
|
||||
subscribe_folder_sync_state_changed(workspace_id.clone(), folder_state_rx, &weak_mutex_folder);
|
||||
subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder);
|
||||
subscribe_folder_trash_changed(section_change_rx, &weak_mutex_folder);
|
||||
subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
|
||||
subscribe_folder_sync_state_changed(
|
||||
workspace_id.clone(),
|
||||
folder_state_rx,
|
||||
Arc::downgrade(&self.user),
|
||||
);
|
||||
subscribe_folder_snapshot_state_changed(
|
||||
workspace_id.clone(),
|
||||
&weak_mutex_folder,
|
||||
Arc::downgrade(&self.user),
|
||||
);
|
||||
subscribe_folder_trash_changed(
|
||||
workspace_id.clone(),
|
||||
section_change_rx,
|
||||
&weak_mutex_folder,
|
||||
Arc::downgrade(&self.user),
|
||||
);
|
||||
subscribe_folder_view_changed(
|
||||
workspace_id.clone(),
|
||||
view_rx,
|
||||
&weak_mutex_folder,
|
||||
Arc::downgrade(&self.user),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,43 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use crate::entities::{
|
||||
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
|
||||
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
|
||||
};
|
||||
use crate::manager::{
|
||||
get_workspace_private_view_pbs, get_workspace_public_view_pbs, FolderUser, MutexFolder,
|
||||
};
|
||||
use crate::notification::{send_notification, FolderNotification};
|
||||
use collab::core::collab_state::SyncState;
|
||||
use collab_folder::{
|
||||
Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange,
|
||||
ViewChangeReceiver,
|
||||
};
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::{event, Level};
|
||||
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use crate::entities::{
|
||||
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
|
||||
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
|
||||
};
|
||||
use crate::manager::{get_workspace_private_view_pbs, get_workspace_public_view_pbs, MutexFolder};
|
||||
use crate::notification::{send_notification, FolderNotification};
|
||||
use tracing::{event, trace, Level};
|
||||
|
||||
/// Listen on the [ViewChange] after create/delete/update events happened
|
||||
pub(crate) fn subscribe_folder_view_changed(
|
||||
workspace_id: String,
|
||||
mut rx: ViewChangeReceiver,
|
||||
weak_mutex_folder: &Weak<MutexFolder>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
let weak_mutex_folder = weak_mutex_folder.clone();
|
||||
af_spawn(async move {
|
||||
while let Ok(value) = rx.recv().await {
|
||||
if let Some(user) = user.upgrade() {
|
||||
if let Ok(actual_workspace_id) = user.workspace_id() {
|
||||
if actual_workspace_id != workspace_id {
|
||||
trace!("Did break the loop when the workspace id is not matched");
|
||||
// break the loop when the workspace id is not matched.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(folder) = weak_mutex_folder.upgrade() {
|
||||
tracing::trace!("Did receive view change: {:?}", value);
|
||||
match value {
|
||||
@ -35,7 +46,7 @@ pub(crate) fn subscribe_folder_view_changed(
|
||||
view_pb_without_child_views(view.clone()),
|
||||
ChildViewChangeReason::Create,
|
||||
);
|
||||
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
|
||||
notify_parent_view_did_change(&workspace_id, folder.clone(), vec![view.parent_view_id]);
|
||||
},
|
||||
ViewChange::DidDeleteView { views } => {
|
||||
for view in views {
|
||||
@ -51,7 +62,11 @@ pub(crate) fn subscribe_folder_view_changed(
|
||||
view_pb_without_child_views(view.clone()),
|
||||
ChildViewChangeReason::Update,
|
||||
);
|
||||
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id.clone()]);
|
||||
notify_parent_view_did_change(
|
||||
&workspace_id,
|
||||
folder.clone(),
|
||||
vec![view.parent_view_id.clone()],
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -62,6 +77,7 @@ pub(crate) fn subscribe_folder_view_changed(
|
||||
pub(crate) fn subscribe_folder_snapshot_state_changed(
|
||||
workspace_id: String,
|
||||
weak_mutex_folder: &Weak<MutexFolder>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
let weak_mutex_folder = weak_mutex_folder.clone();
|
||||
af_spawn(async move {
|
||||
@ -72,6 +88,14 @@ pub(crate) fn subscribe_folder_snapshot_state_changed(
|
||||
.map(|folder| folder.subscribe_snapshot_state());
|
||||
if let Some(mut state_stream) = stream {
|
||||
while let Some(snapshot_state) = state_stream.next().await {
|
||||
if let Some(user) = user.upgrade() {
|
||||
if let Ok(actual_workspace_id) = user.workspace_id() {
|
||||
if actual_workspace_id != workspace_id {
|
||||
// break the loop when the workspace id is not matched.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(new_snapshot_id) = snapshot_state.snapshot_id() {
|
||||
tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id);
|
||||
send_notification(
|
||||
@ -90,10 +114,19 @@ pub(crate) fn subscribe_folder_snapshot_state_changed(
|
||||
pub(crate) fn subscribe_folder_sync_state_changed(
|
||||
workspace_id: String,
|
||||
mut folder_sync_state_rx: WatchStream<SyncState>,
|
||||
_weak_mutex_folder: &Weak<MutexFolder>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
af_spawn(async move {
|
||||
while let Some(state) = folder_sync_state_rx.next().await {
|
||||
if let Some(user) = user.upgrade() {
|
||||
if let Ok(actual_workspace_id) = user.workspace_id() {
|
||||
if actual_workspace_id != workspace_id {
|
||||
// break the loop when the workspace id is not matched.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
|
||||
.payload(FolderSyncStatePB::from(state))
|
||||
.send();
|
||||
@ -103,12 +136,23 @@ pub(crate) fn subscribe_folder_sync_state_changed(
|
||||
|
||||
/// Listen on the [TrashChange]s and notify the frontend some views were changed.
|
||||
pub(crate) fn subscribe_folder_trash_changed(
|
||||
workspace_id: String,
|
||||
mut rx: SectionChangeReceiver,
|
||||
weak_mutex_folder: &Weak<MutexFolder>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
let weak_mutex_folder = weak_mutex_folder.clone();
|
||||
af_spawn(async move {
|
||||
while let Ok(value) = rx.recv().await {
|
||||
if let Some(user) = user.upgrade() {
|
||||
if let Ok(actual_workspace_id) = user.workspace_id() {
|
||||
if actual_workspace_id != workspace_id {
|
||||
// break the loop when the workspace id is not matched.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(folder) = weak_mutex_folder.upgrade() {
|
||||
let mut unique_ids = HashSet::new();
|
||||
tracing::trace!("Did receive trash change: {:?}", value);
|
||||
@ -132,7 +176,7 @@ pub(crate) fn subscribe_folder_trash_changed(
|
||||
}
|
||||
|
||||
let parent_view_ids = unique_ids.into_iter().collect();
|
||||
notify_parent_view_did_change(folder.clone(), parent_view_ids);
|
||||
notify_parent_view_did_change(&workspace_id, folder.clone(), parent_view_ids);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -143,12 +187,12 @@ pub(crate) fn subscribe_folder_trash_changed(
|
||||
/// Notify the list of parent view ids that its child views were changed.
|
||||
#[tracing::instrument(level = "debug", skip(folder, parent_view_ids))]
|
||||
pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
|
||||
workspace_id: &str,
|
||||
folder: Arc<MutexFolder>,
|
||||
parent_view_ids: Vec<T>,
|
||||
) -> Option<()> {
|
||||
let folder = folder.read();
|
||||
let folder = folder.as_ref()?;
|
||||
let workspace_id = folder.get_workspace_id();
|
||||
let trash_ids = folder
|
||||
.get_all_trash_sections()
|
||||
.into_iter()
|
||||
@ -161,8 +205,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
|
||||
// if the view's parent id equal to workspace id. Then it will fetch the current
|
||||
// workspace views. Because the workspace is not a view stored in the views map.
|
||||
if parent_view_id == workspace_id {
|
||||
notify_did_update_workspace(&workspace_id, folder);
|
||||
notify_did_update_section_views(&workspace_id, folder);
|
||||
notify_did_update_workspace(workspace_id, folder);
|
||||
notify_did_update_section_views(workspace_id, folder);
|
||||
} else {
|
||||
// Parent view can contain a list of child views. Currently, only get the first level
|
||||
// child views.
|
||||
@ -183,8 +227,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
|
||||
}
|
||||
|
||||
pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) {
|
||||
let public_views = get_workspace_public_view_pbs(folder);
|
||||
let private_views = get_workspace_private_view_pbs(folder);
|
||||
let public_views = get_workspace_public_view_pbs(workspace_id, folder);
|
||||
let private_views = get_workspace_private_view_pbs(workspace_id, folder);
|
||||
tracing::trace!(
|
||||
"Did update section views: public len = {}, private len = {}",
|
||||
public_views.len(),
|
||||
@ -210,7 +254,7 @@ pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folde
|
||||
}
|
||||
|
||||
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
|
||||
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(folder).into();
|
||||
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(workspace_id, folder).into();
|
||||
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
|
||||
.payload(repeated_view)
|
||||
.send();
|
||||
|
32
frontend/rust-lib/flowy-folder/src/manager_test_util.rs
Normal file
32
frontend/rust-lib/flowy-folder/src/manager_test_util.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use crate::manager::{FolderManager, FolderUser, MutexFolder};
|
||||
use crate::view_operation::FolderOperationHandlers;
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use flowy_folder_pub::cloud::FolderCloudService;
|
||||
use flowy_search_pub::entities::FolderIndexManager;
|
||||
use std::sync::Arc;
|
||||
|
||||
impl FolderManager {
|
||||
pub fn get_mutex_folder(&self) -> Arc<MutexFolder> {
|
||||
self.mutex_folder.clone()
|
||||
}
|
||||
|
||||
pub fn get_cloud_service(&self) -> Arc<dyn FolderCloudService> {
|
||||
self.cloud_service.clone()
|
||||
}
|
||||
|
||||
pub fn get_user(&self) -> Arc<dyn FolderUser> {
|
||||
self.user.clone()
|
||||
}
|
||||
|
||||
pub fn get_indexer(&self) -> Arc<dyn FolderIndexManager> {
|
||||
self.folder_indexer.clone()
|
||||
}
|
||||
|
||||
pub fn get_collab_builder(&self) -> Arc<AppFlowyCollabBuilder> {
|
||||
self.collab_builder.clone()
|
||||
}
|
||||
|
||||
pub fn get_operation_handlers(&self) -> FolderOperationHandlers {
|
||||
self.operation_handlers.clone()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user