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:
Nathan.fooo
2024-04-26 09:44:07 +08:00
committed by GitHub
parent 65a289648e
commit cc66147bc0
51 changed files with 980 additions and 575 deletions

View File

@ -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;

View File

@ -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")),
}
}
}

View File

@ -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(())
}

View File

@ -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();

View 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()
}
}