2023-08-04 11:27:14 +00:00
use crate ::entities ::icon ::UpdateViewIconParams ;
2023-04-04 00:41:16 +00:00
use crate ::entities ::{
2024-04-23 13:46:57 +00:00
view_pb_with_child_views , view_pb_without_child_views , view_pb_without_child_views_from_arc ,
2024-06-25 02:03:02 +00:00
CreateViewParams , CreateWorkspaceParams , DeletedViewPB , DuplicateViewParams , FolderSnapshotPB ,
MoveNestedViewParams , RepeatedTrashPB , RepeatedViewIdPB , RepeatedViewPB , UpdateViewParams ,
2024-07-08 05:45:57 +00:00
ViewLayoutPB , ViewPB , ViewSectionPB , WorkspacePB , WorkspaceSettingPB ,
2023-12-17 19:14:05 +00:00
} ;
use crate ::manager_observer ::{
2024-03-04 17:24:49 +00:00
notify_child_views_changed , notify_did_update_workspace , notify_parent_view_did_change ,
ChildViewChangeReason ,
2023-04-04 00:41:16 +00:00
} ;
use crate ::notification ::{
2024-08-06 05:17:56 +00:00
send_current_workspace_notification , send_notification , FolderNotification ,
2023-04-04 00:41:16 +00:00
} ;
2024-07-08 05:45:57 +00:00
use crate ::publish_util ::{ generate_publish_name , view_pb_to_publish_view } ;
2024-07-01 06:44:08 +00:00
use crate ::share ::{ ImportParams , ImportValue } ;
2023-12-29 05:02:27 +00:00
use crate ::util ::{
folder_not_init_error , insert_parent_child_views , workspace_data_not_sync_error ,
} ;
2024-07-22 05:35:42 +00:00
use crate ::view_operation ::{
create_view , EncodedCollabWrapper , FolderOperationHandler , FolderOperationHandlers ,
} ;
2024-08-18 03:16:42 +00:00
use arc_swap ::ArcSwapOption ;
use collab ::core ::collab ::DataSource ;
2024-07-01 06:44:08 +00:00
use collab_entity ::{ CollabType , EncodedCollab } ;
2024-03-23 01:18:47 +00:00
use collab_folder ::{
2024-08-18 03:16:42 +00:00
Folder , FolderData , FolderNotify , Section , SectionItem , TrashInfo , View , ViewLayout , ViewUpdate ,
2024-04-26 01:44:07 +00:00
Workspace ,
2024-03-23 01:18:47 +00:00
} ;
2024-08-18 03:16:42 +00:00
use collab_integrate ::collab_builder ::{
AppFlowyCollabBuilder , CollabBuilderConfig , KVDBCollabPersistenceImpl ,
} ;
2024-04-26 01:44:07 +00:00
use collab_integrate ::CollabKVDB ;
2024-07-01 06:44:08 +00:00
use flowy_error ::{ internal_error , ErrorCode , FlowyError , FlowyResult } ;
use flowy_folder_pub ::cloud ::{ gen_view_id , FolderCloudService , FolderCollabParams } ;
2024-07-08 05:45:57 +00:00
use flowy_folder_pub ::entities ::{
2024-07-22 05:35:42 +00:00
PublishDatabaseData , PublishDatabasePayload , PublishDocumentPayload , PublishInfoResponse ,
PublishPayload , PublishViewInfo , PublishViewMeta , PublishViewMetaData ,
2024-07-08 05:45:57 +00:00
} ;
2024-03-23 01:18:47 +00:00
use flowy_folder_pub ::folder_builder ::ParentChildViews ;
2024-04-12 08:21:41 +00:00
use flowy_search_pub ::entities ::FolderIndexManager ;
2024-06-30 09:38:39 +00:00
use flowy_sqlite ::kv ::KVStorePreferences ;
2024-07-08 05:45:57 +00:00
use futures ::future ;
2024-07-22 05:35:42 +00:00
use std ::collections ::HashMap ;
2024-03-23 01:18:47 +00:00
use std ::fmt ::{ Display , Formatter } ;
use std ::sync ::{ Arc , Weak } ;
2024-08-18 03:16:42 +00:00
use tokio ::sync ::RwLock ;
2024-04-24 02:26:58 +00:00
use tracing ::{ error , info , instrument } ;
2023-04-04 00:41:16 +00:00
2024-04-26 01:44:07 +00:00
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 > ;
2024-08-18 03:16:42 +00:00
fn is_folder_exist_on_disk ( & self , uid : i64 , workspace_id : & str ) -> FlowyResult < bool > ;
2023-07-29 01:46:24 +00:00
}
2023-07-05 12:57:09 +00:00
pub struct FolderManager {
2024-08-18 03:16:42 +00:00
//FIXME: there's no sense in having a mutex_folder behind an RwLock. It's being obtained multiple
// times in the same function. FolderManager itself should be hidden behind RwLock if necessary.
// Unfortunately, this would require a changing the SyncPlugin architecture which requires access
// to Arc<RwLock<BorrowMut<Collab>>>. Eventually SyncPlugin should be refactored.
2024-04-12 08:21:41 +00:00
/// MutexFolder is the folder that is used to store the data.
2024-08-18 03:16:42 +00:00
pub ( crate ) mutex_folder : ArcSwapOption < RwLock < Folder > > ,
2024-02-24 23:49:44 +00:00
pub ( crate ) collab_builder : Arc < AppFlowyCollabBuilder > ,
2023-12-17 19:14:05 +00:00
pub ( crate ) user : Arc < dyn FolderUser > ,
pub ( crate ) operation_handlers : FolderOperationHandlers ,
2023-11-28 23:49:47 +00:00
pub cloud_service : Arc < dyn FolderCloudService > ,
2024-04-12 08:21:41 +00:00
pub ( crate ) folder_indexer : Arc < dyn FolderIndexManager > ,
2024-06-30 09:38:39 +00:00
pub ( crate ) store_preferences : Arc < KVStorePreferences > ,
2023-04-04 00:41:16 +00:00
}
2023-07-05 12:57:09 +00:00
impl FolderManager {
2024-06-05 02:05:51 +00:00
pub fn new (
2023-04-04 00:41:16 +00:00
user : Arc < dyn FolderUser > ,
2023-05-15 14:16:05 +00:00
collab_builder : Arc < AppFlowyCollabBuilder > ,
2023-05-31 06:08:54 +00:00
operation_handlers : FolderOperationHandlers ,
2023-05-23 15:55:21 +00:00
cloud_service : Arc < dyn FolderCloudService > ,
2024-04-12 08:21:41 +00:00
folder_indexer : Arc < dyn FolderIndexManager > ,
2024-06-30 09:38:39 +00:00
store_preferences : Arc < KVStorePreferences > ,
2023-04-04 00:41:16 +00:00
) -> FlowyResult < Self > {
let manager = Self {
user ,
2024-08-18 03:16:42 +00:00
mutex_folder : Default ::default ( ) ,
2023-05-15 14:16:05 +00:00
collab_builder ,
2023-05-31 06:08:54 +00:00
operation_handlers ,
2023-05-23 15:55:21 +00:00
cloud_service ,
2024-04-12 08:21:41 +00:00
folder_indexer ,
2024-06-05 02:05:51 +00:00
store_preferences ,
2023-04-04 00:41:16 +00:00
} ;
Ok ( manager )
}
2023-11-01 03:45:35 +00:00
#[ instrument(level = " debug " , skip(self), err) ]
2023-06-06 16:05:27 +00:00
pub async fn get_current_workspace ( & self ) -> FlowyResult < WorkspacePB > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2024-08-18 03:16:42 +00:00
match self . mutex_folder . load_full ( ) {
None = > {
2023-08-21 16:19:15 +00:00
let uid = self . user . user_id ( ) ? ;
Err ( workspace_data_not_sync_error ( uid , & workspace_id ) )
} ,
2024-08-18 03:16:42 +00:00
Some ( lock ) = > {
let folder = lock . read ( ) . await ;
2023-07-14 05:37:13 +00:00
let workspace_pb_from_workspace = | workspace : Workspace , folder : & Folder | {
2024-04-26 01:44:07 +00:00
let views = get_workspace_public_view_pbs ( & workspace_id , folder ) ;
2023-07-14 05:37:13 +00:00
let workspace : WorkspacePB = ( workspace , views ) . into ( ) ;
Ok ::< WorkspacePB , FlowyError > ( workspace )
} ;
2023-06-07 14:27:13 +00:00
2024-04-26 01:44:07 +00:00
match folder . get_workspace_info ( & workspace_id ) {
2023-11-01 03:45:35 +00:00
None = > Err ( FlowyError ::record_not_found ( ) . with_context ( " Can not find the workspace " ) ) ,
2024-08-18 03:16:42 +00:00
Some ( workspace ) = > workspace_pb_from_workspace ( workspace , & folder ) ,
2023-07-14 05:37:13 +00:00
}
} ,
2024-08-18 03:16:42 +00:00
}
2023-04-04 00:41:16 +00:00
}
2023-06-05 05:10:14 +00:00
/// Return a list of views of the current workspace.
/// Only the first level of child views are included.
2024-03-22 09:15:18 +00:00
pub async fn get_current_workspace_public_views ( & self ) -> FlowyResult < Vec < ViewPB > > {
2024-04-26 01:44:07 +00:00
let views = self . get_workspace_public_views ( ) . await ? ;
Ok ( views )
2023-04-04 06:08:50 +00:00
}
2023-04-04 00:41:16 +00:00
2024-04-24 00:47:33 +00:00
pub async fn get_workspace_public_views ( & self ) -> FlowyResult < Vec < ViewPB > > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2024-08-18 03:16:42 +00:00
match self . mutex_folder . load_full ( ) {
None = > Ok ( Vec ::default ( ) ) ,
Some ( lock ) = > {
let folder = lock . read ( ) . await ;
Ok ( get_workspace_public_view_pbs ( & workspace_id , & folder ) )
} ,
}
2024-03-21 04:02:03 +00:00
}
2024-04-24 00:47:33 +00:00
pub async fn get_workspace_private_views ( & self ) -> FlowyResult < Vec < ViewPB > > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2024-08-18 03:16:42 +00:00
match self . mutex_folder . load_full ( ) {
None = > Ok ( Vec ::default ( ) ) ,
Some ( folder ) = > {
let folder = folder . read ( ) . await ;
Ok ( get_workspace_private_view_pbs ( & workspace_id , & folder ) )
} ,
}
2023-04-04 00:41:16 +00:00
}
2024-04-26 01:44:07 +00:00
#[ instrument(level = " trace " , skip_all, err) ]
2024-02-24 23:49:44 +00:00
pub ( crate ) async fn make_folder < T : Into < Option < FolderNotify > > > (
2023-07-14 05:37:13 +00:00
& self ,
uid : i64 ,
workspace_id : & str ,
2024-01-04 16:05:38 +00:00
collab_db : Weak < CollabKVDB > ,
2024-08-18 03:16:42 +00:00
data_source : Option < DataSource > ,
2024-02-24 23:49:44 +00:00
folder_notifier : T ,
2024-08-18 03:16:42 +00:00
) -> Result < Arc < RwLock < Folder > > , FlowyError > {
2024-02-24 23:49:44 +00:00
let folder_notifier = folder_notifier . into ( ) ;
2024-04-24 06:38:47 +00:00
// only need the check the workspace id when the doc state is not from the disk.
2024-08-19 14:08:10 +00:00
let config = CollabBuilderConfig ::default ( ) . sync_enable ( true ) ;
2024-08-18 03:16:42 +00:00
let data_source = data_source
. unwrap_or_else ( | | KVDBCollabPersistenceImpl ::new ( collab_db . clone ( ) , uid ) . into_data_source ( ) ) ;
2024-04-24 06:38:47 +00:00
2024-04-26 01:44:07 +00:00
let object_id = workspace_id ;
2024-08-18 03:16:42 +00:00
let collab_object =
self
. collab_builder
. collab_object ( workspace_id , uid , object_id , CollabType ::Folder ) ? ;
let result = self . collab_builder . create_folder (
collab_object ,
data_source ,
2024-03-23 01:18:47 +00:00
collab_db ,
2024-04-24 06:38:47 +00:00
config ,
2024-08-18 03:16:42 +00:00
folder_notifier ,
None ,
) ;
2024-02-24 23:49:44 +00:00
// If opening the folder fails due to missing required data (indicated by a `FolderError::NoRequiredData`),
// the function logs an informational message and attempts to clear the folder data by deleting its
// document from the collaborative database. It then returns the encountered error.
2024-08-18 03:16:42 +00:00
match result {
Ok ( folder ) = > Ok ( folder ) ,
Err ( err ) = > {
info! (
" Clear the folder data and try to open the folder again due to: {} " ,
err
) ;
if let Some ( db ) = self . user . collab_db ( uid ) . ok ( ) . and_then ( | a | a . upgrade ( ) ) {
let _ = db . delete_doc ( uid , workspace_id ) . await ;
}
Err ( err . into ( ) )
} ,
2024-02-24 23:49:44 +00:00
}
}
pub ( crate ) async fn create_empty_collab (
& self ,
uid : i64 ,
workspace_id : & str ,
collab_db : Weak < CollabKVDB > ,
2024-08-18 03:16:42 +00:00
notifier : Option < FolderNotify > ,
folder_data : Option < FolderData > ,
) -> Result < Arc < RwLock < Folder > > , FlowyError > {
2024-04-26 01:44:07 +00:00
let object_id = workspace_id ;
2024-08-18 03:16:42 +00:00
let collab_object =
self
. collab_builder
. collab_object ( workspace_id , uid , object_id , CollabType ::Folder ) ? ;
let doc_state = KVDBCollabPersistenceImpl ::new ( collab_db . clone ( ) , uid ) . into_data_source ( ) ;
let folder = self . collab_builder . create_folder (
collab_object ,
doc_state ,
2024-03-23 01:18:47 +00:00
collab_db ,
CollabBuilderConfig ::default ( ) . sync_enable ( true ) ,
2024-08-18 03:16:42 +00:00
notifier ,
folder_data ,
2024-03-23 01:18:47 +00:00
) ? ;
2024-08-18 03:16:42 +00:00
Ok ( folder )
2023-07-14 05:37:13 +00:00
}
2023-07-29 01:46:24 +00:00
/// Initialize the folder with the given workspace id.
/// Fetch the folder updates from the cloud service and initialize the folder.
2023-11-05 06:00:24 +00:00
#[ tracing::instrument(skip(self, user_id), err) ]
2024-04-26 01:44:07 +00:00
pub async fn initialize_with_workspace_id ( & self , user_id : i64 ) -> FlowyResult < ( ) > {
let workspace_id = self . user . workspace_id ( ) ? ;
let object_id = & workspace_id ;
2024-08-18 03:16:42 +00:00
let is_exist = self
. user
. is_folder_exist_on_disk ( user_id , & workspace_id )
. unwrap_or ( false ) ;
if is_exist {
2023-12-18 20:36:24 +00:00
self
. initialize (
user_id ,
2024-04-26 01:44:07 +00:00
& workspace_id ,
2023-12-18 20:36:24 +00:00
FolderInitDataSource ::LocalDisk {
create_if_not_exist : false ,
} ,
)
. await ? ;
2024-08-18 03:16:42 +00:00
} else {
let folder_doc_state = self
. cloud_service
. get_folder_doc_state ( & workspace_id , user_id , CollabType ::Folder , object_id )
. await ? ;
if let Err ( err ) = self
. initialize (
user_id ,
& workspace_id ,
FolderInitDataSource ::Cloud ( folder_doc_state ) ,
)
. 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.
error! ( " initialize folder with error {:?}, fallback local " , err ) ;
self
. initialize (
user_id ,
& workspace_id ,
FolderInitDataSource ::LocalDisk {
create_if_not_exist : false ,
} ,
)
. await ? ;
}
2023-12-18 20:36:24 +00:00
}
2024-08-18 03:16:42 +00:00
2023-07-14 05:37:13 +00:00
Ok ( ( ) )
}
2023-07-29 01:46:24 +00:00
/// Initialize the folder for the new user.
/// Using the [DefaultFolderBuilder] to create the default workspace for the new user.
2023-11-05 06:00:24 +00:00
#[ instrument(level = " info " , skip_all, err) ]
2023-07-29 01:46:24 +00:00
pub async fn initialize_with_new_user (
2023-05-21 10:53:59 +00:00
& self ,
user_id : i64 ,
2023-07-14 05:37:13 +00:00
_token : & str ,
2023-07-05 12:57:09 +00:00
is_new : bool ,
2023-11-05 06:00:24 +00:00
data_source : FolderInitDataSource ,
2023-05-21 10:53:59 +00:00
workspace_id : & str ,
) -> FlowyResult < ( ) > {
2023-07-05 12:57:09 +00:00
// Create the default workspace if the user is new
2023-10-07 01:58:44 +00:00
info! ( " initialize_when_sign_up: is_new: {} " , is_new ) ;
2023-07-05 12:57:09 +00:00
if is_new {
2023-08-28 05:28:24 +00:00
self . initialize ( user_id , workspace_id , data_source ) . await ? ;
2023-07-14 05:37:13 +00:00
} else {
2023-08-17 15:46:39 +00:00
// The folder updates should not be empty, as the folder data is stored
// when the user signs up for the first time.
2023-10-07 01:58:44 +00:00
let result = self
2023-07-14 05:37:13 +00:00
. cloud_service
2024-02-03 21:49:45 +00:00
. get_folder_doc_state ( workspace_id , user_id , CollabType ::Folder , workspace_id )
2023-10-07 01:58:44 +00:00
. await
. map_err ( FlowyError ::from ) ;
2023-08-17 15:46:39 +00:00
2023-10-07 01:58:44 +00:00
match result {
2024-04-26 01:44:07 +00:00
Ok ( folder_doc_state ) = > {
2023-10-07 01:58:44 +00:00
info! (
2024-02-22 00:07:59 +00:00
" Get folder updates via {}, doc state len: {} " ,
2023-10-07 01:58:44 +00:00
self . cloud_service . service_name ( ) ,
2024-04-26 01:44:07 +00:00
folder_doc_state . len ( )
2023-10-07 01:58:44 +00:00
) ;
self
. initialize (
user_id ,
workspace_id ,
2024-04-26 01:44:07 +00:00
FolderInitDataSource ::Cloud ( folder_doc_state ) ,
2023-10-07 01:58:44 +00:00
)
. await ? ;
} ,
Err ( err ) = > {
if err . is_record_not_found ( ) {
self . initialize ( user_id , workspace_id , data_source ) . await ? ;
} else {
return Err ( err ) ;
}
} ,
}
2023-07-05 12:57:09 +00:00
}
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
/// Called when the current user logout
///
2023-04-28 06:08:53 +00:00
pub async fn clear ( & self , _user_id : i64 ) { }
2023-04-04 00:41:16 +00:00
2023-06-08 06:20:31 +00:00
#[ tracing::instrument(level = " info " , skip_all, err) ]
2024-02-03 21:49:45 +00:00
pub async fn create_workspace ( & self , params : CreateWorkspaceParams ) -> FlowyResult < Workspace > {
let uid = self . user . user_id ( ) ? ;
let new_workspace = self
. cloud_service
. create_workspace ( uid , & params . name )
. await ? ;
Ok ( new_workspace )
2023-04-04 00:41:16 +00:00
}
2023-11-05 06:00:24 +00:00
pub async fn get_workspace_setting_pb ( & self ) -> FlowyResult < WorkspaceSettingPB > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2023-11-01 03:45:35 +00:00
let latest_view = self . get_current_view ( ) . await ;
2023-11-05 06:00:24 +00:00
Ok ( WorkspaceSettingPB {
2023-11-01 03:45:35 +00:00
workspace_id ,
latest_view ,
} )
}
2024-01-12 06:34:59 +00:00
pub async fn insert_parent_child_views (
& self ,
views : Vec < ParentChildViews > ,
) -> Result < ( ) , FlowyError > {
2024-08-18 03:16:42 +00:00
match self . mutex_folder . load_full ( ) {
None = > Err ( FlowyError ::internal ( ) . with_context ( " The folder is not initialized " ) ) ,
Some ( lock ) = > {
let mut folder = lock . write ( ) . await ;
2024-01-12 06:34:59 +00:00
for view in views {
2024-08-18 03:16:42 +00:00
insert_parent_child_views ( & mut folder , view ) ;
2024-01-12 06:34:59 +00:00
}
Ok ( ( ) )
} ,
2024-08-18 03:16:42 +00:00
}
2024-01-12 06:34:59 +00:00
}
2023-11-05 06:00:24 +00:00
pub async fn get_workspace_pb ( & self ) -> FlowyResult < WorkspacePB > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2024-08-18 03:16:42 +00:00
let lock = self
. mutex_folder
. load_full ( )
. ok_or_else ( | | FlowyError ::internal ( ) . with_context ( " folder is not initialized " ) ) ? ;
let folder = lock . read ( ) . await ;
2024-04-26 01:44:07 +00:00
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
. get_views_belong_to ( & workspace . id )
. into_iter ( )
. map ( | view | view_pb_without_child_views ( view . as_ref ( ) . clone ( ) ) )
. collect ::< Vec < ViewPB > > ( ) ;
Ok ( WorkspacePB {
id : workspace . id ,
name : workspace . name ,
views ,
create_time : workspace . created_at ,
} )
2023-07-29 01:46:24 +00:00
}
2024-07-02 05:02:15 +00:00
/// Asynchronously creates a view with provided parameters and notifies the workspace if update is needed.
///
/// Commonly, the notify_workspace_update parameter is set to true when the view is created in the workspace.
/// If you're handling multiple views in the same hierarchy and want to notify the workspace only after the last view is created,
/// you can set notify_workspace_update to false to avoid multiple notifications.
pub async fn create_view_with_params (
& self ,
params : CreateViewParams ,
notify_workspace_update : bool ,
) -> FlowyResult < ( View , Option < EncodedCollab > ) > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2023-04-04 00:41:16 +00:00
let view_layout : ViewLayout = params . layout . clone ( ) . into ( ) ;
2023-05-31 06:08:54 +00:00
let handler = self . get_handler ( & view_layout ) ? ;
2023-04-04 00:41:16 +00:00
let user_id = self . user . user_id ( ) ? ;
2024-07-02 05:02:15 +00:00
let mut encoded_collab : Option < EncodedCollab > = None ;
2023-06-06 09:19:53 +00:00
2024-06-27 01:07:08 +00:00
if params . meta . is_empty ( ) & & params . initial_data . is_empty ( ) {
2023-06-06 09:19:53 +00:00
tracing ::trace! ( " Create view with build-in data " ) ;
handler
. create_built_in_view ( user_id , & params . view_id , & params . name , view_layout . clone ( ) )
. await ? ;
} else {
tracing ::trace! ( " Create view with view data " ) ;
2024-07-02 05:02:15 +00:00
encoded_collab = handler
2024-06-27 01:07:08 +00:00
. create_view_with_view_data ( user_id , params . clone ( ) )
2023-06-06 09:19:53 +00:00
. await ? ;
2023-04-04 00:41:16 +00:00
}
2023-06-06 09:19:53 +00:00
2023-08-02 13:20:51 +00:00
let index = params . index ;
2024-03-21 04:02:03 +00:00
let section = params . section . clone ( ) . unwrap_or ( ViewSectionPB ::Public ) ;
let is_private = section = = ViewSectionPB ::Private ;
2023-11-08 13:48:17 +00:00
let view = create_view ( self . user . user_id ( ) ? , params , view_layout ) ;
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . insert_view ( view . clone ( ) , index ) ;
if is_private {
folder . add_private_view_ids ( vec! [ view . id . clone ( ) ] ) ;
}
if notify_workspace_update {
notify_did_update_workspace ( & workspace_id , & folder ) ;
2024-07-02 05:02:15 +00:00
}
2024-04-12 08:21:41 +00:00
}
2024-07-02 05:02:15 +00:00
Ok ( ( view , encoded_collab ) )
2023-04-04 00:41:16 +00:00
}
2023-06-14 14:16:33 +00:00
/// The orphan view is meant to be a view that is not attached to any parent view. By default, this
/// view will not be shown in the view list unless it is attached to a parent view that is shown in
/// the view list.
pub async fn create_orphan_view_with_params (
& self ,
params : CreateViewParams ,
) -> FlowyResult < View > {
let view_layout : ViewLayout = params . layout . clone ( ) . into ( ) ;
2024-01-04 00:02:12 +00:00
// TODO(nathan): remove orphan view. Just use for create document in row
2023-06-14 14:16:33 +00:00
let handler = self . get_handler ( & view_layout ) ? ;
let user_id = self . user . user_id ( ) ? ;
handler
. create_built_in_view ( user_id , & params . view_id , & params . name , view_layout . clone ( ) )
. await ? ;
2024-01-03 03:41:29 +00:00
2023-11-08 13:48:17 +00:00
let view = create_view ( self . user . user_id ( ) ? , params , view_layout ) ;
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . insert_view ( view . clone ( ) , None ) ;
}
2023-06-14 14:16:33 +00:00
Ok ( view )
}
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub ( crate ) async fn close_view ( & self , view_id : & str ) -> Result < ( ) , FlowyError > {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let folder = lock . read ( ) . await ;
if let Some ( view ) = folder . get_view ( view_id ) {
// Drop the folder lock explicitly to avoid deadlock when following calls contains 'self'
drop ( folder ) ;
let handler = self . get_handler ( & view . layout ) ? ;
handler . close_view ( view_id ) . await ? ;
}
2023-07-29 01:46:24 +00:00
}
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
2024-04-09 00:58:53 +00:00
/// Retrieves the view corresponding to the specified view ID.
///
/// It is important to note that if the target view contains child views,
/// this method only provides access to the first level of child views.
///
/// Therefore, to access a nested child view within one of the initial child views, you must invoke this method
/// again using the ID of the child view you wish to access.
2024-02-09 02:18:52 +00:00
#[ tracing::instrument(level = " debug " , skip(self)) ]
2023-11-01 03:45:35 +00:00
pub async fn get_view_pb ( & self , view_id : & str ) -> FlowyResult < ViewPB > {
2023-04-04 00:41:16 +00:00
let view_id = view_id . to_string ( ) ;
2024-04-23 13:46:57 +00:00
2024-08-18 03:16:42 +00:00
let lock = self
. mutex_folder
. load_full ( )
. ok_or_else ( folder_not_init_error ) ? ;
let folder = lock . read ( ) . await ;
2023-04-04 00:41:16 +00:00
2024-04-07 15:06:33 +00:00
// trash views and other private views should not be accessed
2024-08-18 03:16:42 +00:00
let view_ids_should_be_filtered = Self ::get_view_ids_should_be_filtered ( & folder ) ;
2024-04-07 15:06:33 +00:00
if view_ids_should_be_filtered . contains ( & view_id ) {
2024-02-09 02:18:52 +00:00
return Err ( FlowyError ::new (
ErrorCode ::RecordNotFound ,
2024-04-09 00:58:53 +00:00
format! ( " View: {} is in trash or other private sections " , view_id ) ,
2024-02-09 02:18:52 +00:00
) ) ;
2023-04-04 00:41:16 +00:00
}
2024-08-18 03:16:42 +00:00
match folder . get_view ( & view_id ) {
2024-02-09 02:18:52 +00:00
None = > {
error! ( " Can't find the view with id: {} " , view_id ) ;
Err ( FlowyError ::record_not_found ( ) )
} ,
2023-06-23 15:19:34 +00:00
Some ( view ) = > {
2023-05-10 11:43:32 +00:00
let child_views = folder
. get_views_belong_to ( & view . id )
2023-04-04 00:41:16 +00:00
. into_iter ( )
2024-04-07 15:06:33 +00:00
. filter ( | view | ! view_ids_should_be_filtered . contains ( & view . id ) )
2023-06-23 15:19:34 +00:00
. collect ::< Vec < _ > > ( ) ;
2023-05-10 11:43:32 +00:00
let view_pb = view_pb_with_child_views ( view , child_views ) ;
2023-04-04 00:41:16 +00:00
Ok ( view_pb )
} ,
}
}
2024-07-15 06:23:20 +00:00
/// Retrieves the views corresponding to the specified view IDs.
///
/// It is important to note that if the target view contains child views,
/// this method only provides access to the first level of child views.
///
/// Therefore, to access a nested child view within one of the initial child views, you must invoke this method
/// again using the ID of the child view you wish to access.
#[ tracing::instrument(level = " debug " , skip(self)) ]
pub async fn get_view_pbs_without_children (
& self ,
view_ids : Vec < String > ,
) -> FlowyResult < Vec < ViewPB > > {
2024-08-18 03:16:42 +00:00
let lock = self
. mutex_folder
. load_full ( )
. ok_or_else ( folder_not_init_error ) ? ;
2024-07-15 06:23:20 +00:00
// trash views and other private views should not be accessed
2024-08-18 03:16:42 +00:00
let folder = lock . read ( ) . await ;
let view_ids_should_be_filtered = Self ::get_view_ids_should_be_filtered ( & folder ) ;
2024-07-15 06:23:20 +00:00
let views = view_ids
. into_iter ( )
. filter_map ( | view_id | {
if view_ids_should_be_filtered . contains ( & view_id ) {
return None ;
}
2024-08-18 03:16:42 +00:00
folder . get_view ( & view_id )
2024-07-15 06:23:20 +00:00
} )
. map ( view_pb_without_child_views_from_arc )
. collect ::< Vec < _ > > ( ) ;
Ok ( views )
}
2024-04-23 13:46:57 +00:00
/// Retrieves all views.
///
/// It is important to note that this will return a flat map of all views,
/// excluding all child views themselves, as they are all at the same level in this
/// map.
///
#[ tracing::instrument(level = " debug " , skip(self)) ]
pub async fn get_all_views_pb ( & self ) -> FlowyResult < Vec < ViewPB > > {
2024-08-18 03:16:42 +00:00
let lock = self
. mutex_folder
. load_full ( )
. ok_or_else ( folder_not_init_error ) ? ;
2024-04-23 13:46:57 +00:00
// trash views and other private views should not be accessed
2024-08-18 03:16:42 +00:00
let folder = lock . read ( ) . await ;
let view_ids_should_be_filtered = Self ::get_view_ids_should_be_filtered ( & folder ) ;
2024-04-23 13:46:57 +00:00
2024-08-18 03:16:42 +00:00
let all_views = folder . get_all_views ( ) ;
2024-04-23 13:46:57 +00:00
let views = all_views
. into_iter ( )
. filter ( | view | ! view_ids_should_be_filtered . contains ( & view . id ) )
. map ( view_pb_without_child_views_from_arc )
. collect ::< Vec < _ > > ( ) ;
Ok ( views )
}
2024-04-09 00:58:53 +00:00
/// Retrieves the ancestors of the view corresponding to the specified view ID, including the view itself.
///
/// For example, if the view hierarchy is as follows:
/// - View A
/// - View B
/// - View C
///
/// If you invoke this method with the ID of View C, it will return a list of views: [View A, View B, View C].
#[ tracing::instrument(level = " debug " , skip(self)) ]
pub async fn get_view_ancestors_pb ( & self , view_id : & str ) -> FlowyResult < Vec < ViewPB > > {
let mut ancestors = vec! [ ] ;
let mut parent_view_id = view_id . to_string ( ) ;
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let folder = lock . read ( ) . await ;
while let Some ( view ) = folder . get_view ( & parent_view_id ) {
// If the view is already in the ancestors list, then break the loop
if ancestors . iter ( ) . any ( | v : & ViewPB | v . id = = view . id ) {
break ;
}
ancestors . push ( view_pb_without_child_views ( view . as_ref ( ) . clone ( ) ) ) ;
parent_view_id = view . parent_view_id . clone ( ) ;
2024-05-21 02:47:31 +00:00
}
2024-08-18 03:16:42 +00:00
ancestors . reverse ( ) ;
2024-04-09 00:58:53 +00:00
}
Ok ( ancestors )
}
2023-06-03 05:40:12 +00:00
/// Move the view to trash. If the view is the current view, then set the current view to empty.
/// When the view is moved to trash, all the child views will be moved to trash as well.
2023-08-02 13:20:51 +00:00
/// All the favorite views being trashed will be unfavorited first to remove it from favorites list as well. The process of unfavoriting concerned view is handled by `unfavorite_view_and_decendants()`
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub async fn move_view_to_trash ( & self , view_id : & str ) -> FlowyResult < ( ) > {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
if let Some ( view ) = folder . get_view ( view_id ) {
Self ::unfavorite_view_and_decendants ( view . clone ( ) , & mut folder ) ;
folder . add_trash_view_ids ( vec! [ view_id . to_string ( ) ] ) ;
drop ( folder ) ;
// notify the parent view that the view is moved to trash
send_notification ( view_id , FolderNotification ::DidMoveViewToTrash )
. payload ( DeletedViewPB {
view_id : view_id . to_string ( ) ,
index : None ,
} )
. send ( ) ;
notify_child_views_changed (
view_pb_without_child_views ( view . as_ref ( ) . clone ( ) ) ,
ChildViewChangeReason ::Delete ,
) ;
}
}
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
2024-08-18 03:16:42 +00:00
fn unfavorite_view_and_decendants ( view : Arc < View > , folder : & mut Folder ) {
2023-08-02 13:20:51 +00:00
let mut all_descendant_views : Vec < Arc < View > > = vec! [ view . clone ( ) ] ;
2024-08-18 03:16:42 +00:00
all_descendant_views . extend ( folder . get_views_belong_to ( & view . id ) ) ;
2023-08-02 13:20:51 +00:00
let favorite_descendant_views : Vec < ViewPB > = all_descendant_views
. iter ( )
. filter ( | view | view . is_favorite )
2024-03-12 02:59:52 +00:00
. map ( | view | view_pb_without_child_views ( view . as_ref ( ) . clone ( ) ) )
2023-08-02 13:20:51 +00:00
. collect ( ) ;
if ! favorite_descendant_views . is_empty ( ) {
2024-03-22 09:15:18 +00:00
folder . delete_favorite_view_ids (
2023-08-02 13:20:51 +00:00
favorite_descendant_views
. iter ( )
. map ( | v | v . id . clone ( ) )
. collect ( ) ,
) ;
send_notification ( " favorite " , FolderNotification ::DidUnfavoriteView )
. payload ( RepeatedViewPB {
items : favorite_descendant_views ,
} )
. send ( ) ;
}
}
2023-07-26 08:49:50 +00:00
/// Moves a nested view to a new location in the hierarchy.
///
/// This function takes the `view_id` of the view to be moved,
/// `new_parent_id` of the view under which the `view_id` should be moved,
/// and an optional `prev_view_id` to position the `view_id` right after
/// this specific view.
///
/// If `prev_view_id` is provided, the moved view will be placed right after
/// the view corresponding to `prev_view_id` under the `new_parent_id`.
/// If `prev_view_id` is `None`, the moved view will become the first child of the new parent.
///
/// # Arguments
///
/// * `view_id` - A string slice that holds the id of the view to be moved.
/// * `new_parent_id` - A string slice that holds the id of the new parent view.
/// * `prev_view_id` - An `Option<String>` that holds the id of the view after which the `view_id` should be positioned.
///
#[ tracing::instrument(level = " trace " , skip(self), err) ]
2024-03-21 04:02:03 +00:00
pub async fn move_nested_view ( & self , params : MoveNestedViewParams ) -> FlowyResult < ( ) > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2024-03-21 04:02:03 +00:00
let view_id = params . view_id ;
let new_parent_id = params . new_parent_id ;
let prev_view_id = params . prev_view_id ;
let from_section = params . from_section ;
let to_section = params . to_section ;
2023-11-01 03:45:35 +00:00
let view = self . get_view_pb ( & view_id ) . await ? ;
2023-07-26 08:49:50 +00:00
let old_parent_id = view . parent_view_id ;
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . move_nested_view ( & view_id , & new_parent_id , prev_view_id ) ;
if from_section ! = to_section {
if to_section = = Some ( ViewSectionPB ::Private ) {
folder . add_private_view_ids ( vec! [ view_id . clone ( ) ] ) ;
} else {
folder . delete_private_view_ids ( vec! [ view_id . clone ( ) ] ) ;
2024-03-21 04:02:03 +00:00
}
2024-08-18 03:16:42 +00:00
}
notify_parent_view_did_change ( & workspace_id , & folder , vec! [ new_parent_id , old_parent_id ] ) ;
}
2023-07-26 08:49:50 +00:00
Ok ( ( ) )
}
2023-06-06 16:05:27 +00:00
/// Move the view with given id from one position to another position.
/// The view will be moved to the new position in the same parent view.
/// The passed in index is the index of the view that displayed in the UI.
/// We need to convert the index to the real index of the view in the parent view.
#[ tracing::instrument(level = " trace " , skip(self), err) ]
2023-04-04 00:41:16 +00:00
pub async fn move_view ( & self , view_id : & str , from : usize , to : usize ) -> FlowyResult < ( ) > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2023-06-06 16:05:27 +00:00
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
let display_views = if is_workspace {
self
. get_current_workspace ( )
. await ?
. views
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < _ > > ( )
} else {
self
2023-11-01 03:45:35 +00:00
. get_view_pb ( & parent_view_id )
2023-06-06 16:05:27 +00:00
. await ?
. child_views
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < _ > > ( )
} ;
2023-04-04 00:41:16 +00:00
2023-06-06 16:05:27 +00:00
if display_views . len ( ) > to {
let to_view_id = display_views [ to ] . clone ( ) ;
// Find the actual index of the view in the parent view
let actual_from_index = child_views . iter ( ) . position ( | id | id = = view_id ) ;
let actual_to_index = child_views . iter ( ) . position ( | id | id = = & to_view_id ) ;
if let ( Some ( actual_from_index ) , Some ( actual_to_index ) ) =
( actual_from_index , actual_to_index )
{
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . move_view ( view_id , actual_from_index as u32 , actual_to_index as u32 ) ;
notify_parent_view_did_change ( & workspace_id , & folder , vec! [ parent_view_id ] ) ;
}
2023-06-06 16:05:27 +00:00
}
}
2023-04-04 00:41:16 +00:00
}
Ok ( ( ) )
}
2023-06-03 05:40:12 +00:00
/// Return a list of views that belong to the given parent view id.
#[ tracing::instrument(level = " debug " , skip(self, parent_view_id), err) ]
2023-06-23 15:19:34 +00:00
pub async fn get_views_belong_to ( & self , parent_view_id : & str ) -> FlowyResult < Vec < Arc < View > > > {
2024-08-18 03:16:42 +00:00
match self . mutex_folder . load_full ( ) {
Some ( folder ) = > Ok ( folder . read ( ) . await . get_views_belong_to ( parent_view_id ) ) ,
None = > Ok ( Vec ::default ( ) ) ,
}
2023-04-04 00:41:16 +00:00
}
2023-06-03 05:40:12 +00:00
/// Update the view with the given params.
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " trace " , skip(self), err) ]
2023-05-10 11:43:32 +00:00
pub async fn update_view_with_params ( & self , params : UpdateViewParams ) -> FlowyResult < ( ) > {
2023-08-04 11:27:14 +00:00
self
. update_view ( & params . view_id , | update | {
2023-04-04 00:41:16 +00:00
update
. set_name_if_not_none ( params . name )
. set_desc_if_not_none ( params . desc )
2023-06-01 12:23:27 +00:00
. set_layout_if_not_none ( params . layout )
2023-08-02 13:20:51 +00:00
. set_favorite_if_not_none ( params . is_favorite )
2024-04-30 08:55:15 +00:00
. set_extra_if_not_none ( params . extra )
2023-04-04 00:41:16 +00:00
. done ( )
2023-08-04 11:27:14 +00:00
} )
. await
}
2023-04-04 00:41:16 +00:00
2023-08-04 11:27:14 +00:00
/// Update the icon of the view with the given params.
#[ tracing::instrument(level = " trace " , skip(self), err) ]
pub async fn update_view_icon_with_params (
& self ,
params : UpdateViewIconParams ,
) -> FlowyResult < ( ) > {
self
. update_view ( & params . view_id , | update | {
update . set_icon ( params . icon ) . done ( )
} )
. await
2023-04-04 00:41:16 +00:00
}
2023-06-03 05:40:12 +00:00
/// Duplicate the view with the given view id.
2024-06-25 02:03:02 +00:00
///
/// Including the view data (icon, cover, extra) and the child views.
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " debug " , skip(self), err) ]
2024-06-25 02:03:02 +00:00
pub ( crate ) async fn duplicate_view ( & self , params : DuplicateViewParams ) -> Result < ( ) , FlowyError > {
2024-08-18 03:16:42 +00:00
let lock = self
. mutex_folder
. load_full ( )
. ok_or_else ( | | FlowyError ::record_not_found ( ) . with_context ( " Can't duplicate the view " ) ) ? ;
let folder = lock . read ( ) . await ;
let view = folder
. get_view ( & params . view_id )
2023-08-21 16:19:15 +00:00
. ok_or_else ( | | FlowyError ::record_not_found ( ) . with_context ( " Can't duplicate the view " ) ) ? ;
2024-08-18 03:16:42 +00:00
// Explicitly drop the folder lock to avoid deadlock when following calls contains 'self'
drop ( folder ) ;
2024-06-27 06:12:52 +00:00
let parent_view_id = params
. parent_view_id
. clone ( )
. unwrap_or ( view . parent_view_id . clone ( ) ) ;
2024-06-25 02:03:02 +00:00
self
. duplicate_view_with_parent_id (
& view . id ,
2024-06-27 06:12:52 +00:00
& parent_view_id ,
2024-06-25 02:03:02 +00:00
params . open_after_duplicate ,
params . include_children ,
2024-06-27 06:12:52 +00:00
params . suffix ,
2024-07-02 05:02:15 +00:00
params . sync_after_create ,
2024-06-25 02:03:02 +00:00
)
. await
}
2023-04-04 00:41:16 +00:00
2024-06-25 02:03:02 +00:00
/// Duplicate the view with the given view id and parent view id.
///
/// If the view id is the same as the parent view id, it will return an error.
/// If the view id is not found, it will return an error.
pub ( crate ) async fn duplicate_view_with_parent_id (
& self ,
view_id : & str ,
parent_view_id : & str ,
open_after_duplicated : bool ,
include_children : bool ,
2024-06-27 06:12:52 +00:00
suffix : Option < String > ,
2024-07-02 05:02:15 +00:00
sync_after_create : bool ,
2024-06-25 02:03:02 +00:00
) -> Result < ( ) , FlowyError > {
if view_id = = parent_view_id {
return Err ( FlowyError ::new (
ErrorCode ::Internal ,
format! ( " Can't duplicate the view( {} ) to itself " , view_id ) ,
) ) ;
}
2023-08-02 13:20:51 +00:00
2024-07-02 05:02:15 +00:00
// filter the view ids that in the trash or private section
2024-08-18 03:16:42 +00:00
let filtered_view_ids = match self . mutex_folder . load_full ( ) {
None = > Vec ::default ( ) ,
Some ( lock ) = > {
let folder = lock . read ( ) . await ;
Self ::get_view_ids_should_be_filtered ( & folder )
} ,
} ;
2023-08-02 13:20:51 +00:00
2024-06-25 02:03:02 +00:00
// only apply the `open_after_duplicated` and the `include_children` to the first view
let mut is_source_view = true ;
// use a stack to duplicate the view and its children
let mut stack = vec! [ ( view_id . to_string ( ) , parent_view_id . to_string ( ) ) ] ;
2024-07-02 05:02:15 +00:00
let mut objects = vec! [ ] ;
2024-06-27 06:12:52 +00:00
let suffix = suffix . unwrap_or ( " (copy) " . to_string ( ) ) ;
2024-03-22 09:15:18 +00:00
2024-08-18 03:16:42 +00:00
let lock = match self . mutex_folder . load_full ( ) {
None = > {
return Err (
FlowyError ::record_not_found ( )
. with_context ( format! ( " Can't duplicate the view( {} ) " , view_id ) ) ,
)
} ,
Some ( lock ) = > lock ,
} ;
2024-06-25 02:03:02 +00:00
while let Some ( ( current_view_id , current_parent_id ) ) = stack . pop ( ) {
2024-08-18 03:16:42 +00:00
let view = lock
. read ( )
. await
. get_view ( & current_view_id )
2024-06-25 02:03:02 +00:00
. ok_or_else ( | | {
FlowyError ::record_not_found ( )
. with_context ( format! ( " Can't duplicate the view( {} ) " , view_id ) )
} ) ? ;
let handler = self . get_handler ( & view . layout ) ? ;
let view_data = handler . duplicate_view ( & view . id ) . await ? ;
let index = self
. get_view_relation ( & current_parent_id )
. await
. and_then ( | ( _ , _ , views ) | {
views
. iter ( )
. filter ( | id | filtered_view_ids . contains ( id ) )
. position ( | id | * id = = current_view_id )
. map ( | i | i as u32 )
} ) ;
2024-08-18 03:16:42 +00:00
let section = {
let folder = lock . read ( ) . await ;
if folder . is_view_in_section ( Section ::Private , & view . id ) {
ViewSectionPB ::Private
} else {
ViewSectionPB ::Public
}
} ;
2024-06-25 02:03:02 +00:00
let name = if is_source_view {
2024-06-27 06:12:52 +00:00
format! ( " {} {} " , & view . name , suffix )
2024-06-25 02:03:02 +00:00
} else {
view . name . clone ( )
} ;
2024-07-02 05:02:15 +00:00
2024-06-25 02:03:02 +00:00
let duplicate_params = CreateViewParams {
parent_view_id : current_parent_id . clone ( ) ,
name ,
desc : view . desc . clone ( ) ,
layout : view . layout . clone ( ) . into ( ) ,
initial_data : view_data . to_vec ( ) ,
view_id : gen_view_id ( ) . to_string ( ) ,
meta : Default ::default ( ) ,
set_as_current : is_source_view & & open_after_duplicated ,
index ,
section : Some ( section ) ,
extra : view . extra . clone ( ) ,
icon : view . icon . clone ( ) ,
} ;
2024-07-02 05:02:15 +00:00
// set the notify_workspace_update to false to avoid multiple notifications
let ( duplicated_view , encoded_collab ) = self
. create_view_with_params ( duplicate_params , false )
. await ? ;
if sync_after_create {
if let Some ( encoded_collab ) = encoded_collab {
let object_id = duplicated_view . id . clone ( ) ;
let collab_type = match duplicated_view . layout {
ViewLayout ::Document = > CollabType ::Document ,
ViewLayout ::Board | ViewLayout ::Grid | ViewLayout ::Calendar = > CollabType ::Database ,
ViewLayout ::Chat = > CollabType ::Unknown ,
} ;
// don't block the whole import process if the view can't be encoded
if collab_type ! = CollabType ::Unknown {
match self . get_folder_collab_params ( object_id , collab_type , encoded_collab ) {
Ok ( params ) = > objects . push ( params ) ,
Err ( e ) = > {
error! ( " duplicate error {} " , e ) ;
} ,
}
}
}
}
2024-06-25 02:03:02 +00:00
if include_children {
let child_views = self . get_views_belong_to ( & current_view_id ) . await ? ;
// reverse the child views to keep the order
for child_view in child_views . iter ( ) . rev ( ) {
// skip the view_id should be filtered and the child_view is the duplicated view
2024-07-02 06:35:29 +00:00
if ! filtered_view_ids . contains ( & child_view . id ) & & child_view . layout ! = ViewLayout ::Chat {
2024-06-25 02:03:02 +00:00
stack . push ( ( child_view . id . clone ( ) , duplicated_view . id . clone ( ) ) ) ;
}
}
}
is_source_view = false
}
2023-04-04 00:41:16 +00:00
2024-07-02 05:02:15 +00:00
let workspace_id = & self . user . workspace_id ( ) ? ;
// Sync the view to the cloud
if sync_after_create {
self
. cloud_service
. batch_create_folder_collab_objects ( workspace_id , objects )
. await ? ;
}
// notify the update here
2024-08-18 03:16:42 +00:00
let folder = lock . read ( ) . await ;
notify_parent_view_did_change ( workspace_id , & folder , vec! [ parent_view_id . to_string ( ) ] ) ;
2024-07-02 05:02:15 +00:00
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
#[ tracing::instrument(level = " trace " , skip(self), err) ]
2024-08-18 03:16:42 +00:00
pub ( crate ) async fn set_current_view ( & self , view_id : String ) -> Result < ( ) , FlowyError > {
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . set_current_view ( view_id . clone ( ) ) ;
folder . add_recent_view_ids ( vec! [ view_id . clone ( ) ] ) ;
} else {
return Err ( FlowyError ::record_not_found ( ) ) ;
}
2023-04-04 00:41:16 +00:00
2024-03-30 08:28:24 +00:00
let view = self . get_current_view ( ) . await ;
if let Some ( view ) = & view {
let view_layout : ViewLayout = view . layout . clone ( ) . into ( ) ;
if let Some ( handle ) = self . operation_handlers . get ( & view_layout ) {
2024-06-03 06:27:28 +00:00
info! ( " Open view: {} " , view . id ) ;
2024-08-18 03:16:42 +00:00
if let Err ( err ) = handle . open_view ( & view_id ) . await {
2024-06-03 06:27:28 +00:00
error! ( " Open view error: {:?} " , err ) ;
}
2024-03-30 08:28:24 +00:00
}
}
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2024-08-06 05:17:56 +00:00
let setting = WorkspaceSettingPB {
workspace_id ,
latest_view : view ,
} ;
send_current_workspace_notification ( FolderNotification ::DidUpdateWorkspaceSetting , setting ) ;
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
#[ tracing::instrument(level = " trace " , skip(self)) ]
pub ( crate ) async fn get_current_view ( & self ) -> Option < ViewPB > {
2024-08-18 03:16:42 +00:00
let view_id = {
let lock = self . mutex_folder . load_full ( ) ? ;
let folder = lock . read ( ) . await ;
let view = folder . get_current_view ( ) ? ;
drop ( folder ) ;
view
} ;
2023-11-01 03:45:35 +00:00
self . get_view_pb ( & view_id ) . await . ok ( )
2023-04-04 00:41:16 +00:00
}
2023-08-02 13:20:51 +00:00
/// Toggles the favorite status of a view identified by `view_id`If the view is not a favorite, it will be added to the favorites list; otherwise, it will be removed from the list.
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub async fn toggle_favorites ( & self , view_id : & str ) -> FlowyResult < ( ) > {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
if let Some ( old_view ) = folder . get_view ( view_id ) {
if old_view . is_favorite {
folder . delete_favorite_view_ids ( vec! [ view_id . to_string ( ) ] ) ;
} else {
folder . add_favorite_view_ids ( vec! [ view_id . to_string ( ) ] ) ;
2023-08-02 13:20:51 +00:00
}
2024-08-18 03:16:42 +00:00
}
}
2023-08-02 13:20:51 +00:00
self . send_toggle_favorite_notification ( view_id ) . await ;
Ok ( ( ) )
}
2023-12-01 01:58:36 +00:00
/// Add the view to the recent view list / history.
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub async fn add_recent_views ( & self , view_ids : Vec < String > ) -> FlowyResult < ( ) > {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . add_recent_view_ids ( view_ids ) ;
}
2023-12-01 01:58:36 +00:00
self . send_update_recent_views_notification ( ) . await ;
Ok ( ( ) )
}
/// Add the view to the recent view list / history.
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub async fn remove_recent_views ( & self , view_ids : Vec < String > ) -> FlowyResult < ( ) > {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . delete_recent_view_ids ( view_ids ) ;
}
2023-12-01 01:58:36 +00:00
self . send_update_recent_views_notification ( ) . await ;
Ok ( ( ) )
}
2024-07-22 05:35:42 +00:00
/// Publishes a view identified by the given `view_id`.
///
/// If `publish_name` is `None`, a default name will be generated using the view name and view id.
2024-07-08 05:45:57 +00:00
#[ tracing::instrument(level = " debug " , skip(self), err) ]
2024-07-22 05:35:42 +00:00
pub async fn publish_view (
& self ,
view_id : & str ,
publish_name : Option < String > ,
selected_view_ids : Option < Vec < String > > ,
) -> FlowyResult < ( ) > {
2024-08-18 03:16:42 +00:00
let view = {
let lock = match self . mutex_folder . load_full ( ) {
None = > {
return Err (
FlowyError ::record_not_found ( )
. with_context ( format! ( " Can't find the view with ID: {} " , view_id ) ) ,
)
} ,
Some ( lock ) = > lock ,
} ;
let read_guard = lock . read ( ) . await ;
read_guard . get_view ( view_id ) . ok_or_else ( | | {
2024-07-22 05:35:42 +00:00
FlowyError ::record_not_found ( )
. with_context ( format! ( " Can't find the view with ID: {} " , view_id ) )
2024-08-18 03:16:42 +00:00
} ) ?
} ;
2024-07-08 05:45:57 +00:00
2024-07-22 05:35:42 +00:00
if view . layout = = ViewLayout ::Chat {
2024-07-08 05:45:57 +00:00
return Err ( FlowyError ::new (
ErrorCode ::NotSupportYet ,
2024-07-22 05:35:42 +00:00
" The chat view is not supported to publish. " . to_string ( ) ,
2024-07-08 05:45:57 +00:00
) ) ;
}
2024-07-22 05:35:42 +00:00
// Retrieve the view payload and its child views recursively
2024-07-08 05:45:57 +00:00
let payload = self
2024-07-22 05:35:42 +00:00
. get_batch_publish_payload ( view_id , publish_name , false )
2024-07-08 05:45:57 +00:00
. await ? ;
2024-07-22 05:35:42 +00:00
// set the selected view ids to the payload
let payload = if let Some ( selected_view_ids ) = selected_view_ids {
payload
. into_iter ( )
. map ( | mut p | {
if let PublishPayload ::Database ( p ) = & mut p {
p . data . visible_database_view_ids = selected_view_ids . clone ( ) ;
}
p
} )
. collect ::< Vec < _ > > ( )
} else {
payload
} ;
2024-07-08 05:45:57 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
self
. cloud_service
. publish_view ( workspace_id . as_str ( ) , payload )
. await ? ;
Ok ( ( ) )
}
/// Unpublish the view with the given view id.
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub async fn unpublish_views ( & self , view_ids : Vec < String > ) -> FlowyResult < ( ) > {
let workspace_id = self . user . workspace_id ( ) ? ;
self
. cloud_service
. unpublish_views ( workspace_id . as_str ( ) , view_ids )
. await ? ;
Ok ( ( ) )
}
/// Get the publish info of the view with the given view id.
/// The publish info contains the namespace and publish_name of the view.
2024-07-22 07:43:48 +00:00
#[ tracing::instrument(level = " debug " , skip(self)) ]
2024-07-08 05:45:57 +00:00
pub async fn get_publish_info ( & self , view_id : & str ) -> FlowyResult < PublishInfoResponse > {
let publish_info = self . cloud_service . get_publish_info ( view_id ) . await ? ;
Ok ( publish_info )
}
/// Get the namespace of the current workspace.
/// The namespace is used to generate the URL of the published view.
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub async fn set_publish_namespace ( & self , namespace : String ) -> FlowyResult < ( ) > {
let workspace_id = self . user . workspace_id ( ) ? ;
self
. cloud_service
. set_publish_namespace ( workspace_id . as_str ( ) , namespace . as_str ( ) )
. await ? ;
Ok ( ( ) )
}
/// Get the namespace of the current workspace.
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub async fn get_publish_namespace ( & self ) -> FlowyResult < String > {
let workspace_id = self . user . workspace_id ( ) ? ;
let namespace = self
. cloud_service
. get_publish_namespace ( workspace_id . as_str ( ) )
. await ? ;
Ok ( namespace )
}
2024-07-22 05:35:42 +00:00
/// Retrieves the publishing payload for a specified view and optionally its child views.
///
/// # Arguments
/// * `view_id` - The ID of the view to publish.
/// * `publish_name` - Optional name for the published view.
/// * `include_children` - Flag to include child views in the payload.
2024-07-08 05:45:57 +00:00
pub async fn get_batch_publish_payload (
& self ,
view_id : & str ,
publish_name : Option < String > ,
2024-07-22 05:35:42 +00:00
include_children : bool ,
) -> FlowyResult < Vec < PublishPayload > > {
2024-07-08 05:45:57 +00:00
let mut stack = vec! [ view_id . to_string ( ) ] ;
let mut payloads = Vec ::new ( ) ;
while let Some ( current_view_id ) = stack . pop ( ) {
let view = match self . get_view_pb ( & current_view_id ) . await {
Ok ( view ) = > view ,
Err ( _ ) = > continue ,
} ;
2024-07-22 05:35:42 +00:00
// Skip the chat view
if view . layout = = ViewLayoutPB ::Chat {
2024-07-08 05:45:57 +00:00
continue ;
2024-07-22 05:35:42 +00:00
}
let layout : ViewLayout = view . layout . into ( ) ;
2024-07-08 05:45:57 +00:00
// Only support set the publish_name for the current view, not for the child views
let publish_name = if current_view_id = = view_id {
publish_name . clone ( )
} else {
None
} ;
2024-07-22 05:35:42 +00:00
if let Ok ( payload ) = self
2024-07-08 05:45:57 +00:00
. get_publish_payload ( & current_view_id , publish_name , layout )
2024-07-22 05:35:42 +00:00
. await
{
2024-07-08 05:45:57 +00:00
payloads . push ( payload ) ;
}
2024-07-22 05:35:42 +00:00
if include_children {
2024-07-08 05:45:57 +00:00
// Add the child views to the stack
2024-07-22 05:35:42 +00:00
stack . extend ( view . child_views . iter ( ) . map ( | child | child . id . clone ( ) ) ) ;
2024-07-08 05:45:57 +00:00
}
}
Ok ( payloads )
}
async fn build_publish_views ( & self , view_id : & str ) -> Option < PublishViewInfo > {
let view_pb = self . get_view_pb ( view_id ) . await . ok ( ) ? ;
let mut child_views_futures = vec! [ ] ;
for child in & view_pb . child_views {
let future = self . build_publish_views ( & child . id ) ;
child_views_futures . push ( future ) ;
}
let child_views = future ::join_all ( child_views_futures )
. await
. into_iter ( )
. flatten ( )
. collect ::< Vec < PublishViewInfo > > ( ) ;
let view_child_views = if child_views . is_empty ( ) {
None
} else {
Some ( child_views )
} ;
let view = view_pb_to_publish_view ( & view_pb ) ;
let view = PublishViewInfo {
child_views : view_child_views ,
.. view
} ;
Some ( view )
}
2024-07-22 05:35:42 +00:00
2024-07-08 05:45:57 +00:00
async fn get_publish_payload (
& self ,
view_id : & str ,
publish_name : Option < String > ,
layout : ViewLayout ,
2024-07-22 05:35:42 +00:00
) -> FlowyResult < PublishPayload > {
let handler : Arc < dyn FolderOperationHandler + Sync + Send > = self . get_handler ( & layout ) ? ;
let encoded_collab_wrapper : EncodedCollabWrapper = handler
. get_encoded_collab_v1_from_disk ( self . user . clone ( ) , view_id )
. await ? ;
let view = self . get_view_pb ( view_id ) . await ? ;
2024-07-08 05:45:57 +00:00
let publish_name = publish_name . unwrap_or_else ( | | generate_publish_name ( & view . id , & view . name ) ) ;
let child_views = self
. build_publish_views ( view_id )
. await
2024-07-22 05:35:42 +00:00
. and_then ( | v | v . child_views )
. unwrap_or_default ( ) ;
2024-07-08 05:45:57 +00:00
let ancestor_views = self
. get_view_ancestors_pb ( view_id )
. await ?
. iter ( )
. map ( view_pb_to_publish_view )
. collect ::< Vec < PublishViewInfo > > ( ) ;
let metadata = PublishViewMetaData {
2024-07-22 05:35:42 +00:00
view : view_pb_to_publish_view ( & view ) ,
2024-07-08 05:45:57 +00:00
child_views ,
ancestor_views ,
} ;
let meta = PublishViewMeta {
view_id : view . id . clone ( ) ,
publish_name ,
metadata ,
} ;
2024-07-22 05:35:42 +00:00
let payload = match encoded_collab_wrapper {
EncodedCollabWrapper ::Database ( v ) = > {
let database_collab = v . database_encoded_collab . doc_state . to_vec ( ) ;
let database_relations = v . database_relations ;
let database_row_collabs = v
. database_row_encoded_collabs
. into_iter ( )
. map ( | v | ( v . 0 , v . 1. doc_state . to_vec ( ) ) ) // Convert to HashMap
. collect ::< HashMap < String , Vec < u8 > > > ( ) ;
let data = PublishDatabaseData {
database_collab ,
database_row_collabs ,
database_relations ,
.. Default ::default ( )
} ;
PublishPayload ::Database ( PublishDatabasePayload { meta , data } )
} ,
EncodedCollabWrapper ::Document ( v ) = > {
let data = v . document_encoded_collab . doc_state . to_vec ( ) ;
PublishPayload ::Document ( PublishDocumentPayload { meta , data } )
} ,
EncodedCollabWrapper ::Unknown = > PublishPayload ::Unknown ,
} ;
Ok ( payload )
2024-07-08 05:45:57 +00:00
}
2023-08-02 13:20:51 +00:00
// Used by toggle_favorites to send notification to frontend, after the favorite status of view has been changed.It sends two distinct notifications: one to correctly update the concerned view's is_favorite status, and another to update the list of favorites that is to be displayed.
async fn send_toggle_favorite_notification ( & self , view_id : & str ) {
2023-11-01 03:45:35 +00:00
if let Ok ( view ) = self . get_view_pb ( view_id ) . await {
2023-08-02 13:20:51 +00:00
let notification_type = if view . is_favorite {
FolderNotification ::DidFavoriteView
} else {
FolderNotification ::DidUnfavoriteView
} ;
send_notification ( " favorite " , notification_type )
. payload ( RepeatedViewPB {
items : vec ! [ view . clone ( ) ] ,
} )
. send ( ) ;
send_notification ( & view . id , FolderNotification ::DidUpdateView )
. payload ( view )
. send ( )
}
}
2023-12-01 01:58:36 +00:00
async fn send_update_recent_views_notification ( & self ) {
2024-04-02 01:57:14 +00:00
let recent_views = self . get_my_recent_sections ( ) . await ;
2023-12-01 01:58:36 +00:00
send_notification ( " recent_views " , FolderNotification ::DidUpdateRecentViews )
. payload ( RepeatedViewIdPB {
items : recent_views . into_iter ( ) . map ( | item | item . id ) . collect ( ) ,
} )
. send ( ) ;
}
2023-08-02 13:20:51 +00:00
#[ tracing::instrument(level = " trace " , skip(self)) ]
2023-11-01 06:47:25 +00:00
pub ( crate ) async fn get_all_favorites ( & self ) -> Vec < SectionItem > {
2024-08-18 03:16:42 +00:00
self . get_sections ( Section ::Favorite ) . await
2023-11-13 02:07:46 +00:00
}
2023-08-02 13:20:51 +00:00
2024-04-02 01:57:14 +00:00
#[ tracing::instrument(level = " debug " , skip(self)) ]
pub ( crate ) async fn get_my_recent_sections ( & self ) -> Vec < SectionItem > {
2024-08-18 03:16:42 +00:00
self . get_sections ( Section ::Recent ) . await
2023-08-02 13:20:51 +00:00
}
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " trace " , skip(self)) ]
2024-03-22 09:15:18 +00:00
pub ( crate ) async fn get_my_trash_info ( & self ) -> Vec < TrashInfo > {
2024-08-18 03:16:42 +00:00
match self . mutex_folder . load_full ( ) {
None = > Vec ::default ( ) ,
Some ( folder ) = > folder . read ( ) . await . get_my_trash_info ( ) ,
}
2023-04-04 00:41:16 +00:00
}
#[ tracing::instrument(level = " trace " , skip(self)) ]
pub ( crate ) async fn restore_all_trash ( & self ) {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . remove_all_my_trash_sections ( ) ;
send_notification ( " trash " , FolderNotification ::DidUpdateTrash )
. payload ( RepeatedTrashPB { items : vec ! [ ] } )
. send ( ) ;
}
2023-04-04 00:41:16 +00:00
}
#[ tracing::instrument(level = " trace " , skip(self)) ]
pub ( crate ) async fn restore_trash ( & self , trash_id : & str ) {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . delete_trash_view_ids ( vec! [ trash_id . to_string ( ) ] ) ;
}
2023-04-04 00:41:16 +00:00
}
2023-06-05 01:42:11 +00:00
/// Delete all the trash permanently.
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " trace " , skip(self)) ]
2024-03-22 09:15:18 +00:00
pub ( crate ) async fn delete_my_trash ( & self ) {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let deleted_trash = lock . read ( ) . await . get_my_trash_info ( ) ;
// Explicitly drop the folder lock to avoid deadlock when following calls contains 'self'
drop ( lock ) ;
for trash in deleted_trash {
let _ = self . delete_trash ( & trash . id ) . await ;
}
send_notification ( " trash " , FolderNotification ::DidUpdateTrash )
. payload ( RepeatedTrashPB { items : vec ! [ ] } )
. send ( ) ;
2023-06-05 01:42:11 +00:00
}
2023-04-04 00:41:16 +00:00
}
2023-06-05 01:42:11 +00:00
/// Delete the trash permanently.
/// Delete the view will delete all the resources that the view holds. For example, if the view
/// is a database view. Then the database will be deleted as well.
#[ tracing::instrument(level = " debug " , skip(self, view_id), err) ]
pub async fn delete_trash ( & self , view_id : & str ) -> FlowyResult < ( ) > {
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let view = {
let mut folder = lock . write ( ) . await ;
let view = folder . get_view ( view_id ) ;
2024-03-22 09:15:18 +00:00
folder . delete_trash_view_ids ( vec! [ view_id . to_string ( ) ] ) ;
2024-08-18 03:16:42 +00:00
folder . delete_views ( vec! [ view_id ] ) ;
view
} ;
if let Some ( view ) = view {
if let Ok ( handler ) = self . get_handler ( & view . layout ) {
handler . delete_view ( view_id ) . await ? ;
}
2023-06-05 01:42:11 +00:00
}
}
Ok ( ( ) )
}
2024-07-01 06:44:08 +00:00
/// Imports a single file to the folder and returns the encoded collab for immediate cloud sync.
pub ( crate ) async fn import_single_file (
& self ,
parent_view_id : String ,
import_data : ImportValue ,
) -> FlowyResult < ( View , Option < EncodedCollab > ) > {
// Ensure either data or file_path is provided
2023-05-31 06:08:54 +00:00
if import_data . data . is_none ( ) & & import_data . file_path . is_none ( ) {
return Err ( FlowyError ::new (
2023-07-05 12:57:09 +00:00
ErrorCode ::InvalidParams ,
2024-07-01 06:44:08 +00:00
" Either data or file_path is required " ,
2023-05-31 06:08:54 +00:00
) ) ;
}
let handler = self . get_handler ( & import_data . view_layout ) ? ;
2023-08-28 05:28:24 +00:00
let view_id = gen_view_id ( ) . to_string ( ) ;
2023-08-17 15:46:39 +00:00
let uid = self . user . user_id ( ) ? ;
2024-07-01 06:44:08 +00:00
let mut encoded_collab : Option < EncodedCollab > = None ;
// Import data from bytes if available
2023-05-31 06:08:54 +00:00
if let Some ( data ) = import_data . data {
2024-07-01 06:44:08 +00:00
encoded_collab = Some (
handler
. import_from_bytes (
uid ,
& view_id ,
& import_data . name ,
import_data . import_type ,
data ,
)
. await ? ,
) ;
2023-05-31 06:08:54 +00:00
}
2024-07-01 06:44:08 +00:00
// Import data from file path if available
2023-05-31 06:08:54 +00:00
if let Some ( file_path ) = import_data . file_path {
handler
. import_from_file_path ( & view_id , & import_data . name , file_path )
. await ? ;
}
let params = CreateViewParams {
2024-07-01 06:44:08 +00:00
parent_view_id ,
2023-05-31 06:08:54 +00:00
name : import_data . name ,
desc : " " . to_string ( ) ,
layout : import_data . view_layout . clone ( ) . into ( ) ,
initial_data : vec ! [ ] ,
view_id ,
meta : Default ::default ( ) ,
2023-06-03 05:40:12 +00:00
set_as_current : false ,
2023-08-02 13:20:51 +00:00
index : None ,
2024-03-22 09:15:18 +00:00
section : None ,
2024-06-25 02:03:02 +00:00
extra : None ,
icon : None ,
2023-05-31 06:08:54 +00:00
} ;
2023-11-08 13:48:17 +00:00
let view = create_view ( self . user . user_id ( ) ? , params , import_data . view_layout ) ;
2024-07-01 06:44:08 +00:00
// Insert the new view into the folder
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
folder . insert_view ( view . clone ( ) , None ) ;
}
2024-07-01 06:44:08 +00:00
Ok ( ( view , encoded_collab ) )
}
/// Import function to handle the import of data.
pub ( crate ) async fn import ( & self , import_data : ImportParams ) -> FlowyResult < RepeatedViewPB > {
let workspace_id = self . user . workspace_id ( ) ? ;
// Initialize an empty vector to store the objects
let sync_after_create = import_data . sync_after_create ;
let mut objects = vec! [ ] ;
let mut views = vec! [ ] ;
// Iterate over the values in the import data
for data in import_data . values {
let collab_type = data . import_type . clone ( ) . into ( ) ;
// Import a single file and get the view and encoded collab data
let ( view , encoded_collab ) = self
. import_single_file ( import_data . parent_view_id . clone ( ) , data )
. await ? ;
let object_id = view . id . clone ( ) ;
views . push ( view_pb_without_child_views ( view ) ) ;
if sync_after_create {
if let Some ( encoded_collab ) = encoded_collab {
2024-07-02 05:02:15 +00:00
// don't block the whole import process if the view can't be encoded
match self . get_folder_collab_params ( object_id , collab_type , encoded_collab ) {
Ok ( params ) = > objects . push ( params ) ,
2024-07-01 06:44:08 +00:00
Err ( e ) = > {
2024-07-02 05:02:15 +00:00
error! ( " import error {} " , e ) ;
2024-07-01 06:44:08 +00:00
} ,
}
}
}
}
// Sync the view to the cloud
if sync_after_create {
self
. cloud_service
. batch_create_folder_collab_objects ( & workspace_id , objects )
. await ? ;
}
// Notify that the parent view has changed
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let folder = lock . read ( ) . await ;
notify_parent_view_did_change ( & workspace_id , & folder , vec! [ import_data . parent_view_id ] ) ;
}
2024-07-01 06:44:08 +00:00
Ok ( RepeatedViewPB { items : views } )
2023-05-31 06:08:54 +00:00
}
2023-08-04 11:27:14 +00:00
/// Update the view with the provided view_id using the specified function.
async fn update_view < F > ( & self , view_id : & str , f : F ) -> FlowyResult < ( ) >
where
F : FnOnce ( ViewUpdate ) -> Option < View > ,
{
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) ? ;
2024-08-18 03:16:42 +00:00
let value = match self . mutex_folder . load_full ( ) {
None = > None ,
Some ( lock ) = > {
let mut folder = lock . write ( ) . await ;
let old_view = folder . get_view ( view_id ) ;
let new_view = folder . update_view ( view_id , f ) ;
2023-08-04 11:27:14 +00:00
2023-08-21 16:19:15 +00:00
Some ( ( old_view , new_view ) )
} ,
2024-08-18 03:16:42 +00:00
} ;
2023-08-04 11:27:14 +00:00
if let Some ( ( Some ( old_view ) , Some ( new_view ) ) ) = value {
if let Ok ( handler ) = self . get_handler ( & old_view . layout ) {
handler . did_update_view ( & old_view , & new_view ) . await ? ;
}
}
2023-11-01 03:45:35 +00:00
if let Ok ( view_pb ) = self . get_view_pb ( view_id ) . await {
2023-08-04 11:27:14 +00:00
send_notification ( & view_pb . id , FolderNotification ::DidUpdateView )
. payload ( view_pb )
. send ( ) ;
2024-03-04 17:24:49 +00:00
2024-08-18 03:16:42 +00:00
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let folder = lock . read ( ) . await ;
notify_did_update_workspace ( & workspace_id , & folder ) ;
2024-03-04 17:24:49 +00:00
}
2023-08-04 11:27:14 +00:00
}
2024-03-04 17:24:49 +00:00
2023-08-04 11:27:14 +00:00
Ok ( ( ) )
}
2023-05-31 06:08:54 +00:00
/// Returns a handler that implements the [FolderOperationHandler] trait
fn get_handler (
2023-04-04 00:41:16 +00:00
& self ,
view_layout : & ViewLayout ,
2023-05-31 06:08:54 +00:00
) -> FlowyResult < Arc < dyn FolderOperationHandler + Send + Sync > > {
match self . operation_handlers . get ( view_layout ) {
2023-08-21 16:19:15 +00:00
None = > Err ( FlowyError ::internal ( ) . with_context ( format! (
2023-04-04 00:41:16 +00:00
" Get data processor failed. Unknown layout type: {:?} " ,
view_layout
) ) ) ,
Some ( processor ) = > Ok ( processor . clone ( ) ) ,
}
}
2023-06-06 16:05:27 +00:00
2024-07-02 05:02:15 +00:00
fn get_folder_collab_params (
& self ,
object_id : String ,
collab_type : CollabType ,
encoded_collab : EncodedCollab ,
) -> FlowyResult < FolderCollabParams > {
// Try to encode the collaboration data to bytes
let encoded_collab_v1 : Result < Vec < u8 > , FlowyError > =
encoded_collab . encode_to_bytes ( ) . map_err ( internal_error ) ;
encoded_collab_v1 . map ( | encoded_collab_v1 | FolderCollabParams {
object_id ,
encoded_collab_v1 ,
collab_type ,
} )
}
2023-06-06 16:05:27 +00:00
/// Returns the relation of the view. The relation is a tuple of (is_workspace, parent_view_id,
/// child_view_ids). If the view is a workspace, then the parent_view_id is the workspace id.
/// 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 > ) > {
2024-04-26 01:44:07 +00:00
let workspace_id = self . user . workspace_id ( ) . ok ( ) ? ;
2024-08-18 03:16:42 +00:00
let lock = self . mutex_folder . load_full ( ) ? ;
let folder = lock . read ( ) . await ;
let view = folder . get_view ( view_id ) ? ;
match folder . get_view ( & view . parent_view_id ) {
None = > folder . get_workspace_info ( & workspace_id ) . map ( | workspace | {
(
true ,
workspace . id ,
workspace
. child_views
. items
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < String > > ( ) ,
)
} ) ,
Some ( parent_view ) = > Some ( (
false ,
parent_view . id . clone ( ) ,
parent_view
. children
. items
. clone ( )
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < String > > ( ) ,
) ) ,
}
2023-06-06 16:05:27 +00:00
}
2023-07-05 12:57:09 +00:00
pub async fn get_folder_snapshots (
& self ,
workspace_id : & str ,
2023-08-17 15:46:39 +00:00
limit : usize ,
2023-07-05 12:57:09 +00:00
) -> FlowyResult < Vec < FolderSnapshotPB > > {
2023-08-17 15:46:39 +00:00
let snapshots = self
2023-07-05 12:57:09 +00:00
. cloud_service
2023-08-17 15:46:39 +00:00
. get_folder_snapshots ( workspace_id , limit )
2023-07-05 12:57:09 +00:00
. await ?
2023-08-17 15:46:39 +00:00
. into_iter ( )
2023-07-05 12:57:09 +00:00
. map ( | snapshot | FolderSnapshotPB {
snapshot_id : snapshot . snapshot_id ,
snapshot_desc : " " . to_string ( ) ,
created_at : snapshot . created_at ,
data : snapshot . data ,
} )
2023-08-17 15:46:39 +00:00
. collect ::< Vec < _ > > ( ) ;
2023-07-05 12:57:09 +00:00
Ok ( snapshots )
}
2024-08-18 03:16:42 +00:00
pub async fn set_views_visibility ( & self , view_ids : Vec < String > , is_public : bool ) {
if let Some ( lock ) = self . mutex_folder . load_full ( ) {
let mut folder = lock . write ( ) . await ;
if is_public {
folder . delete_private_view_ids ( view_ids ) ;
} else {
folder . add_private_view_ids ( view_ids ) ;
}
}
2024-04-01 06:27:29 +00:00
}
2024-04-02 01:57:14 +00:00
/// Only support getting the Favorite and Recent sections.
2024-08-18 03:16:42 +00:00
async fn get_sections ( & self , section_type : Section ) -> Vec < SectionItem > {
match self . mutex_folder . load_full ( ) {
None = > Vec ::default ( ) ,
Some ( lock ) = > {
let folder = lock . read ( ) . await ;
let views = match section_type {
Section ::Favorite = > folder . get_my_favorite_sections ( ) ,
Section ::Recent = > folder . get_my_recent_sections ( ) ,
_ = > vec! [ ] ,
} ;
let view_ids_should_be_filtered = Self ::get_view_ids_should_be_filtered ( & folder ) ;
views
. into_iter ( )
. filter ( | view | ! view_ids_should_be_filtered . contains ( & view . id ) )
. collect ( )
} ,
}
2023-11-13 02:07:46 +00:00
}
2024-04-02 01:57:14 +00:00
/// Get all the view that are in the trash, including the child views of the child views.
/// For example, if A view which is in the trash has a child view B, this function will return
/// both A and B.
2024-08-18 03:16:42 +00:00
fn get_all_trash_ids ( folder : & Folder ) -> Vec < String > {
2024-04-02 01:57:14 +00:00
let trash_ids = folder
. get_all_trash_sections ( )
. into_iter ( )
. map ( | trash | trash . id )
. collect ::< Vec < String > > ( ) ;
let mut all_trash_ids = trash_ids . clone ( ) ;
for trash_id in trash_ids {
all_trash_ids . extend ( get_all_child_view_ids ( folder , & trash_id ) ) ;
}
all_trash_ids
}
/// Filter the views that are in the trash and belong to the other private sections.
2024-08-18 03:16:42 +00:00
fn get_view_ids_should_be_filtered ( folder : & Folder ) -> Vec < String > {
let trash_ids = Self ::get_all_trash_ids ( folder ) ;
let other_private_view_ids = Self ::get_other_private_view_ids ( folder ) ;
2024-04-02 01:57:14 +00:00
[ trash_ids , other_private_view_ids ] . concat ( )
}
2024-08-18 03:16:42 +00:00
fn get_other_private_view_ids ( folder : & Folder ) -> Vec < String > {
2024-04-02 01:57:14 +00:00
let my_private_view_ids = folder
. get_my_private_sections ( )
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < String > > ( ) ;
let all_private_view_ids = folder
. get_all_private_sections ( )
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < String > > ( ) ;
all_private_view_ids
. into_iter ( )
. filter ( | id | ! my_private_view_ids . contains ( id ) )
. collect ( )
}
2024-06-05 11:44:32 +00:00
pub fn remove_indices_for_workspace ( & self , workspace_id : String ) -> FlowyResult < ( ) > {
self
. folder_indexer
. remove_indices_for_workspace ( workspace_id ) ? ;
Ok ( ( ) )
}
2023-04-04 00:41:16 +00:00
}
2024-03-21 04:02:03 +00:00
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.
2024-04-26 01:44:07 +00:00
pub ( crate ) fn get_workspace_public_view_pbs ( workspace_id : & str , folder : & Folder ) -> Vec < ViewPB > {
2024-03-21 04:02:03 +00:00
// get the trash ids
let trash_ids = folder
2024-03-22 09:15:18 +00:00
. get_all_trash_sections ( )
2023-04-04 06:08:50 +00:00
. into_iter ( )
. map ( | trash | trash . id )
. collect ::< Vec < String > > ( ) ;
2024-03-21 04:02:03 +00:00
// get the private view ids
let private_view_ids = folder
2024-03-22 09:15:18 +00:00
. get_all_private_sections ( )
2024-03-21 04:02:03 +00:00
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < String > > ( ) ;
2024-08-18 03:16:42 +00:00
let mut views = folder . get_views_belong_to ( workspace_id ) ;
2024-03-21 04:02:03 +00:00
// 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 ) ) ;
views
. into_iter ( )
. map ( | view | {
// Get child views
2024-08-18 03:16:42 +00:00
let mut child_views : Vec < Arc < View > > =
folder . get_views_belong_to ( & view . id ) . into_iter ( ) . collect ( ) ;
2024-06-14 01:32:02 +00:00
child_views . retain ( | view | ! trash_ids . contains ( & view . id ) ) ;
2024-03-21 04:02:03 +00:00
view_pb_with_child_views ( view , child_views )
} )
. collect ( )
}
2024-04-02 01:57:14 +00:00
/// Get all the child views belong to the view id, including the child views of the child views.
fn get_all_child_view_ids ( folder : & Folder , view_id : & str ) -> Vec < String > {
let child_view_ids = folder
. get_views_belong_to ( view_id )
. into_iter ( )
. map ( | view | view . id . clone ( ) )
. collect ::< Vec < String > > ( ) ;
let mut all_child_view_ids = child_view_ids . clone ( ) ;
for child_view_id in child_view_ids {
all_child_view_ids . extend ( get_all_child_view_ids ( folder , & child_view_id ) ) ;
}
all_child_view_ids
}
2024-03-21 04:02:03 +00:00
/// Get the current private views of the user.
2024-04-26 01:44:07 +00:00
pub ( crate ) fn get_workspace_private_view_pbs ( workspace_id : & str , folder : & Folder ) -> Vec < ViewPB > {
2024-03-21 04:02:03 +00:00
// get the trash ids
let trash_ids = folder
2024-03-22 09:15:18 +00:00
. get_all_trash_sections ( )
2024-03-21 04:02:03 +00:00
. into_iter ( )
. map ( | trash | trash . id )
. collect ::< Vec < String > > ( ) ;
// get the private view ids
let private_view_ids = folder
2024-03-22 09:15:18 +00:00
. get_my_private_sections ( )
2024-03-21 04:02:03 +00:00
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < String > > ( ) ;
2024-08-18 03:16:42 +00:00
let mut views = folder . get_views_belong_to ( workspace_id ) ;
2024-03-21 04:02:03 +00:00
// 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 ) ) ;
2023-04-04 06:08:50 +00:00
views
. into_iter ( )
. map ( | view | {
// Get child views
2024-08-18 03:16:42 +00:00
let mut child_views : Vec < Arc < View > > =
folder . get_views_belong_to ( & view . id ) . into_iter ( ) . collect ( ) ;
2024-06-14 01:32:02 +00:00
child_views . retain ( | view | ! trash_ids . contains ( & view . id ) ) ;
2023-05-10 11:43:32 +00:00
view_pb_with_child_views ( view , child_views )
2023-04-04 06:08:50 +00:00
} )
. collect ( )
}
2023-11-08 13:48:17 +00:00
#[ allow(clippy::large_enum_variant) ]
2023-11-05 06:00:24 +00:00
pub enum FolderInitDataSource {
2023-08-17 15:46:39 +00:00
/// It means using the data stored on local disk to initialize the folder
2023-08-28 05:28:24 +00:00
LocalDisk { create_if_not_exist : bool } ,
2023-08-17 15:46:39 +00:00
/// If there is no data stored on local disk, we will use the data from the server to initialize the folder
2024-03-23 01:18:47 +00:00
Cloud ( Vec < u8 > ) ,
2023-07-14 05:37:13 +00:00
}
2023-08-17 15:46:39 +00:00
2023-11-05 06:00:24 +00:00
impl Display for FolderInitDataSource {
fn fmt ( & self , f : & mut Formatter < '_ > ) -> std ::fmt ::Result {
match self {
FolderInitDataSource ::LocalDisk { .. } = > f . write_fmt ( format_args! ( " LocalDisk " ) ) ,
FolderInitDataSource ::Cloud ( _ ) = > f . write_fmt ( format_args! ( " Cloud " ) ) ,
}
}
}