2023-11-05 06:00:24 +00:00
use std ::fmt ::{ Display , Formatter } ;
2023-04-28 06:08:53 +00:00
use std ::ops ::Deref ;
2023-05-31 09:42:14 +00:00
use std ::sync ::{ Arc , Weak } ;
2023-04-28 06:08:53 +00:00
2023-12-29 05:02:27 +00:00
use collab ::core ::collab ::{ CollabDocState , MutexCollab } ;
2023-10-10 11:05:55 +00:00
use collab_entity ::CollabType ;
2023-11-01 03:45:35 +00:00
use collab_folder ::{
2023-12-17 19:14:05 +00:00
Folder , FolderData , Section , SectionItem , TrashInfo , View , ViewLayout , ViewUpdate , Workspace ,
2023-04-28 06:08:53 +00:00
} ;
2023-08-21 16:19:15 +00:00
use parking_lot ::{ Mutex , RwLock } ;
2023-12-18 20:36:24 +00:00
use tracing ::{ error , event , info , instrument , Level } ;
2023-04-28 06:08:53 +00:00
2023-12-27 03:42:39 +00:00
use collab_integrate ::collab_builder ::{ AppFlowyCollabBuilder , CollabBuilderConfig } ;
2024-01-04 16:05:38 +00:00
use collab_integrate ::{ CollabKVDB , CollabPersistenceConfig } ;
2024-01-12 06:34:59 +00:00
use flowy_error ::{ ErrorCode , FlowyError , FlowyResult } ;
2024-01-11 06:42:03 +00:00
use flowy_folder_pub ::cloud ::{ gen_view_id , FolderCloudService } ;
use flowy_folder_pub ::folder_builder ::ParentChildViews ;
2023-12-29 05:02:27 +00:00
use lib_infra ::async_trait ::async_trait ;
2023-04-28 06:08:53 +00:00
2023-08-04 11:27:14 +00:00
use crate ::entities ::icon ::UpdateViewIconParams ;
2023-04-04 00:41:16 +00:00
use crate ::entities ::{
2023-12-17 19:14:05 +00:00
view_pb_with_child_views , view_pb_without_child_views , CreateViewParams , CreateWorkspaceParams ,
DeletedViewPB , FolderSnapshotPB , RepeatedTrashPB , RepeatedViewIdPB , RepeatedViewPB ,
UpdateViewParams , ViewPB , WorkspacePB , WorkspaceSettingPB ,
} ;
use crate ::manager_observer ::{
notify_child_views_changed , notify_parent_view_did_change , ChildViewChangeReason ,
2023-04-04 00:41:16 +00:00
} ;
use crate ::notification ::{
2023-11-01 03:45:35 +00:00
send_notification , send_workspace_setting_notification , FolderNotification ,
2023-04-04 00:41:16 +00:00
} ;
2023-05-31 06:08:54 +00:00
use crate ::share ::ImportParams ;
2023-12-29 05:02:27 +00:00
use crate ::util ::{
folder_not_init_error , insert_parent_child_views , workspace_data_not_sync_error ,
} ;
2023-08-28 05:28:24 +00:00
use crate ::view_operation ::{ create_view , FolderOperationHandler , FolderOperationHandlers } ;
2023-04-04 00:41:16 +00:00
2023-07-29 01:46:24 +00:00
/// [FolderUser] represents the user for folder.
2023-12-29 05:02:27 +00:00
#[ async_trait ]
2023-07-29 01:46:24 +00:00
pub trait FolderUser : Send + Sync {
fn user_id ( & self ) -> Result < i64 , FlowyError > ;
2024-01-04 16:05:38 +00:00
fn collab_db ( & self , uid : i64 ) -> Result < Weak < CollabKVDB > , FlowyError > ;
2023-07-29 01:46:24 +00:00
}
2023-07-05 12:57:09 +00:00
pub struct FolderManager {
2023-12-17 19:14:05 +00:00
pub ( crate ) workspace_id : RwLock < Option < String > > ,
pub ( crate ) mutex_folder : Arc < MutexFolder > ,
2023-05-15 14:16:05 +00:00
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 > ,
2023-04-04 00:41:16 +00:00
}
2023-07-05 12:57:09 +00:00
impl FolderManager {
2023-04-04 00:41:16 +00:00
pub async fn new (
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 > ,
2023-04-04 00:41:16 +00:00
) -> FlowyResult < Self > {
2023-05-31 09:42:14 +00:00
let mutex_folder = Arc ::new ( MutexFolder ::default ( ) ) ;
2023-04-04 00:41:16 +00:00
let manager = Self {
user ,
2023-05-31 09:42:14 +00:00
mutex_folder ,
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 ,
2023-08-21 16:19:15 +00:00
workspace_id : Default ::default ( ) ,
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 > {
2023-07-14 05:37:13 +00:00
self . with_folder (
2023-08-21 16:19:15 +00:00
| | {
let uid = self . user . user_id ( ) ? ;
let workspace_id = self . workspace_id . read ( ) . as_ref ( ) . cloned ( ) . ok_or (
FlowyError ::from ( ErrorCode ::WorkspaceIdInvalid )
. with_context ( " Unexpected empty workspace id " ) ,
) ? ;
Err ( workspace_data_not_sync_error ( uid , & workspace_id ) )
} ,
2023-07-14 05:37:13 +00:00
| folder | {
let workspace_pb_from_workspace = | workspace : Workspace , folder : & Folder | {
let views = get_workspace_view_pbs ( & workspace . id , folder ) ;
let workspace : WorkspacePB = ( workspace , views ) . into ( ) ;
Ok ::< WorkspacePB , FlowyError > ( workspace )
} ;
2023-06-07 14:27:13 +00:00
2023-07-14 05:37:13 +00:00
match folder . get_current_workspace ( ) {
2023-11-01 03:45:35 +00:00
None = > Err ( FlowyError ::record_not_found ( ) . with_context ( " Can not find the workspace " ) ) ,
2023-07-14 05:37:13 +00:00
Some ( workspace ) = > workspace_pb_from_workspace ( workspace , folder ) ,
}
} ,
)
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.
2023-04-04 06:08:50 +00:00
pub async fn get_current_workspace_views ( & self ) -> FlowyResult < Vec < ViewPB > > {
let workspace_id = self
2023-05-31 09:42:14 +00:00
. mutex_folder
2023-04-04 06:08:50 +00:00
. lock ( )
. as_ref ( )
2023-11-01 03:45:35 +00:00
. map ( | folder | folder . get_workspace_id ( ) ) ;
2023-04-04 06:08:50 +00:00
2023-11-01 03:45:35 +00:00
if let Some ( workspace_id ) = workspace_id {
2023-04-04 06:08:50 +00:00
self . get_workspace_views ( & workspace_id ) . await
} else {
2023-07-14 05:37:13 +00:00
tracing ::warn! ( " Can't get current workspace views " ) ;
2023-04-04 06:08:50 +00:00
Ok ( vec! [ ] )
}
}
2023-04-04 00:41:16 +00:00
2023-04-04 06:08:50 +00:00
pub async fn get_workspace_views ( & self , workspace_id : & str ) -> FlowyResult < Vec < ViewPB > > {
2023-11-01 03:45:35 +00:00
let views = self . with_folder ( Vec ::new , | folder | {
2023-08-28 05:28:24 +00:00
get_workspace_view_pbs ( workspace_id , folder )
} ) ;
2023-04-04 00:41:16 +00:00
Ok ( views )
}
2023-12-17 19:14:05 +00:00
pub ( crate ) async fn collab_for_folder (
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 > ,
2023-12-29 05:02:27 +00:00
collab_doc_state : CollabDocState ,
2023-07-14 05:37:13 +00:00
) -> Result < Arc < MutexCollab > , FlowyError > {
2023-10-07 01:58:44 +00:00
let collab = self
. collab_builder
. build_with_config (
uid ,
workspace_id ,
CollabType ::Folder ,
collab_db ,
2023-12-29 05:02:27 +00:00
collab_doc_state ,
2024-01-06 04:46:11 +00:00
CollabPersistenceConfig ::new ( )
. enable_snapshot ( true )
2024-01-07 05:59:39 +00:00
. snapshot_per_update ( 50 ) ,
2023-12-27 03:42:39 +00:00
CollabBuilderConfig ::default ( ) . sync_enable ( true ) ,
2023-10-07 01:58:44 +00:00
)
. await ? ;
2023-07-14 05:37:13 +00:00
Ok ( collab )
}
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) ]
2023-07-29 01:46:24 +00:00
pub async fn initialize_with_workspace_id (
& self ,
user_id : i64 ,
workspace_id : & str ,
) -> FlowyResult < ( ) > {
2023-11-28 23:49:47 +00:00
let folder_doc_state = self
2023-07-14 05:37:13 +00:00
. cloud_service
2023-12-29 05:02:27 +00:00
. get_collab_doc_state_f ( workspace_id , user_id , CollabType ::Folder , workspace_id )
2023-07-14 05:37:13 +00:00
. await ? ;
2023-11-05 06:00:24 +00:00
event! (
Level ::INFO ,
2023-07-14 05:37:13 +00:00
" Get folder updates via {}, number of updates: {} " ,
self . cloud_service . service_name ( ) ,
2023-11-28 23:49:47 +00:00
folder_doc_state . len ( )
2023-07-14 05:37:13 +00:00
) ;
2023-12-18 20:36:24 +00:00
if let Err ( err ) = self
2023-07-14 05:37:13 +00:00
. initialize (
user_id ,
workspace_id ,
2023-11-28 23:49:47 +00:00
FolderInitDataSource ::Cloud ( folder_doc_state ) ,
2023-07-14 05:37:13 +00:00
)
2023-12-18 20:36:24 +00:00
. 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! (
" Failed to initialize folder with error {}, fallback to use local data " ,
err
) ;
self
. initialize (
user_id ,
workspace_id ,
FolderInitDataSource ::LocalDisk {
create_if_not_exist : false ,
} ,
)
. await ? ;
}
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
2023-12-29 05:02:27 +00:00
. get_collab_doc_state_f ( 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 {
Ok ( folder_updates ) = > {
info! (
" Get folder updates via {}, number of updates: {} " ,
self . cloud_service . service_name ( ) ,
folder_updates . len ( )
) ;
self
. initialize (
user_id ,
workspace_id ,
2023-11-05 06:00:24 +00:00
FolderInitDataSource ::Cloud ( folder_updates ) ,
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) ]
2023-11-01 03:45:35 +00:00
pub async fn create_workspace ( & self , _params : CreateWorkspaceParams ) -> FlowyResult < Workspace > {
Err ( FlowyError ::not_support ( ) )
2023-04-04 00:41:16 +00:00
}
2023-06-08 06:20:31 +00:00
#[ tracing::instrument(level = " info " , skip_all, err) ]
2023-11-01 03:45:35 +00:00
pub async fn open_workspace ( & self , _workspace_id : & str ) -> FlowyResult < Workspace > {
2023-08-21 16:19:15 +00:00
self . with_folder (
| | Err ( FlowyError ::internal ( ) ) ,
| folder | {
2023-11-01 03:45:35 +00:00
let workspace = folder . get_current_workspace ( ) . ok_or_else ( | | {
FlowyError ::record_not_found ( ) . with_context ( " Can't open not existing workspace " )
} ) ? ;
2023-08-21 16:19:15 +00:00
Ok ::< Workspace , FlowyError > ( workspace )
} ,
)
2023-04-04 00:41:16 +00:00
}
2023-11-01 03:45:35 +00:00
pub async fn get_workspace ( & self , _workspace_id : & str ) -> Option < Workspace > {
self . with_folder ( | | None , | folder | folder . get_current_workspace ( ) )
}
2023-11-05 06:00:24 +00:00
pub async fn get_workspace_setting_pb ( & self ) -> FlowyResult < WorkspaceSettingPB > {
let workspace_id = self . get_current_workspace_id ( ) . await ? ;
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 > {
self . with_folder (
| | Err ( FlowyError ::internal ( ) . with_context ( " The folder is not initialized " ) ) ,
| folder | {
for view in views {
insert_parent_child_views ( folder , view ) ;
}
Ok ( ( ) )
} ,
) ? ;
Ok ( ( ) )
}
2023-11-05 06:00:24 +00:00
pub async fn get_workspace_pb ( & self ) -> FlowyResult < WorkspacePB > {
2023-11-01 03:45:35 +00:00
let workspace_pb = {
let guard = self . mutex_folder . lock ( ) ;
2023-11-05 06:00:24 +00:00
let folder = guard
. as_ref ( )
. ok_or ( FlowyError ::internal ( ) . with_context ( " folder is not initialized " ) ) ? ;
let workspace = folder . get_current_workspace ( ) . ok_or (
FlowyError ::record_not_found ( ) . with_context ( " Can't find the current workspace id " ) ,
) ? ;
2023-11-01 03:45:35 +00:00
let views = folder
. views
. get_views_belong_to ( & workspace . id )
. into_iter ( )
. map ( view_pb_without_child_views )
. collect ::< Vec < ViewPB > > ( ) ;
WorkspacePB {
id : workspace . id ,
name : workspace . name ,
views ,
create_time : workspace . created_at ,
}
} ;
2023-11-05 06:00:24 +00:00
Ok ( workspace_pb )
2023-04-04 00:41:16 +00:00
}
2023-07-29 01:46:24 +00:00
async fn get_current_workspace_id ( & self ) -> FlowyResult < String > {
self
. mutex_folder
. lock ( )
. as_ref ( )
2023-11-01 03:45:35 +00:00
. map ( | folder | folder . get_workspace_id ( ) )
2023-08-21 16:19:15 +00:00
. ok_or ( FlowyError ::internal ( ) . with_context ( " Unexpected empty workspace id " ) )
2023-07-29 01:46:24 +00:00
}
2023-08-21 16:19:15 +00:00
/// This function acquires a lock on the `mutex_folder` and checks its state.
/// If the folder is `None`, it invokes the `none_callback`, otherwise, it passes the folder to the `f2` callback.
///
/// # Parameters
///
/// * `none_callback`: A callback function that is invoked when `mutex_folder` contains `None`.
/// * `f2`: A callback function that is invoked when `mutex_folder` contains a `Some` value. The contained folder is passed as an argument to this callback.
fn with_folder < F1 , F2 , Output > ( & self , none_callback : F1 , f2 : F2 ) -> Output
2023-04-04 00:41:16 +00:00
where
2023-08-21 16:19:15 +00:00
F1 : FnOnce ( ) -> Output ,
F2 : FnOnce ( & Folder ) -> Output ,
2023-04-04 00:41:16 +00:00
{
2023-05-31 09:42:14 +00:00
let folder = self . mutex_folder . lock ( ) ;
2023-04-04 00:41:16 +00:00
match & * folder {
2023-08-21 16:19:15 +00:00
None = > none_callback ( ) ,
Some ( folder ) = > f2 ( folder ) ,
2023-04-04 00:41:16 +00:00
}
}
pub async fn get_all_workspaces ( & self ) -> Vec < Workspace > {
2023-11-01 03:45:35 +00:00
self . with_folder ( Vec ::new , | folder | {
let mut workspaces = vec! [ ] ;
if let Some ( workspace ) = folder . get_current_workspace ( ) {
workspaces . push ( workspace ) ;
}
workspaces
2023-08-28 05:28:24 +00:00
} )
2023-04-04 00:41:16 +00:00
}
pub async fn create_view_with_params ( & self , params : CreateViewParams ) -> FlowyResult < View > {
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 ( ) ? ;
2023-06-01 12:23:27 +00:00
let meta = params . meta . clone ( ) ;
2023-06-06 09:19:53 +00:00
if meta . is_empty ( ) & & params . initial_data . is_empty ( ) {
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 " ) ;
handler
. create_view_with_view_data (
user_id ,
& params . view_id ,
& params . name ,
params . initial_data . clone ( ) ,
view_layout . clone ( ) ,
meta ,
)
. 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 ;
2023-11-08 13:48:17 +00:00
let view = create_view ( self . user . user_id ( ) ? , params , view_layout ) ;
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
folder . insert_view ( view . clone ( ) , index ) ;
} ,
) ;
2023-04-04 00:41:16 +00:00
Ok ( view )
}
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 ) ;
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
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 > {
2023-08-21 16:19:15 +00:00
if let Some ( view ) = self . with_folder ( | | None , | folder | folder . views . get_view ( view_id ) ) {
2023-07-29 01:46:24 +00:00
let handler = self . get_handler ( & view . layout ) ? ;
handler . close_view ( view_id ) . await ? ;
}
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
2023-06-03 05:40:12 +00:00
/// Returns the view with the given view id.
/// The child views of the view will only access the first. So if you want to get the child view's
/// child view, you need to call this method again.
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " debug " , skip(self, view_id), err) ]
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 ( ) ;
2023-05-31 09:42:14 +00:00
let folder = self . mutex_folder . lock ( ) ;
2023-04-04 00:41:16 +00:00
let folder = folder . as_ref ( ) . ok_or_else ( folder_not_init_error ) ? ;
let trash_ids = folder
. get_all_trash ( )
. into_iter ( )
. map ( | trash | trash . id )
. collect ::< Vec < String > > ( ) ;
if trash_ids . contains ( & view_id ) {
return Err ( FlowyError ::record_not_found ( ) ) ;
}
match folder . views . get_view ( & view_id ) {
None = > 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
2023-04-04 00:41:16 +00:00
. views
2023-05-10 11:43:32 +00:00
. get_views_belong_to ( & view . id )
2023-04-04 00:41:16 +00:00
. into_iter ( )
. filter ( | view | ! trash_ids . 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 )
} ,
}
}
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 < ( ) > {
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
if let Some ( view ) = folder . views . get_view ( view_id ) {
self . unfavorite_view_and_decendants ( view . clone ( ) , folder ) ;
folder . add_trash ( vec! [ view_id . to_string ( ) ] ) ;
// 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 ) ,
2023-12-29 05:02:27 +00:00
ChildViewChangeReason ::Delete ,
2023-08-21 16:19:15 +00:00
) ;
}
} ,
) ;
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
2023-08-02 13:20:51 +00:00
fn unfavorite_view_and_decendants ( & self , view : Arc < View > , folder : & Folder ) {
let mut all_descendant_views : Vec < Arc < View > > = vec! [ view . clone ( ) ] ;
all_descendant_views . extend ( folder . views . get_views_belong_to ( & view . id ) ) ;
let favorite_descendant_views : Vec < ViewPB > = all_descendant_views
. iter ( )
. filter ( | view | view . is_favorite )
. map ( | view | view_pb_without_child_views ( view . clone ( ) ) )
. collect ( ) ;
if ! favorite_descendant_views . is_empty ( ) {
folder . delete_favorites (
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) ]
pub async fn move_nested_view (
& self ,
view_id : String ,
new_parent_id : String ,
prev_view_id : Option < String > ,
) -> FlowyResult < ( ) > {
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 ;
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
folder . move_nested_view ( & view_id , & new_parent_id , prev_view_id ) ;
} ,
) ;
2023-07-26 08:49:50 +00:00
notify_parent_view_did_change (
self . mutex_folder . clone ( ) ,
vec! [ new_parent_id , old_parent_id ] ,
) ;
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 < ( ) > {
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 )
{
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
folder . move_view ( view_id , actual_from_index as u32 , actual_to_index as u32 ) ;
} ,
) ;
2023-06-06 16:05:27 +00:00
notify_parent_view_did_change ( self . mutex_folder . clone ( ) , vec! [ parent_view_id ] ) ;
}
}
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 > > > {
2023-11-01 03:45:35 +00:00
let views = self . with_folder ( Vec ::new , | folder | {
2023-08-28 05:28:24 +00:00
folder . views . get_views_belong_to ( parent_view_id )
} ) ;
2023-04-04 00:41:16 +00:00
Ok ( views )
}
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 )
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.
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " debug " , skip(self), err) ]
pub ( crate ) async fn duplicate_view ( & self , view_id : & str ) -> Result < ( ) , FlowyError > {
let view = self
2023-08-21 16:19:15 +00:00
. with_folder ( | | None , | folder | folder . views . get_view ( view_id ) )
. ok_or_else ( | | FlowyError ::record_not_found ( ) . with_context ( " Can't duplicate the view " ) ) ? ;
2023-04-04 00:41:16 +00:00
2023-05-31 06:08:54 +00:00
let handler = self . get_handler ( & view . layout ) ? ;
let view_data = handler . duplicate_view ( & view . id ) . await ? ;
2023-08-02 13:20:51 +00:00
// get the current view index in the parent view, because we need to insert the duplicated view below the current view.
let index = if let Some ( ( _ , __ , views ) ) = self . get_view_relation ( & view . parent_view_id ) . await {
views . iter ( ) . position ( | id | id = = view_id ) . map ( | i | i as u32 )
} else {
None
} ;
2023-04-04 00:41:16 +00:00
let duplicate_params = CreateViewParams {
2023-06-01 12:23:27 +00:00
parent_view_id : view . parent_view_id . clone ( ) ,
2023-04-04 00:41:16 +00:00
name : format ! ( " {} (copy) " , & view . name ) ,
2023-06-23 15:19:34 +00:00
desc : view . desc . clone ( ) ,
layout : view . layout . clone ( ) . into ( ) ,
2023-04-04 00:41:16 +00:00
initial_data : view_data . to_vec ( ) ,
2023-08-28 05:28:24 +00:00
view_id : gen_view_id ( ) . to_string ( ) ,
2023-06-03 05:40:12 +00:00
meta : Default ::default ( ) ,
set_as_current : true ,
2023-08-02 13:20:51 +00:00
index ,
2023-04-04 00:41:16 +00:00
} ;
2023-08-02 13:20:51 +00:00
self . create_view_with_params ( duplicate_params ) . await ? ;
2023-04-04 00:41:16 +00:00
Ok ( ( ) )
}
#[ tracing::instrument(level = " trace " , skip(self), err) ]
pub ( crate ) async fn set_current_view ( & self , view_id : & str ) -> Result < ( ) , FlowyError > {
2023-11-01 03:45:35 +00:00
let workspace_id = self . with_folder (
| | Err ( FlowyError ::record_not_found ( ) ) ,
| folder | {
folder . set_current_view ( view_id ) ;
2023-11-13 02:07:46 +00:00
folder . add_recent_view_ids ( vec! [ view_id . to_string ( ) ] ) ;
2023-11-01 03:45:35 +00:00
Ok ( folder . get_workspace_id ( ) )
} ,
) ? ;
2023-04-04 00:41:16 +00:00
2023-11-01 03:45:35 +00:00
send_workspace_setting_notification ( workspace_id , self . get_current_view ( ) . await ) ;
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 > {
2023-08-21 16:19:15 +00:00
let view_id = self . with_folder ( | | None , | folder | folder . get_current_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 < ( ) > {
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
if let Some ( old_view ) = folder . views . get_view ( view_id ) {
if old_view . is_favorite {
folder . delete_favorites ( vec! [ view_id . to_string ( ) ] ) ;
} else {
folder . add_favorites ( vec! [ view_id . to_string ( ) ] ) ;
}
2023-08-02 13:20:51 +00:00
}
2023-08-21 16:19:15 +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 < ( ) > {
self . with_folder (
| | ( ) ,
| folder | {
folder . add_recent_view_ids ( view_ids ) ;
} ,
) ;
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 < ( ) > {
self . with_folder (
| | ( ) ,
| folder | {
folder . delete_recent_view_ids ( view_ids ) ;
} ,
) ;
self . send_update_recent_views_notification ( ) . await ;
Ok ( ( ) )
}
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 ) {
let recent_views = self . get_all_recent_sections ( ) . await ;
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 > {
2023-11-13 02:07:46 +00:00
self . get_sections ( Section ::Favorite )
}
2023-08-02 13:20:51 +00:00
2023-11-13 02:07:46 +00:00
#[ tracing::instrument(level = " trace " , skip(self)) ]
pub ( crate ) async fn get_all_recent_sections ( & self ) -> Vec < SectionItem > {
self . get_sections ( Section ::Recent )
2023-08-02 13:20:51 +00:00
}
2023-04-04 00:41:16 +00:00
#[ tracing::instrument(level = " trace " , skip(self)) ]
pub ( crate ) async fn get_all_trash ( & self ) -> Vec < TrashInfo > {
2023-11-01 03:45:35 +00:00
self . with_folder ( Vec ::new , | folder | folder . get_all_trash ( ) )
2023-04-04 00:41:16 +00:00
}
#[ tracing::instrument(level = " trace " , skip(self)) ]
pub ( crate ) async fn restore_all_trash ( & self ) {
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
folder . remote_all_trash ( ) ;
} ,
) ;
2023-04-04 00:41:16 +00:00
send_notification ( " trash " , FolderNotification ::DidUpdateTrash )
. payload ( RepeatedTrashPB { items : vec ! [ ] } )
. send ( ) ;
}
#[ tracing::instrument(level = " trace " , skip(self)) ]
pub ( crate ) async fn restore_trash ( & self , trash_id : & str ) {
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
folder . delete_trash ( 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)) ]
pub ( crate ) async fn delete_all_trash ( & self ) {
2023-11-01 03:45:35 +00:00
let deleted_trash = self . with_folder ( Vec ::new , | folder | folder . get_all_trash ( ) ) ;
2023-06-05 01:42:11 +00:00
for trash in deleted_trash {
let _ = self . delete_trash ( & trash . id ) . await ;
}
2023-04-04 00:41:16 +00:00
send_notification ( " trash " , FolderNotification ::DidUpdateTrash )
. payload ( RepeatedTrashPB { items : vec ! [ ] } )
. send ( ) ;
}
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 < ( ) > {
2023-08-21 16:19:15 +00:00
let view = self . with_folder ( | | None , | folder | folder . views . get_view ( view_id ) ) ;
self . with_folder (
| | ( ) ,
| folder | {
folder . delete_trash ( vec! [ view_id . to_string ( ) ] ) ;
folder . views . delete_views ( vec! [ view_id ] ) ;
} ,
) ;
2023-06-05 01:42:11 +00:00
if let Some ( view ) = view {
if let Ok ( handler ) = self . get_handler ( & view . layout ) {
handler . delete_view ( view_id ) . await ? ;
}
}
Ok ( ( ) )
}
2023-05-31 06:08:54 +00:00
pub ( crate ) async fn import ( & self , import_data : ImportParams ) -> FlowyResult < View > {
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 ,
2023-05-31 06:08:54 +00:00
" data or file_path is required " ,
) ) ;
}
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 ( ) ? ;
2023-05-31 06:08:54 +00:00
if let Some ( data ) = import_data . data {
handler
2023-08-17 15:46:39 +00:00
. import_from_bytes (
uid ,
& view_id ,
& import_data . name ,
import_data . import_type ,
data ,
)
2023-05-31 06:08:54 +00:00
. await ? ;
}
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 {
parent_view_id : import_data . parent_view_id ,
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 ,
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 ) ;
2023-08-21 16:19:15 +00:00
self . with_folder (
| | ( ) ,
| folder | {
folder . insert_view ( view . clone ( ) , None ) ;
} ,
) ;
2023-06-01 12:23:27 +00:00
notify_parent_view_did_change ( self . mutex_folder . clone ( ) , vec! [ view . parent_view_id . clone ( ) ] ) ;
2023-05-31 06:08:54 +00:00
Ok ( view )
}
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 > ,
{
2023-08-21 16:19:15 +00:00
let value = self . with_folder (
| | None ,
| folder | {
let old_view = folder . views . get_view ( view_id ) ;
let new_view = folder . views . 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 ) )
} ,
) ;
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 ( ) ;
}
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
/// 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 > ) > {
2023-08-21 16:19:15 +00:00
self . with_folder (
| | None ,
| folder | {
let view = folder . views . get_view ( view_id ) ? ;
match folder . views . get_view ( & view . parent_view_id ) {
None = > folder . get_current_workspace ( ) . map ( | workspace | {
(
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
2023-06-06 16:05:27 +00:00
. items
2023-08-21 16:19:15 +00:00
. clone ( )
2023-06-06 16:05:27 +00:00
. into_iter ( )
. map ( | view | view . id )
. collect ::< Vec < String > > ( ) ,
2023-08-21 16:19:15 +00:00
) ) ,
}
} ,
)
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 )
}
/// Only expose this method for testing
#[ cfg(debug_assertions) ]
pub fn get_mutex_folder ( & self ) -> & Arc < MutexFolder > {
& self . mutex_folder
}
/// Only expose this method for testing
#[ cfg(debug_assertions) ]
pub fn get_cloud_service ( & self ) -> & Arc < dyn FolderCloudService > {
& self . cloud_service
}
2023-11-13 02:07:46 +00:00
fn get_sections ( & self , section_type : Section ) -> Vec < SectionItem > {
self . with_folder ( Vec ::new , | folder | {
let trash_ids = folder
. get_all_trash ( )
. into_iter ( )
. map ( | trash | trash . id )
. collect ::< Vec < String > > ( ) ;
let mut views = match section_type {
Section ::Favorite = > folder . get_all_favorites ( ) ,
Section ::Recent = > folder . get_all_recent_sections ( ) ,
_ = > vec! [ ] ,
} ;
// filter the views that are in the trash
views . retain ( | view | ! trash_ids . contains ( & view . id ) ) ;
views
} )
}
2023-04-04 00:41:16 +00:00
}
2023-06-06 16:05:27 +00:00
/// Return the views that belong to the workspace. The views are filtered by the trash.
2023-12-17 19:14:05 +00:00
pub ( crate ) fn get_workspace_view_pbs ( _workspace_id : & str , folder : & Folder ) -> Vec < ViewPB > {
2023-12-25 18:03:42 +00:00
let items = folder . get_all_trash ( ) ;
let trash_ids = items
2023-04-04 06:08:50 +00:00
. into_iter ( )
. map ( | trash | trash . id )
. collect ::< Vec < String > > ( ) ;
2023-11-28 23:49:47 +00:00
let mut views = folder . get_workspace_views ( ) ;
2023-04-04 06:08:50 +00:00
views . retain ( | view | ! trash_ids . contains ( & view . id ) ) ;
views
. into_iter ( )
. map ( | view | {
// Get child views
2023-05-10 11:43:32 +00:00
let child_views = folder
2023-04-04 06:08:50 +00:00
. views
2023-05-10 11:43:32 +00:00
. get_views_belong_to ( & view . id )
2023-04-04 06:08:50 +00:00
. into_iter ( )
. collect ( ) ;
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-04-04 00:41:16 +00:00
#[ derive(Clone, Default) ]
2023-05-31 09:42:14 +00:00
pub struct MutexFolder ( Arc < Mutex < Option < Folder > > > ) ;
impl Deref for MutexFolder {
type Target = Arc < Mutex < Option < Folder > > > ;
2023-04-04 00:41:16 +00:00
fn deref ( & self ) -> & Self ::Target {
& self . 0
}
}
2023-05-31 09:42:14 +00:00
unsafe impl Sync for MutexFolder { }
unsafe impl Send for MutexFolder { }
2023-07-14 05:37:13 +00:00
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
2023-12-29 05:02:27 +00:00
Cloud ( CollabDocState ) ,
2023-08-17 15:46:39 +00:00
/// If the user is new, we use the [DefaultFolderBuilder] to create the default folder.
2023-08-28 05:28:24 +00:00
FolderData ( FolderData ) ,
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 " ) ) ,
FolderInitDataSource ::FolderData ( _ ) = > f . write_fmt ( format_args! ( " Custom FolderData " ) ) ,
}
}
}