mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: open local folder when fail to init with remote server data (#4158)
This commit is contained in:
parent
781fbf1b30
commit
5ef9d55dca
20
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
20
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -139,7 +139,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -786,7 +786,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -1471,7 +1471,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2830,7 +2830,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2846,7 +2846,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3268,7 +3268,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -5014,7 +5014,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-entity"
|
name = "realtime-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -5036,7 +5036,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-protocol"
|
name = "realtime-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -5783,7 +5783,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared_entity"
|
name = "shared_entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -7673,7 +7673,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "workspace-template"
|
name = "workspace-template"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -57,7 +57,7 @@ custom-protocol = ["tauri/custom-protocol"]
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9589054f3874c60063878f084af01905b182d537" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "69ed6ff4e80ab1476d73f7b587ae61e3e24f8894" }
|
||||||
# Please use the following script to update collab.
|
# Please use the following script to update collab.
|
||||||
# Working directory: frontend
|
# Working directory: frontend
|
||||||
#
|
#
|
||||||
|
20
frontend/rust-lib/Cargo.lock
generated
20
frontend/rust-lib/Cargo.lock
generated
@ -125,7 +125,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -667,7 +667,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -1277,7 +1277,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2471,7 +2471,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2487,7 +2487,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -2848,7 +2848,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -4303,7 +4303,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-entity"
|
name = "realtime-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -4325,7 +4325,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-protocol"
|
name = "realtime-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -4985,7 +4985,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared_entity"
|
name = "shared_entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -6323,7 +6323,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "workspace-template"
|
name = "workspace-template"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9589054f3874c60063878f084af01905b182d537#9589054f3874c60063878f084af01905b182d537"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=69ed6ff4e80ab1476d73f7b587ae61e3e24f8894#69ed6ff4e80ab1476d73f7b587ae61e3e24f8894"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -99,7 +99,7 @@ incremental = false
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9589054f3874c60063878f084af01905b182d537" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "69ed6ff4e80ab1476d73f7b587ae61e3e24f8894" }
|
||||||
# Please use the following script to update collab.
|
# Please use the following script to update collab.
|
||||||
# Working directory: frontend
|
# Working directory: frontend
|
||||||
#
|
#
|
||||||
|
@ -9,6 +9,9 @@ pub mod protobuf;
|
|||||||
mod user_default;
|
mod user_default;
|
||||||
pub mod view_operation;
|
pub mod view_operation;
|
||||||
|
|
||||||
|
mod manager_init;
|
||||||
|
mod manager_observer;
|
||||||
pub mod share;
|
pub mod share;
|
||||||
#[cfg(feature = "test_helper")]
|
#[cfg(feature = "test_helper")]
|
||||||
mod test_helper;
|
mod test_helper;
|
||||||
|
mod util;
|
||||||
|
@ -1,38 +1,34 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use collab::core::collab::{CollabRawData, MutexCollab};
|
use collab::core::collab::{CollabRawData, MutexCollab};
|
||||||
use collab::core::collab_state::SyncState;
|
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use collab_folder::{
|
use collab_folder::{
|
||||||
Folder, FolderData, FolderNotify, Section, SectionItem, TrashChange, TrashChangeReceiver,
|
Folder, FolderData, Section, SectionItem, TrashInfo, View, ViewLayout, ViewUpdate, Workspace,
|
||||||
TrashInfo, UserId, View, ViewChange, ViewChangeReceiver, ViewLayout, ViewUpdate, Workspace,
|
|
||||||
};
|
};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use tokio_stream::wrappers::WatchStream;
|
|
||||||
use tokio_stream::StreamExt;
|
|
||||||
use tracing::{event, info, instrument, Level};
|
use tracing::{event, info, instrument, Level};
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB, YrsDocAction};
|
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
use flowy_folder_deps::cloud::{gen_view_id, FolderCloudService};
|
use flowy_folder_deps::cloud::{gen_view_id, FolderCloudService};
|
||||||
use lib_dispatch::prelude::af_spawn;
|
|
||||||
|
|
||||||
use crate::entities::icon::UpdateViewIconParams;
|
use crate::entities::icon::UpdateViewIconParams;
|
||||||
use crate::entities::{
|
use crate::entities::{
|
||||||
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, CreateViewParams,
|
view_pb_with_child_views, view_pb_without_child_views, CreateViewParams, CreateWorkspaceParams,
|
||||||
CreateWorkspaceParams, DeletedViewPB, FolderSnapshotPB, FolderSnapshotStatePB, FolderSyncStatePB,
|
DeletedViewPB, FolderSnapshotPB, RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB,
|
||||||
RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB, UpdateViewParams, UserFolderPB, ViewPB,
|
UpdateViewParams, ViewPB, WorkspacePB, WorkspaceSettingPB,
|
||||||
WorkspacePB, WorkspaceSettingPB,
|
};
|
||||||
|
use crate::manager_observer::{
|
||||||
|
notify_child_views_changed, notify_parent_view_did_change, ChildViewChangeReason,
|
||||||
};
|
};
|
||||||
use crate::notification::{
|
use crate::notification::{
|
||||||
send_notification, send_workspace_setting_notification, FolderNotification,
|
send_notification, send_workspace_setting_notification, FolderNotification,
|
||||||
};
|
};
|
||||||
use crate::share::ImportParams;
|
use crate::share::ImportParams;
|
||||||
use crate::user_default::DefaultFolderBuilder;
|
use crate::util::{folder_not_init_error, workspace_data_not_sync_error};
|
||||||
use crate::view_operation::{create_view, FolderOperationHandler, FolderOperationHandlers};
|
use crate::view_operation::{create_view, FolderOperationHandler, FolderOperationHandlers};
|
||||||
|
|
||||||
/// [FolderUser] represents the user for folder.
|
/// [FolderUser] represents the user for folder.
|
||||||
@ -43,11 +39,11 @@ pub trait FolderUser: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct FolderManager {
|
pub struct FolderManager {
|
||||||
workspace_id: RwLock<Option<String>>,
|
pub(crate) workspace_id: RwLock<Option<String>>,
|
||||||
mutex_folder: Arc<MutexFolder>,
|
pub(crate) mutex_folder: Arc<MutexFolder>,
|
||||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||||
user: Arc<dyn FolderUser>,
|
pub(crate) user: Arc<dyn FolderUser>,
|
||||||
operation_handlers: FolderOperationHandlers,
|
pub(crate) operation_handlers: FolderOperationHandlers,
|
||||||
pub cloud_service: Arc<dyn FolderCloudService>,
|
pub cloud_service: Arc<dyn FolderCloudService>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,105 +121,7 @@ impl FolderManager {
|
|||||||
Ok(views)
|
Ok(views)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called immediately after the application launched if the user already sign in/sign up.
|
pub(crate) async fn collab_for_folder(
|
||||||
#[tracing::instrument(level = "info", skip(self, initial_data), err)]
|
|
||||||
pub async fn initialize(
|
|
||||||
&self,
|
|
||||||
uid: i64,
|
|
||||||
workspace_id: &str,
|
|
||||||
initial_data: FolderInitDataSource,
|
|
||||||
) -> FlowyResult<()> {
|
|
||||||
// Update the workspace id
|
|
||||||
event!(
|
|
||||||
Level::INFO,
|
|
||||||
"Init workspace: {} from: {}",
|
|
||||||
workspace_id,
|
|
||||||
initial_data
|
|
||||||
);
|
|
||||||
*self.workspace_id.write() = Some(workspace_id.to_string());
|
|
||||||
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)?;
|
|
||||||
|
|
||||||
let (view_tx, view_rx) = tokio::sync::broadcast::channel(100);
|
|
||||||
let (trash_tx, trash_rx) = tokio::sync::broadcast::channel(100);
|
|
||||||
let folder_notifier = FolderNotify {
|
|
||||||
view_change_tx: view_tx,
|
|
||||||
trash_change_tx: trash_tx,
|
|
||||||
};
|
|
||||||
|
|
||||||
let folder = match initial_data {
|
|
||||||
FolderInitDataSource::LocalDisk {
|
|
||||||
create_if_not_exist,
|
|
||||||
} => {
|
|
||||||
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
|
|
||||||
if is_exist {
|
|
||||||
event!(Level::INFO, "Init folder from local disk");
|
|
||||||
let collab = self
|
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
|
||||||
.await?;
|
|
||||||
Folder::open(UserId::from(uid), collab, Some(folder_notifier))?
|
|
||||||
} else if create_if_not_exist {
|
|
||||||
// Currently, this branch is only used when the server type is supabase. For appflowy cloud,
|
|
||||||
// the default workspace is already created when the user sign up.
|
|
||||||
event!(Level::INFO, "Create folder with default folder builder");
|
|
||||||
let folder_data =
|
|
||||||
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let collab = self
|
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
|
||||||
.await?;
|
|
||||||
Folder::create(
|
|
||||||
UserId::from(uid),
|
|
||||||
collab,
|
|
||||||
Some(folder_notifier),
|
|
||||||
folder_data,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return Err(FlowyError::new(
|
|
||||||
ErrorCode::RecordNotFound,
|
|
||||||
"Can't find any workspace data",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FolderInitDataSource::Cloud(raw_data) => {
|
|
||||||
event!(Level::INFO, "Restore folder from cloud service");
|
|
||||||
if raw_data.is_empty() {
|
|
||||||
return Err(workspace_data_not_sync_error(uid, &workspace_id));
|
|
||||||
}
|
|
||||||
let collab = self
|
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, raw_data)
|
|
||||||
.await?;
|
|
||||||
Folder::open(UserId::from(uid), collab, Some(folder_notifier))?
|
|
||||||
},
|
|
||||||
FolderInitDataSource::FolderData(folder_data) => {
|
|
||||||
event!(Level::INFO, "Restore folder with passed-in folder data");
|
|
||||||
let collab = self
|
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
|
||||||
.await?;
|
|
||||||
Folder::create(
|
|
||||||
UserId::from(uid),
|
|
||||||
collab,
|
|
||||||
Some(folder_notifier),
|
|
||||||
folder_data,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let folder_state_rx = folder.subscribe_sync_state();
|
|
||||||
*self.mutex_folder.lock() = 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(trash_rx, &weak_mutex_folder);
|
|
||||||
subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn collab_for_folder(
|
|
||||||
&self,
|
&self,
|
||||||
uid: i64,
|
uid: i64,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
@ -1095,124 +993,8 @@ impl FolderManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Listen on the [ViewChange] after create/delete/update events happened
|
|
||||||
fn subscribe_folder_view_changed(
|
|
||||||
mut rx: ViewChangeReceiver,
|
|
||||||
weak_mutex_folder: &Weak<MutexFolder>,
|
|
||||||
) {
|
|
||||||
let weak_mutex_folder = weak_mutex_folder.clone();
|
|
||||||
af_spawn(async move {
|
|
||||||
while let Ok(value) = rx.recv().await {
|
|
||||||
if let Some(folder) = weak_mutex_folder.upgrade() {
|
|
||||||
tracing::trace!("Did receive view change: {:?}", value);
|
|
||||||
match value {
|
|
||||||
ViewChange::DidCreateView { view } => {
|
|
||||||
notify_child_views_changed(
|
|
||||||
view_pb_without_child_views(Arc::new(view.clone())),
|
|
||||||
ChildViewChangeReason::DidCreateView,
|
|
||||||
);
|
|
||||||
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
|
|
||||||
},
|
|
||||||
ViewChange::DidDeleteView { views } => {
|
|
||||||
for view in views {
|
|
||||||
notify_child_views_changed(
|
|
||||||
view_pb_without_child_views(view),
|
|
||||||
ChildViewChangeReason::DidDeleteView,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ViewChange::DidUpdate { view } => {
|
|
||||||
notify_child_views_changed(
|
|
||||||
view_pb_without_child_views(Arc::new(view.clone())),
|
|
||||||
ChildViewChangeReason::DidUpdateView,
|
|
||||||
);
|
|
||||||
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subscribe_folder_snapshot_state_changed(
|
|
||||||
workspace_id: String,
|
|
||||||
weak_mutex_folder: &Weak<MutexFolder>,
|
|
||||||
) {
|
|
||||||
let weak_mutex_folder = weak_mutex_folder.clone();
|
|
||||||
af_spawn(async move {
|
|
||||||
if let Some(mutex_folder) = weak_mutex_folder.upgrade() {
|
|
||||||
let stream = mutex_folder
|
|
||||||
.lock()
|
|
||||||
.as_ref()
|
|
||||||
.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(new_snapshot_id) = snapshot_state.snapshot_id() {
|
|
||||||
tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id);
|
|
||||||
send_notification(
|
|
||||||
&workspace_id,
|
|
||||||
FolderNotification::DidUpdateFolderSnapshotState,
|
|
||||||
)
|
|
||||||
.payload(FolderSnapshotStatePB { new_snapshot_id })
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subscribe_folder_sync_state_changed(
|
|
||||||
workspace_id: String,
|
|
||||||
mut folder_sync_state_rx: WatchStream<SyncState>,
|
|
||||||
_weak_mutex_folder: &Weak<MutexFolder>,
|
|
||||||
) {
|
|
||||||
af_spawn(async move {
|
|
||||||
while let Some(state) = folder_sync_state_rx.next().await {
|
|
||||||
send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
|
|
||||||
.payload(FolderSyncStatePB::from(state))
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Listen on the [TrashChange]s and notify the frontend some views were changed.
|
|
||||||
fn subscribe_folder_trash_changed(
|
|
||||||
mut rx: TrashChangeReceiver,
|
|
||||||
weak_mutex_folder: &Weak<MutexFolder>,
|
|
||||||
) {
|
|
||||||
let weak_mutex_folder = weak_mutex_folder.clone();
|
|
||||||
af_spawn(async move {
|
|
||||||
while let Ok(value) = rx.recv().await {
|
|
||||||
if let Some(folder) = weak_mutex_folder.upgrade() {
|
|
||||||
let mut unique_ids = HashSet::new();
|
|
||||||
tracing::trace!("Did receive trash change: {:?}", value);
|
|
||||||
let ids = match value {
|
|
||||||
TrashChange::DidCreateTrash { ids } => ids,
|
|
||||||
TrashChange::DidDeleteTrash { ids } => ids,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(folder) = folder.lock().as_ref() {
|
|
||||||
let views = folder.views.get_views(&ids);
|
|
||||||
for view in views {
|
|
||||||
unique_ids.insert(view.parent_view_id.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let repeated_trash: RepeatedTrashPB = folder.get_all_trash().into();
|
|
||||||
send_notification("trash", FolderNotification::DidUpdateTrash)
|
|
||||||
.payload(repeated_trash)
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
let parent_view_ids = unique_ids.into_iter().collect();
|
|
||||||
notify_parent_view_did_change(folder.clone(), parent_view_ids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the views that belong to the workspace. The views are filtered by the trash.
|
/// Return the views that belong to the workspace. The views are filtered by the trash.
|
||||||
fn get_workspace_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
pub(crate) fn get_workspace_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
||||||
let trash_ids = folder
|
let trash_ids = folder
|
||||||
.get_all_trash()
|
.get_all_trash()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -1236,91 +1018,6 @@ fn get_workspace_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
|
|
||||||
let repeated_view: RepeatedViewPB = get_workspace_view_pbs(workspace_id, folder).into();
|
|
||||||
tracing::trace!("Did update workspace views: {:?}", repeated_view);
|
|
||||||
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
|
|
||||||
.payload(repeated_view)
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notify the the list of parent view ids that its child views were changed.
|
|
||||||
#[tracing::instrument(level = "debug", skip(folder, parent_view_ids))]
|
|
||||||
fn notify_parent_view_did_change<T: AsRef<str>>(
|
|
||||||
folder: Arc<MutexFolder>,
|
|
||||||
parent_view_ids: Vec<T>,
|
|
||||||
) -> Option<()> {
|
|
||||||
let folder = folder.lock();
|
|
||||||
let folder = folder.as_ref()?;
|
|
||||||
let workspace_id = folder.get_workspace_id();
|
|
||||||
let trash_ids = folder
|
|
||||||
.get_all_trash()
|
|
||||||
.into_iter()
|
|
||||||
.map(|trash| trash.id)
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
for parent_view_id in parent_view_ids {
|
|
||||||
let parent_view_id = parent_view_id.as_ref();
|
|
||||||
|
|
||||||
// if the view's parent id equal to workspace id. Then it will fetch the current
|
|
||||||
// workspace views. Because the the workspace is not a view stored in the views map.
|
|
||||||
if parent_view_id == workspace_id {
|
|
||||||
notify_did_update_workspace(&workspace_id, folder)
|
|
||||||
} else {
|
|
||||||
// Parent view can contain a list of child views. Currently, only get the first level
|
|
||||||
// child views.
|
|
||||||
let parent_view = folder.views.get_view(parent_view_id)?;
|
|
||||||
let mut child_views = folder.views.get_views_belong_to(parent_view_id);
|
|
||||||
child_views.retain(|view| !trash_ids.contains(&view.id));
|
|
||||||
event!(Level::DEBUG, child_views_count = child_views.len());
|
|
||||||
|
|
||||||
// Post the notification
|
|
||||||
let parent_view_pb = view_pb_with_child_views(parent_view, child_views);
|
|
||||||
send_notification(parent_view_id, FolderNotification::DidUpdateView)
|
|
||||||
.payload(parent_view_pb)
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ChildViewChangeReason {
|
|
||||||
DidCreateView,
|
|
||||||
DidDeleteView,
|
|
||||||
DidUpdateView,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notify the the list of parent view ids that its child views were changed.
|
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
|
||||||
fn notify_child_views_changed(view_pb: ViewPB, reason: ChildViewChangeReason) {
|
|
||||||
let parent_view_id = view_pb.parent_view_id.clone();
|
|
||||||
let mut payload = ChildViewUpdatePB {
|
|
||||||
parent_view_id: view_pb.parent_view_id.clone(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
match reason {
|
|
||||||
ChildViewChangeReason::DidCreateView => {
|
|
||||||
payload.create_child_views.push(view_pb);
|
|
||||||
},
|
|
||||||
ChildViewChangeReason::DidDeleteView => {
|
|
||||||
payload.delete_child_views.push(view_pb.id);
|
|
||||||
},
|
|
||||||
ChildViewChangeReason::DidUpdateView => {
|
|
||||||
payload.update_child_views.push(view_pb);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
send_notification(&parent_view_id, FolderNotification::DidUpdateChildViews)
|
|
||||||
.payload(payload)
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn folder_not_init_error() -> FlowyError {
|
|
||||||
FlowyError::internal().with_context("Folder not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct MutexFolder(Arc<Mutex<Option<Folder>>>);
|
pub struct MutexFolder(Arc<Mutex<Option<Folder>>>);
|
||||||
impl Deref for MutexFolder {
|
impl Deref for MutexFolder {
|
||||||
@ -1351,20 +1048,3 @@ impl Display for FolderInitDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_exist_in_local_disk(user: &Arc<dyn FolderUser>, doc_id: &str) -> FlowyResult<bool> {
|
|
||||||
let uid = user.user_id()?;
|
|
||||||
if let Some(collab_db) = user.collab_db(uid)?.upgrade() {
|
|
||||||
let read_txn = collab_db.read_txn();
|
|
||||||
Ok(read_txn.is_exist(uid, doc_id))
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn workspace_data_not_sync_error(uid: i64, workspace_id: &str) -> FlowyError {
|
|
||||||
FlowyError::from(ErrorCode::WorkspaceDataNotSync).with_payload(UserFolderPB {
|
|
||||||
uid,
|
|
||||||
workspace_id: workspace_id.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
158
frontend/rust-lib/flowy-folder2/src/manager_init.rs
Normal file
158
frontend/rust-lib/flowy-folder2/src/manager_init.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
|
use collab_folder::{Folder, FolderNotify, UserId};
|
||||||
|
use tracing::{event, Level};
|
||||||
|
|
||||||
|
use collab_integrate::RocksCollabDB;
|
||||||
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
|
|
||||||
|
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::user_default::DefaultFolderBuilder;
|
||||||
|
use crate::util::is_exist_in_local_disk;
|
||||||
|
|
||||||
|
impl FolderManager {
|
||||||
|
/// Called immediately after the application launched if the user already sign in/sign up.
|
||||||
|
#[tracing::instrument(level = "info", skip(self, initial_data), err)]
|
||||||
|
pub async fn initialize(
|
||||||
|
&self,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: &str,
|
||||||
|
initial_data: FolderInitDataSource,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
// Update the workspace id
|
||||||
|
event!(
|
||||||
|
Level::INFO,
|
||||||
|
"Init workspace: {} from: {}",
|
||||||
|
workspace_id,
|
||||||
|
initial_data
|
||||||
|
);
|
||||||
|
*self.workspace_id.write() = Some(workspace_id.to_string());
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
let (view_tx, view_rx) = tokio::sync::broadcast::channel(100);
|
||||||
|
let (trash_tx, trash_rx) = tokio::sync::broadcast::channel(100);
|
||||||
|
let folder_notifier = FolderNotify {
|
||||||
|
view_change_tx: view_tx,
|
||||||
|
trash_change_tx: trash_tx,
|
||||||
|
};
|
||||||
|
|
||||||
|
let folder = match initial_data {
|
||||||
|
FolderInitDataSource::LocalDisk {
|
||||||
|
create_if_not_exist,
|
||||||
|
} => {
|
||||||
|
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
|
||||||
|
if is_exist {
|
||||||
|
self
|
||||||
|
.open_local_folder(uid, &workspace_id, collab_db, folder_notifier)
|
||||||
|
.await?
|
||||||
|
} else if create_if_not_exist {
|
||||||
|
// Currently, this branch is only used when the server type is supabase. For appflowy cloud,
|
||||||
|
// the default workspace is already created when the user sign up.
|
||||||
|
self
|
||||||
|
.create_default_folder(uid, &workspace_id, collab_db, folder_notifier)
|
||||||
|
.await?
|
||||||
|
} else {
|
||||||
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::RecordNotFound,
|
||||||
|
"Can't find any workspace data",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FolderInitDataSource::Cloud(raw_data) => {
|
||||||
|
if raw_data.is_empty() {
|
||||||
|
event!(Level::INFO, "remote folder data is empty, open from local");
|
||||||
|
self
|
||||||
|
.open_local_folder(uid, &workspace_id, collab_db, folder_notifier)
|
||||||
|
.await?
|
||||||
|
} else {
|
||||||
|
event!(Level::INFO, "Restore folder with remote data");
|
||||||
|
let result = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db.clone(), raw_data)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// If failed to open folder with remote data, open from local disk. After open from the local
|
||||||
|
// disk. the data will be synced to the remote server.
|
||||||
|
match result {
|
||||||
|
Ok(collab) => Folder::open(UserId::from(uid), collab, Some(folder_notifier.clone()))?,
|
||||||
|
Err(err) => {
|
||||||
|
event!(
|
||||||
|
Level::ERROR,
|
||||||
|
"Open folder with remote data failed: {:?}, open from local disk",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
self
|
||||||
|
.open_local_folder(uid, &workspace_id, collab_db, folder_notifier)
|
||||||
|
.await?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FolderInitDataSource::FolderData(folder_data) => {
|
||||||
|
event!(Level::INFO, "Restore folder with passed-in folder data");
|
||||||
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
|
Folder::create(
|
||||||
|
UserId::from(uid),
|
||||||
|
collab,
|
||||||
|
Some(folder_notifier),
|
||||||
|
folder_data,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let folder_state_rx = folder.subscribe_sync_state();
|
||||||
|
*self.mutex_folder.lock() = 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(trash_rx, &weak_mutex_folder);
|
||||||
|
subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_default_folder(
|
||||||
|
&self,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: &String,
|
||||||
|
collab_db: Weak<RocksCollabDB>,
|
||||||
|
folder_notifier: FolderNotify,
|
||||||
|
) -> Result<Folder, FlowyError> {
|
||||||
|
event!(Level::INFO, "Create folder with default folder builder");
|
||||||
|
let folder_data =
|
||||||
|
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers).await;
|
||||||
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
|
Ok(Folder::create(
|
||||||
|
UserId::from(uid),
|
||||||
|
collab,
|
||||||
|
Some(folder_notifier),
|
||||||
|
folder_data,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_local_folder(
|
||||||
|
&self,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: &String,
|
||||||
|
collab_db: Weak<RocksCollabDB>,
|
||||||
|
folder_notifier: FolderNotify,
|
||||||
|
) -> Result<Folder, FlowyError> {
|
||||||
|
event!(Level::INFO, "Init folder from local disk");
|
||||||
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
|
let folder = Folder::open(UserId::from(uid), collab, Some(folder_notifier))?;
|
||||||
|
Ok(folder)
|
||||||
|
}
|
||||||
|
}
|
214
frontend/rust-lib/flowy-folder2/src/manager_observer.rs
Normal file
214
frontend/rust-lib/flowy-folder2/src/manager_observer.rs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
|
use collab::core::collab_state::SyncState;
|
||||||
|
use collab_folder::{Folder, TrashChange, TrashChangeReceiver, ViewChange, ViewChangeReceiver};
|
||||||
|
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, ViewPB,
|
||||||
|
};
|
||||||
|
use crate::manager::{get_workspace_view_pbs, MutexFolder};
|
||||||
|
use crate::notification::{send_notification, FolderNotification};
|
||||||
|
|
||||||
|
/// Listen on the [ViewChange] after create/delete/update events happened
|
||||||
|
pub(crate) fn subscribe_folder_view_changed(
|
||||||
|
mut rx: ViewChangeReceiver,
|
||||||
|
weak_mutex_folder: &Weak<MutexFolder>,
|
||||||
|
) {
|
||||||
|
let weak_mutex_folder = weak_mutex_folder.clone();
|
||||||
|
af_spawn(async move {
|
||||||
|
while let Ok(value) = rx.recv().await {
|
||||||
|
if let Some(folder) = weak_mutex_folder.upgrade() {
|
||||||
|
tracing::trace!("Did receive view change: {:?}", value);
|
||||||
|
match value {
|
||||||
|
ViewChange::DidCreateView { view } => {
|
||||||
|
notify_child_views_changed(
|
||||||
|
view_pb_without_child_views(Arc::new(view.clone())),
|
||||||
|
ChildViewChangeReason::DidCreateView,
|
||||||
|
);
|
||||||
|
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
|
||||||
|
},
|
||||||
|
ViewChange::DidDeleteView { views } => {
|
||||||
|
for view in views {
|
||||||
|
notify_child_views_changed(
|
||||||
|
view_pb_without_child_views(view),
|
||||||
|
ChildViewChangeReason::DidDeleteView,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ViewChange::DidUpdate { view } => {
|
||||||
|
notify_child_views_changed(
|
||||||
|
view_pb_without_child_views(Arc::new(view.clone())),
|
||||||
|
ChildViewChangeReason::DidUpdateView,
|
||||||
|
);
|
||||||
|
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn subscribe_folder_snapshot_state_changed(
|
||||||
|
workspace_id: String,
|
||||||
|
weak_mutex_folder: &Weak<MutexFolder>,
|
||||||
|
) {
|
||||||
|
let weak_mutex_folder = weak_mutex_folder.clone();
|
||||||
|
af_spawn(async move {
|
||||||
|
if let Some(mutex_folder) = weak_mutex_folder.upgrade() {
|
||||||
|
let stream = mutex_folder
|
||||||
|
.lock()
|
||||||
|
.as_ref()
|
||||||
|
.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(new_snapshot_id) = snapshot_state.snapshot_id() {
|
||||||
|
tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id);
|
||||||
|
send_notification(
|
||||||
|
&workspace_id,
|
||||||
|
FolderNotification::DidUpdateFolderSnapshotState,
|
||||||
|
)
|
||||||
|
.payload(FolderSnapshotStatePB { new_snapshot_id })
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn subscribe_folder_sync_state_changed(
|
||||||
|
workspace_id: String,
|
||||||
|
mut folder_sync_state_rx: WatchStream<SyncState>,
|
||||||
|
_weak_mutex_folder: &Weak<MutexFolder>,
|
||||||
|
) {
|
||||||
|
af_spawn(async move {
|
||||||
|
while let Some(state) = folder_sync_state_rx.next().await {
|
||||||
|
send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
|
||||||
|
.payload(FolderSyncStatePB::from(state))
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Listen on the [TrashChange]s and notify the frontend some views were changed.
|
||||||
|
pub(crate) fn subscribe_folder_trash_changed(
|
||||||
|
mut rx: TrashChangeReceiver,
|
||||||
|
weak_mutex_folder: &Weak<MutexFolder>,
|
||||||
|
) {
|
||||||
|
let weak_mutex_folder = weak_mutex_folder.clone();
|
||||||
|
af_spawn(async move {
|
||||||
|
while let Ok(value) = rx.recv().await {
|
||||||
|
if let Some(folder) = weak_mutex_folder.upgrade() {
|
||||||
|
let mut unique_ids = HashSet::new();
|
||||||
|
tracing::trace!("Did receive trash change: {:?}", value);
|
||||||
|
let ids = match value {
|
||||||
|
TrashChange::DidCreateTrash { ids } => ids,
|
||||||
|
TrashChange::DidDeleteTrash { ids } => ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(folder) = folder.lock().as_ref() {
|
||||||
|
let views = folder.views.get_views(&ids);
|
||||||
|
for view in views {
|
||||||
|
unique_ids.insert(view.parent_view_id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let repeated_trash: RepeatedTrashPB = folder.get_all_trash().into();
|
||||||
|
send_notification("trash", FolderNotification::DidUpdateTrash)
|
||||||
|
.payload(repeated_trash)
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent_view_ids = unique_ids.into_iter().collect();
|
||||||
|
notify_parent_view_did_change(folder.clone(), parent_view_ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify the 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>>(
|
||||||
|
folder: Arc<MutexFolder>,
|
||||||
|
parent_view_ids: Vec<T>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let folder = folder.lock();
|
||||||
|
let folder = folder.as_ref()?;
|
||||||
|
let workspace_id = folder.get_workspace_id();
|
||||||
|
let trash_ids = folder
|
||||||
|
.get_all_trash()
|
||||||
|
.into_iter()
|
||||||
|
.map(|trash| trash.id)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
for parent_view_id in parent_view_ids {
|
||||||
|
let parent_view_id = parent_view_id.as_ref();
|
||||||
|
|
||||||
|
// if the view's parent id equal to workspace id. Then it will fetch the current
|
||||||
|
// workspace views. Because the the workspace is not a view stored in the views map.
|
||||||
|
if parent_view_id == workspace_id {
|
||||||
|
notify_did_update_workspace(&workspace_id, folder)
|
||||||
|
} else {
|
||||||
|
// Parent view can contain a list of child views. Currently, only get the first level
|
||||||
|
// child views.
|
||||||
|
let parent_view = folder.views.get_view(parent_view_id)?;
|
||||||
|
let mut child_views = folder.views.get_views_belong_to(parent_view_id);
|
||||||
|
child_views.retain(|view| !trash_ids.contains(&view.id));
|
||||||
|
event!(Level::DEBUG, child_views_count = child_views.len());
|
||||||
|
|
||||||
|
// Post the notification
|
||||||
|
let parent_view_pb = view_pb_with_child_views(parent_view, child_views);
|
||||||
|
send_notification(parent_view_id, FolderNotification::DidUpdateView)
|
||||||
|
.payload(parent_view_pb)
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
|
||||||
|
let repeated_view: RepeatedViewPB = get_workspace_view_pbs(workspace_id, folder).into();
|
||||||
|
tracing::trace!("Did update workspace views: {:?}", repeated_view);
|
||||||
|
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
|
||||||
|
.payload(repeated_view)
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ChildViewChangeReason {
|
||||||
|
DidCreateView,
|
||||||
|
DidDeleteView,
|
||||||
|
DidUpdateView,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify the the list of parent view ids that its child views were changed.
|
||||||
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
|
pub(crate) fn notify_child_views_changed(view_pb: ViewPB, reason: ChildViewChangeReason) {
|
||||||
|
let parent_view_id = view_pb.parent_view_id.clone();
|
||||||
|
let mut payload = ChildViewUpdatePB {
|
||||||
|
parent_view_id: view_pb.parent_view_id.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
match reason {
|
||||||
|
ChildViewChangeReason::DidCreateView => {
|
||||||
|
payload.create_child_views.push(view_pb);
|
||||||
|
},
|
||||||
|
ChildViewChangeReason::DidDeleteView => {
|
||||||
|
payload.delete_child_views.push(view_pb.id);
|
||||||
|
},
|
||||||
|
ChildViewChangeReason::DidUpdateView => {
|
||||||
|
payload.update_child_views.push(view_pb);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
send_notification(&parent_view_id, FolderNotification::DidUpdateChildViews)
|
||||||
|
.payload(payload)
|
||||||
|
.send();
|
||||||
|
}
|
31
frontend/rust-lib/flowy-folder2/src/util.rs
Normal file
31
frontend/rust-lib/flowy-folder2/src/util.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use collab_integrate::YrsDocAction;
|
||||||
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
|
|
||||||
|
use crate::entities::UserFolderPB;
|
||||||
|
use crate::manager::FolderUser;
|
||||||
|
|
||||||
|
pub(crate) fn folder_not_init_error() -> FlowyError {
|
||||||
|
FlowyError::internal().with_context("Folder not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_exist_in_local_disk(
|
||||||
|
user: &Arc<dyn FolderUser>,
|
||||||
|
doc_id: &str,
|
||||||
|
) -> FlowyResult<bool> {
|
||||||
|
let uid = user.user_id()?;
|
||||||
|
if let Some(collab_db) = user.collab_db(uid)?.upgrade() {
|
||||||
|
let read_txn = collab_db.read_txn();
|
||||||
|
Ok(read_txn.is_exist(uid, doc_id))
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn workspace_data_not_sync_error(uid: i64, workspace_id: &str) -> FlowyError {
|
||||||
|
FlowyError::from(ErrorCode::WorkspaceDataNotSync).with_payload(UserFolderPB {
|
||||||
|
uid,
|
||||||
|
workspace_id: workspace_id.to_string(),
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user