manager.rs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. use std::collections::HashSet;
  2. use std::ops::Deref;
  3. use std::sync::{Arc, Weak};
  4. use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
  5. use appflowy_integrate::{CollabPersistenceConfig, CollabType, RocksCollabDB};
  6. use collab::core::collab::{CollabRawData, MutexCollab};
  7. use collab::core::collab_state::SyncState;
  8. use collab_folder::core::{
  9. Folder, FolderData, FolderNotify, TrashChange, TrashChangeReceiver, TrashInfo, View, ViewChange,
  10. ViewChangeReceiver, ViewLayout, Workspace,
  11. };
  12. use parking_lot::Mutex;
  13. use tokio_stream::wrappers::WatchStream;
  14. use tokio_stream::StreamExt;
  15. use tracing::{event, Level};
  16. use flowy_error::{ErrorCode, FlowyError, FlowyResult};
  17. use flowy_folder_deps::cloud::FolderCloudService;
  18. use crate::entities::{
  19. view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, CreateViewParams,
  20. CreateWorkspaceParams, DeletedViewPB, FolderSnapshotPB, FolderSnapshotStatePB, FolderSyncStatePB,
  21. RepeatedTrashPB, RepeatedViewPB, RepeatedWorkspacePB, UpdateViewParams, ViewPB, WorkspacePB,
  22. };
  23. use crate::notification::{
  24. send_notification, send_workspace_notification, send_workspace_setting_notification,
  25. FolderNotification,
  26. };
  27. use crate::share::ImportParams;
  28. use crate::user_default::DefaultFolderBuilder;
  29. use crate::view_operation::{
  30. create_view, gen_view_id, FolderOperationHandler, FolderOperationHandlers,
  31. };
  32. /// [FolderUser] represents the user for folder.
  33. pub trait FolderUser: Send + Sync {
  34. fn user_id(&self) -> Result<i64, FlowyError>;
  35. fn token(&self) -> Result<Option<String>, FlowyError>;
  36. fn collab_db(&self, uid: i64) -> Result<Weak<RocksCollabDB>, FlowyError>;
  37. }
  38. pub struct FolderManager {
  39. mutex_folder: Arc<MutexFolder>,
  40. collab_builder: Arc<AppFlowyCollabBuilder>,
  41. user: Arc<dyn FolderUser>,
  42. operation_handlers: FolderOperationHandlers,
  43. cloud_service: Arc<dyn FolderCloudService>,
  44. }
  45. unsafe impl Send for FolderManager {}
  46. unsafe impl Sync for FolderManager {}
  47. impl FolderManager {
  48. pub async fn new(
  49. user: Arc<dyn FolderUser>,
  50. collab_builder: Arc<AppFlowyCollabBuilder>,
  51. operation_handlers: FolderOperationHandlers,
  52. cloud_service: Arc<dyn FolderCloudService>,
  53. ) -> FlowyResult<Self> {
  54. let mutex_folder = Arc::new(MutexFolder::default());
  55. let manager = Self {
  56. user,
  57. mutex_folder,
  58. collab_builder,
  59. operation_handlers,
  60. cloud_service,
  61. };
  62. Ok(manager)
  63. }
  64. pub async fn get_current_workspace(&self) -> FlowyResult<WorkspacePB> {
  65. self.with_folder(
  66. Err(FlowyError::internal().context("Folder is not initialized".to_string())),
  67. |folder| {
  68. let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| {
  69. let views = get_workspace_view_pbs(&workspace.id, folder);
  70. let workspace: WorkspacePB = (workspace, views).into();
  71. Ok::<WorkspacePB, FlowyError>(workspace)
  72. };
  73. match folder.get_current_workspace() {
  74. None => {
  75. // The current workspace should always exist. If not, try to find the first workspace.
  76. // from the folder. Otherwise, return an error.
  77. let mut workspaces = folder.workspaces.get_all_workspaces();
  78. if workspaces.is_empty() {
  79. Err(FlowyError::record_not_found().context("Can not find the workspace"))
  80. } else {
  81. tracing::error!("Can't find the current workspace, use the first workspace");
  82. let workspace = workspaces.remove(0);
  83. folder.set_current_workspace(&workspace.id);
  84. workspace_pb_from_workspace(workspace, folder)
  85. }
  86. },
  87. Some(workspace) => workspace_pb_from_workspace(workspace, folder),
  88. }
  89. },
  90. )
  91. }
  92. /// Return a list of views of the current workspace.
  93. /// Only the first level of child views are included.
  94. pub async fn get_current_workspace_views(&self) -> FlowyResult<Vec<ViewPB>> {
  95. let workspace_id = self
  96. .mutex_folder
  97. .lock()
  98. .as_ref()
  99. .map(|folder| folder.get_current_workspace_id());
  100. if let Some(Some(workspace_id)) = workspace_id {
  101. self.get_workspace_views(&workspace_id).await
  102. } else {
  103. tracing::warn!("Can't get current workspace views");
  104. Ok(vec![])
  105. }
  106. }
  107. pub async fn get_workspace_views(&self, workspace_id: &str) -> FlowyResult<Vec<ViewPB>> {
  108. let views = self.with_folder(vec![], |folder| {
  109. get_workspace_view_pbs(workspace_id, folder)
  110. });
  111. Ok(views)
  112. }
  113. /// Called immediately after the application launched fi the user already sign in/sign up.
  114. #[tracing::instrument(level = "info", skip(self, initial_data), err)]
  115. pub async fn initialize(
  116. &self,
  117. uid: i64,
  118. workspace_id: &str,
  119. initial_data: FolderInitializeData,
  120. ) -> FlowyResult<()> {
  121. let workspace_id = workspace_id.to_string();
  122. if let Ok(collab_db) = self.user.collab_db(uid) {
  123. let (view_tx, view_rx) = tokio::sync::broadcast::channel(100);
  124. let (trash_tx, trash_rx) = tokio::sync::broadcast::channel(100);
  125. let folder_notifier = FolderNotify {
  126. view_change_tx: view_tx,
  127. trash_change_tx: trash_tx,
  128. };
  129. let folder = match initial_data {
  130. FolderInitializeData::Empty => {
  131. let collab = self.collab_for_folder(uid, &workspace_id, collab_db, vec![])?;
  132. Folder::open(collab, Some(folder_notifier))
  133. },
  134. FolderInitializeData::Raw(raw_data) => {
  135. let collab = self.collab_for_folder(uid, &workspace_id, collab_db, raw_data)?;
  136. Folder::open(collab, Some(folder_notifier))
  137. },
  138. FolderInitializeData::Data(folder_data) => {
  139. let collab = self.collab_for_folder(uid, &workspace_id, collab_db, vec![])?;
  140. Folder::create(collab, Some(folder_notifier), Some(folder_data))
  141. },
  142. };
  143. tracing::debug!("Current workspace_id: {}", workspace_id);
  144. let folder_state_rx = folder.subscribe_sync_state();
  145. *self.mutex_folder.lock() = Some(folder);
  146. let weak_mutex_folder = Arc::downgrade(&self.mutex_folder);
  147. subscribe_folder_sync_state_changed(
  148. workspace_id.clone(),
  149. folder_state_rx,
  150. &weak_mutex_folder,
  151. );
  152. subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder);
  153. subscribe_folder_trash_changed(trash_rx, &weak_mutex_folder);
  154. subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
  155. }
  156. Ok(())
  157. }
  158. fn collab_for_folder(
  159. &self,
  160. uid: i64,
  161. workspace_id: &str,
  162. collab_db: Weak<RocksCollabDB>,
  163. raw_data: CollabRawData,
  164. ) -> Result<Arc<MutexCollab>, FlowyError> {
  165. let collab = self.collab_builder.build_with_config(
  166. uid,
  167. workspace_id,
  168. CollabType::Folder,
  169. collab_db,
  170. raw_data,
  171. &CollabPersistenceConfig::new().enable_snapshot(true),
  172. )?;
  173. Ok(collab)
  174. }
  175. /// Initialize the folder with the given workspace id.
  176. /// Fetch the folder updates from the cloud service and initialize the folder.
  177. #[tracing::instrument(level = "debug", skip(self, user_id), err)]
  178. pub async fn initialize_with_workspace_id(
  179. &self,
  180. user_id: i64,
  181. workspace_id: &str,
  182. ) -> FlowyResult<()> {
  183. let folder_updates = self
  184. .cloud_service
  185. .get_folder_updates(workspace_id, user_id)
  186. .await?;
  187. tracing::trace!(
  188. "Get folder updates via {}, number of updates: {}",
  189. self.cloud_service.service_name(),
  190. folder_updates.len()
  191. );
  192. self
  193. .initialize(
  194. user_id,
  195. workspace_id,
  196. FolderInitializeData::Raw(folder_updates),
  197. )
  198. .await?;
  199. Ok(())
  200. }
  201. /// Initialize the folder for the new user.
  202. /// Using the [DefaultFolderBuilder] to create the default workspace for the new user.
  203. pub async fn initialize_with_new_user(
  204. &self,
  205. user_id: i64,
  206. _token: &str,
  207. is_new: bool,
  208. folder_data: Option<FolderData>,
  209. workspace_id: &str,
  210. ) -> FlowyResult<()> {
  211. // Create the default workspace if the user is new
  212. tracing::info!("initialize_when_sign_up: is_new: {}", is_new);
  213. if is_new {
  214. let folder_data = match folder_data {
  215. None => {
  216. DefaultFolderBuilder::build(
  217. self.user.user_id()?,
  218. workspace_id.to_string(),
  219. &self.operation_handlers,
  220. )
  221. .await
  222. },
  223. Some(folder_data) => folder_data,
  224. };
  225. self
  226. .initialize(
  227. user_id,
  228. workspace_id,
  229. FolderInitializeData::Data(folder_data),
  230. )
  231. .await?;
  232. } else {
  233. // The folder data is loaded through the [FolderCloudService]. If the cloud service in use is
  234. // [LocalServerFolderCloudServiceImpl], the folder data will be None because the Folder will load
  235. // the data directly from the disk. If any other cloud service is in use, the folder data will be loaded remotely.
  236. let folder_updates = self
  237. .cloud_service
  238. .get_folder_updates(workspace_id, user_id)
  239. .await?;
  240. if !folder_updates.is_empty() {
  241. tracing::trace!(
  242. "Get folder updates via {}",
  243. self.cloud_service.service_name()
  244. );
  245. }
  246. self
  247. .initialize(
  248. user_id,
  249. workspace_id,
  250. FolderInitializeData::Raw(folder_updates),
  251. )
  252. .await?;
  253. }
  254. Ok(())
  255. }
  256. /// Called when the current user logout
  257. ///
  258. pub async fn clear(&self, _user_id: i64) {}
  259. #[tracing::instrument(level = "info", skip_all, err)]
  260. pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> FlowyResult<Workspace> {
  261. let workspace = self
  262. .cloud_service
  263. .create_workspace(self.user.user_id()?, &params.name)
  264. .await?;
  265. self.with_folder((), |folder| {
  266. folder.workspaces.create_workspace(workspace.clone());
  267. folder.set_current_workspace(&workspace.id);
  268. });
  269. let repeated_workspace = RepeatedWorkspacePB {
  270. items: vec![workspace.clone().into()],
  271. };
  272. send_workspace_notification(FolderNotification::DidCreateWorkspace, repeated_workspace);
  273. Ok(workspace)
  274. }
  275. #[tracing::instrument(level = "info", skip_all, err)]
  276. pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<Workspace> {
  277. self.with_folder(Err(FlowyError::internal()), |folder| {
  278. let workspace = folder
  279. .workspaces
  280. .get_workspace(workspace_id)
  281. .ok_or_else(|| {
  282. FlowyError::record_not_found().context("Can't open not existing workspace")
  283. })?;
  284. folder.set_current_workspace(&workspace.id);
  285. Ok::<Workspace, FlowyError>(workspace)
  286. })
  287. }
  288. pub async fn get_workspace(&self, workspace_id: &str) -> Option<Workspace> {
  289. self.with_folder(None, |folder| folder.workspaces.get_workspace(workspace_id))
  290. }
  291. async fn get_current_workspace_id(&self) -> FlowyResult<String> {
  292. self
  293. .mutex_folder
  294. .lock()
  295. .as_ref()
  296. .and_then(|folder| folder.get_current_workspace_id())
  297. .ok_or(FlowyError::internal().context("Unexpected empty workspace id"))
  298. }
  299. fn with_folder<F, Output>(&self, default_value: Output, f: F) -> Output
  300. where
  301. F: FnOnce(&Folder) -> Output,
  302. {
  303. let folder = self.mutex_folder.lock();
  304. match &*folder {
  305. None => default_value,
  306. Some(folder) => f(folder),
  307. }
  308. }
  309. pub async fn get_all_workspaces(&self) -> Vec<Workspace> {
  310. self.with_folder(vec![], |folder| folder.workspaces.get_all_workspaces())
  311. }
  312. pub async fn create_view_with_params(&self, params: CreateViewParams) -> FlowyResult<View> {
  313. let view_layout: ViewLayout = params.layout.clone().into();
  314. let _workspace_id = self.get_current_workspace_id().await?;
  315. let handler = self.get_handler(&view_layout)?;
  316. let user_id = self.user.user_id()?;
  317. let meta = params.meta.clone();
  318. if meta.is_empty() && params.initial_data.is_empty() {
  319. tracing::trace!("Create view with build-in data");
  320. handler
  321. .create_built_in_view(user_id, &params.view_id, &params.name, view_layout.clone())
  322. .await?;
  323. } else {
  324. tracing::trace!("Create view with view data");
  325. handler
  326. .create_view_with_view_data(
  327. user_id,
  328. &params.view_id,
  329. &params.name,
  330. params.initial_data.clone(),
  331. view_layout.clone(),
  332. meta,
  333. )
  334. .await?;
  335. }
  336. let view = create_view(params, view_layout);
  337. self.with_folder((), |folder| {
  338. folder.insert_view(view.clone());
  339. });
  340. Ok(view)
  341. }
  342. /// The orphan view is meant to be a view that is not attached to any parent view. By default, this
  343. /// view will not be shown in the view list unless it is attached to a parent view that is shown in
  344. /// the view list.
  345. pub async fn create_orphan_view_with_params(
  346. &self,
  347. params: CreateViewParams,
  348. ) -> FlowyResult<View> {
  349. let view_layout: ViewLayout = params.layout.clone().into();
  350. let handler = self.get_handler(&view_layout)?;
  351. let user_id = self.user.user_id()?;
  352. handler
  353. .create_built_in_view(user_id, &params.view_id, &params.name, view_layout.clone())
  354. .await?;
  355. let view = create_view(params, view_layout);
  356. self.with_folder((), |folder| {
  357. folder.insert_view(view.clone());
  358. });
  359. Ok(view)
  360. }
  361. #[tracing::instrument(level = "debug", skip(self), err)]
  362. pub(crate) async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
  363. if let Some(view) = self.with_folder(None, |folder| folder.views.get_view(view_id)) {
  364. let handler = self.get_handler(&view.layout)?;
  365. handler.close_view(view_id).await?;
  366. }
  367. Ok(())
  368. }
  369. /// Returns the view with the given view id.
  370. /// The child views of the view will only access the first. So if you want to get the child view's
  371. /// child view, you need to call this method again.
  372. #[tracing::instrument(level = "debug", skip(self, view_id), err)]
  373. pub async fn get_view(&self, view_id: &str) -> FlowyResult<ViewPB> {
  374. let view_id = view_id.to_string();
  375. let folder = self.mutex_folder.lock();
  376. let folder = folder.as_ref().ok_or_else(folder_not_init_error)?;
  377. let trash_ids = folder
  378. .get_all_trash()
  379. .into_iter()
  380. .map(|trash| trash.id)
  381. .collect::<Vec<String>>();
  382. if trash_ids.contains(&view_id) {
  383. return Err(FlowyError::record_not_found());
  384. }
  385. match folder.views.get_view(&view_id) {
  386. None => Err(FlowyError::record_not_found()),
  387. Some(view) => {
  388. let child_views = folder
  389. .views
  390. .get_views_belong_to(&view.id)
  391. .into_iter()
  392. .filter(|view| !trash_ids.contains(&view.id))
  393. .collect::<Vec<_>>();
  394. let view_pb = view_pb_with_child_views(view, child_views);
  395. Ok(view_pb)
  396. },
  397. }
  398. }
  399. /// Move the view to trash. If the view is the current view, then set the current view to empty.
  400. /// When the view is moved to trash, all the child views will be moved to trash as well.
  401. #[tracing::instrument(level = "debug", skip(self), err)]
  402. pub async fn move_view_to_trash(&self, view_id: &str) -> FlowyResult<()> {
  403. self.with_folder((), |folder| {
  404. let view = folder.views.get_view(view_id);
  405. folder.add_trash(vec![view_id.to_string()]);
  406. // notify the parent view that the view is moved to trash
  407. send_notification(view_id, FolderNotification::DidMoveViewToTrash)
  408. .payload(DeletedViewPB {
  409. view_id: view_id.to_string(),
  410. index: None,
  411. })
  412. .send();
  413. if let Some(view) = view {
  414. notify_child_views_changed(
  415. view_pb_without_child_views(view),
  416. ChildViewChangeReason::DidDeleteView,
  417. );
  418. }
  419. });
  420. Ok(())
  421. }
  422. /// Moves a nested view to a new location in the hierarchy.
  423. ///
  424. /// This function takes the `view_id` of the view to be moved,
  425. /// `new_parent_id` of the view under which the `view_id` should be moved,
  426. /// and an optional `prev_view_id` to position the `view_id` right after
  427. /// this specific view.
  428. ///
  429. /// If `prev_view_id` is provided, the moved view will be placed right after
  430. /// the view corresponding to `prev_view_id` under the `new_parent_id`.
  431. /// If `prev_view_id` is `None`, the moved view will become the first child of the new parent.
  432. ///
  433. /// # Arguments
  434. ///
  435. /// * `view_id` - A string slice that holds the id of the view to be moved.
  436. /// * `new_parent_id` - A string slice that holds the id of the new parent view.
  437. /// * `prev_view_id` - An `Option<String>` that holds the id of the view after which the `view_id` should be positioned.
  438. ///
  439. #[tracing::instrument(level = "trace", skip(self), err)]
  440. pub async fn move_nested_view(
  441. &self,
  442. view_id: String,
  443. new_parent_id: String,
  444. prev_view_id: Option<String>,
  445. ) -> FlowyResult<()> {
  446. let view = self.get_view(&view_id).await?;
  447. let old_parent_id = view.parent_view_id;
  448. self.with_folder((), |folder| {
  449. folder.move_nested_view(&view_id, &new_parent_id, prev_view_id);
  450. });
  451. notify_parent_view_did_change(
  452. self.mutex_folder.clone(),
  453. vec![new_parent_id, old_parent_id],
  454. );
  455. Ok(())
  456. }
  457. /// Move the view with given id from one position to another position.
  458. /// The view will be moved to the new position in the same parent view.
  459. /// The passed in index is the index of the view that displayed in the UI.
  460. /// We need to convert the index to the real index of the view in the parent view.
  461. #[tracing::instrument(level = "trace", skip(self), err)]
  462. pub async fn move_view(&self, view_id: &str, from: usize, to: usize) -> FlowyResult<()> {
  463. if let Some((is_workspace, parent_view_id, child_views)) = self.get_view_relation(view_id).await
  464. {
  465. // The display parent view is the view that is displayed in the UI
  466. let display_views = if is_workspace {
  467. self
  468. .get_current_workspace()
  469. .await?
  470. .views
  471. .into_iter()
  472. .map(|view| view.id)
  473. .collect::<Vec<_>>()
  474. } else {
  475. self
  476. .get_view(&parent_view_id)
  477. .await?
  478. .child_views
  479. .into_iter()
  480. .map(|view| view.id)
  481. .collect::<Vec<_>>()
  482. };
  483. if display_views.len() > to {
  484. let to_view_id = display_views[to].clone();
  485. // Find the actual index of the view in the parent view
  486. let actual_from_index = child_views.iter().position(|id| id == view_id);
  487. let actual_to_index = child_views.iter().position(|id| id == &to_view_id);
  488. if let (Some(actual_from_index), Some(actual_to_index)) =
  489. (actual_from_index, actual_to_index)
  490. {
  491. self.with_folder((), |folder| {
  492. folder.move_view(view_id, actual_from_index as u32, actual_to_index as u32);
  493. });
  494. notify_parent_view_did_change(self.mutex_folder.clone(), vec![parent_view_id]);
  495. }
  496. }
  497. }
  498. Ok(())
  499. }
  500. /// Return a list of views that belong to the given parent view id.
  501. #[tracing::instrument(level = "debug", skip(self, parent_view_id), err)]
  502. pub async fn get_views_belong_to(&self, parent_view_id: &str) -> FlowyResult<Vec<Arc<View>>> {
  503. let views = self.with_folder(vec![], |folder| {
  504. folder.views.get_views_belong_to(parent_view_id)
  505. });
  506. Ok(views)
  507. }
  508. /// Update the view with the given params.
  509. #[tracing::instrument(level = "trace", skip(self), err)]
  510. pub async fn update_view_with_params(&self, params: UpdateViewParams) -> FlowyResult<()> {
  511. let value = self.with_folder(None, |folder| {
  512. let old_view = folder.views.get_view(&params.view_id);
  513. let new_view = folder.views.update_view(&params.view_id, |update| {
  514. update
  515. .set_name_if_not_none(params.name)
  516. .set_desc_if_not_none(params.desc)
  517. .set_layout_if_not_none(params.layout)
  518. .set_icon_url_if_not_none(params.icon_url)
  519. .set_cover_url_if_not_none(params.cover_url)
  520. .done()
  521. });
  522. Some((old_view, new_view))
  523. });
  524. if let Some((Some(old_view), Some(new_view))) = value {
  525. if let Ok(handler) = self.get_handler(&old_view.layout) {
  526. handler.did_update_view(&old_view, &new_view).await?;
  527. }
  528. }
  529. if let Ok(view_pb) = self.get_view(&params.view_id).await {
  530. send_notification(&view_pb.id, FolderNotification::DidUpdateView)
  531. .payload(view_pb)
  532. .send();
  533. }
  534. Ok(())
  535. }
  536. /// Duplicate the view with the given view id.
  537. #[tracing::instrument(level = "debug", skip(self), err)]
  538. pub(crate) async fn duplicate_view(&self, view_id: &str) -> Result<(), FlowyError> {
  539. let view = self
  540. .with_folder(None, |folder| folder.views.get_view(view_id))
  541. .ok_or_else(|| FlowyError::record_not_found().context("Can't duplicate the view"))?;
  542. let handler = self.get_handler(&view.layout)?;
  543. let view_data = handler.duplicate_view(&view.id).await?;
  544. let duplicate_params = CreateViewParams {
  545. parent_view_id: view.parent_view_id.clone(),
  546. name: format!("{} (copy)", &view.name),
  547. desc: view.desc.clone(),
  548. layout: view.layout.clone().into(),
  549. initial_data: view_data.to_vec(),
  550. view_id: gen_view_id(),
  551. meta: Default::default(),
  552. set_as_current: true,
  553. };
  554. let _ = self.create_view_with_params(duplicate_params).await?;
  555. Ok(())
  556. }
  557. #[tracing::instrument(level = "trace", skip(self), err)]
  558. pub(crate) async fn set_current_view(&self, view_id: &str) -> Result<(), FlowyError> {
  559. let folder = self.mutex_folder.lock();
  560. let folder = folder.as_ref().ok_or_else(folder_not_init_error)?;
  561. folder.set_current_view(view_id);
  562. let workspace = folder.get_current_workspace();
  563. let view = folder
  564. .get_current_view()
  565. .and_then(|view_id| folder.views.get_view(&view_id));
  566. send_workspace_setting_notification(workspace, view);
  567. Ok(())
  568. }
  569. #[tracing::instrument(level = "trace", skip(self))]
  570. pub(crate) async fn get_current_view(&self) -> Option<ViewPB> {
  571. let view_id = self.with_folder(None, |folder| folder.get_current_view())?;
  572. self.get_view(&view_id).await.ok()
  573. }
  574. #[tracing::instrument(level = "trace", skip(self))]
  575. pub(crate) async fn get_all_trash(&self) -> Vec<TrashInfo> {
  576. self.with_folder(vec![], |folder| folder.get_all_trash())
  577. }
  578. #[tracing::instrument(level = "trace", skip(self))]
  579. pub(crate) async fn restore_all_trash(&self) {
  580. self.with_folder((), |folder| {
  581. folder.remote_all_trash();
  582. });
  583. send_notification("trash", FolderNotification::DidUpdateTrash)
  584. .payload(RepeatedTrashPB { items: vec![] })
  585. .send();
  586. }
  587. #[tracing::instrument(level = "trace", skip(self))]
  588. pub(crate) async fn restore_trash(&self, trash_id: &str) {
  589. self.with_folder((), |folder| {
  590. folder.delete_trash(vec![trash_id.to_string()]);
  591. });
  592. }
  593. /// Delete all the trash permanently.
  594. #[tracing::instrument(level = "trace", skip(self))]
  595. pub(crate) async fn delete_all_trash(&self) {
  596. let deleted_trash = self.with_folder(vec![], |folder| folder.get_all_trash());
  597. for trash in deleted_trash {
  598. let _ = self.delete_trash(&trash.id).await;
  599. }
  600. send_notification("trash", FolderNotification::DidUpdateTrash)
  601. .payload(RepeatedTrashPB { items: vec![] })
  602. .send();
  603. }
  604. /// Delete the trash permanently.
  605. /// Delete the view will delete all the resources that the view holds. For example, if the view
  606. /// is a database view. Then the database will be deleted as well.
  607. #[tracing::instrument(level = "debug", skip(self, view_id), err)]
  608. pub async fn delete_trash(&self, view_id: &str) -> FlowyResult<()> {
  609. let view = self.with_folder(None, |folder| folder.views.get_view(view_id));
  610. self.with_folder((), |folder| {
  611. folder.delete_trash(vec![view_id.to_string()]);
  612. folder.views.delete_views(vec![view_id]);
  613. });
  614. if let Some(view) = view {
  615. if let Ok(handler) = self.get_handler(&view.layout) {
  616. handler.delete_view(view_id).await?;
  617. }
  618. }
  619. Ok(())
  620. }
  621. pub(crate) async fn import(&self, import_data: ImportParams) -> FlowyResult<View> {
  622. if import_data.data.is_none() && import_data.file_path.is_none() {
  623. return Err(FlowyError::new(
  624. ErrorCode::InvalidParams,
  625. "data or file_path is required",
  626. ));
  627. }
  628. let handler = self.get_handler(&import_data.view_layout)?;
  629. let view_id = gen_view_id();
  630. if let Some(data) = import_data.data {
  631. handler
  632. .import_from_bytes(&view_id, &import_data.name, import_data.import_type, data)
  633. .await?;
  634. }
  635. if let Some(file_path) = import_data.file_path {
  636. handler
  637. .import_from_file_path(&view_id, &import_data.name, file_path)
  638. .await?;
  639. }
  640. let params = CreateViewParams {
  641. parent_view_id: import_data.parent_view_id,
  642. name: import_data.name,
  643. desc: "".to_string(),
  644. layout: import_data.view_layout.clone().into(),
  645. initial_data: vec![],
  646. view_id,
  647. meta: Default::default(),
  648. set_as_current: false,
  649. };
  650. let view = create_view(params, import_data.view_layout);
  651. self.with_folder((), |folder| {
  652. folder.insert_view(view.clone());
  653. });
  654. notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.parent_view_id.clone()]);
  655. Ok(view)
  656. }
  657. /// Returns a handler that implements the [FolderOperationHandler] trait
  658. fn get_handler(
  659. &self,
  660. view_layout: &ViewLayout,
  661. ) -> FlowyResult<Arc<dyn FolderOperationHandler + Send + Sync>> {
  662. match self.operation_handlers.get(view_layout) {
  663. None => Err(FlowyError::internal().context(format!(
  664. "Get data processor failed. Unknown layout type: {:?}",
  665. view_layout
  666. ))),
  667. Some(processor) => Ok(processor.clone()),
  668. }
  669. }
  670. /// Returns the relation of the view. The relation is a tuple of (is_workspace, parent_view_id,
  671. /// child_view_ids). If the view is a workspace, then the parent_view_id is the workspace id.
  672. /// Otherwise, the parent_view_id is the parent view id of the view. The child_view_ids is the
  673. /// child view ids of the view.
  674. async fn get_view_relation(&self, view_id: &str) -> Option<(bool, String, Vec<String>)> {
  675. self.with_folder(None, |folder| {
  676. let view = folder.views.get_view(view_id)?;
  677. match folder.views.get_view(&view.parent_view_id) {
  678. None => folder.get_current_workspace().map(|workspace| {
  679. (
  680. true,
  681. workspace.id,
  682. workspace
  683. .child_views
  684. .items
  685. .into_iter()
  686. .map(|view| view.id)
  687. .collect::<Vec<String>>(),
  688. )
  689. }),
  690. Some(parent_view) => Some((
  691. false,
  692. parent_view.id.clone(),
  693. parent_view
  694. .children
  695. .items
  696. .clone()
  697. .into_iter()
  698. .map(|view| view.id)
  699. .collect::<Vec<String>>(),
  700. )),
  701. }
  702. })
  703. }
  704. pub async fn get_folder_snapshots(
  705. &self,
  706. workspace_id: &str,
  707. ) -> FlowyResult<Vec<FolderSnapshotPB>> {
  708. let mut snapshots = vec![];
  709. if let Some(snapshot) = self
  710. .cloud_service
  711. .get_folder_latest_snapshot(workspace_id)
  712. .await?
  713. .map(|snapshot| FolderSnapshotPB {
  714. snapshot_id: snapshot.snapshot_id,
  715. snapshot_desc: "".to_string(),
  716. created_at: snapshot.created_at,
  717. data: snapshot.data,
  718. })
  719. {
  720. snapshots.push(snapshot);
  721. }
  722. Ok(snapshots)
  723. }
  724. /// Only expose this method for testing
  725. #[cfg(debug_assertions)]
  726. pub fn get_mutex_folder(&self) -> &Arc<MutexFolder> {
  727. &self.mutex_folder
  728. }
  729. /// Only expose this method for testing
  730. #[cfg(debug_assertions)]
  731. pub fn get_cloud_service(&self) -> &Arc<dyn FolderCloudService> {
  732. &self.cloud_service
  733. }
  734. }
  735. /// Listen on the [ViewChange] after create/delete/update events happened
  736. fn subscribe_folder_view_changed(
  737. mut rx: ViewChangeReceiver,
  738. weak_mutex_folder: &Weak<MutexFolder>,
  739. ) {
  740. let weak_mutex_folder = weak_mutex_folder.clone();
  741. tokio::spawn(async move {
  742. while let Ok(value) = rx.recv().await {
  743. if let Some(folder) = weak_mutex_folder.upgrade() {
  744. tracing::trace!("Did receive view change: {:?}", value);
  745. match value {
  746. ViewChange::DidCreateView { view } => {
  747. notify_child_views_changed(
  748. view_pb_without_child_views(Arc::new(view.clone())),
  749. ChildViewChangeReason::DidCreateView,
  750. );
  751. notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
  752. },
  753. ViewChange::DidDeleteView { views } => {
  754. for view in views {
  755. notify_child_views_changed(
  756. view_pb_without_child_views(view),
  757. ChildViewChangeReason::DidDeleteView,
  758. );
  759. }
  760. },
  761. ViewChange::DidUpdate { view } => {
  762. notify_child_views_changed(
  763. view_pb_without_child_views(Arc::new(view.clone())),
  764. ChildViewChangeReason::DidUpdateView,
  765. );
  766. notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
  767. },
  768. };
  769. }
  770. }
  771. });
  772. }
  773. fn subscribe_folder_snapshot_state_changed(
  774. workspace_id: String,
  775. weak_mutex_folder: &Weak<MutexFolder>,
  776. ) {
  777. let weak_mutex_folder = weak_mutex_folder.clone();
  778. tokio::spawn(async move {
  779. if let Some(mutex_folder) = weak_mutex_folder.upgrade() {
  780. let stream = mutex_folder
  781. .lock()
  782. .as_ref()
  783. .map(|folder| folder.subscribe_snapshot_state());
  784. if let Some(mut state_stream) = stream {
  785. while let Some(snapshot_state) = state_stream.next().await {
  786. if let Some(new_snapshot_id) = snapshot_state.snapshot_id() {
  787. tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id);
  788. send_notification(
  789. &workspace_id,
  790. FolderNotification::DidUpdateFolderSnapshotState,
  791. )
  792. .payload(FolderSnapshotStatePB { new_snapshot_id })
  793. .send();
  794. }
  795. }
  796. }
  797. }
  798. });
  799. }
  800. fn subscribe_folder_sync_state_changed(
  801. workspace_id: String,
  802. mut folder_sync_state_rx: WatchStream<SyncState>,
  803. _weak_mutex_folder: &Weak<MutexFolder>,
  804. ) {
  805. tokio::spawn(async move {
  806. while let Some(state) = folder_sync_state_rx.next().await {
  807. send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
  808. .payload(FolderSyncStatePB::from(state))
  809. .send();
  810. }
  811. });
  812. }
  813. /// Listen on the [TrashChange]s and notify the frontend some views were changed.
  814. fn subscribe_folder_trash_changed(
  815. mut rx: TrashChangeReceiver,
  816. weak_mutex_folder: &Weak<MutexFolder>,
  817. ) {
  818. let weak_mutex_folder = weak_mutex_folder.clone();
  819. tokio::spawn(async move {
  820. while let Ok(value) = rx.recv().await {
  821. if let Some(folder) = weak_mutex_folder.upgrade() {
  822. let mut unique_ids = HashSet::new();
  823. tracing::trace!("Did receive trash change: {:?}", value);
  824. let ids = match value {
  825. TrashChange::DidCreateTrash { ids } => ids,
  826. TrashChange::DidDeleteTrash { ids } => ids,
  827. };
  828. if let Some(folder) = folder.lock().as_ref() {
  829. let views = folder.views.get_views(&ids);
  830. for view in views {
  831. unique_ids.insert(view.parent_view_id.clone());
  832. }
  833. let repeated_trash: RepeatedTrashPB = folder.get_all_trash().into();
  834. send_notification("trash", FolderNotification::DidUpdateTrash)
  835. .payload(repeated_trash)
  836. .send();
  837. }
  838. let parent_view_ids = unique_ids.into_iter().collect();
  839. notify_parent_view_did_change(folder.clone(), parent_view_ids);
  840. }
  841. }
  842. });
  843. }
  844. /// Return the views that belong to the workspace. The views are filtered by the trash.
  845. fn get_workspace_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
  846. let trash_ids = folder
  847. .get_all_trash()
  848. .into_iter()
  849. .map(|trash| trash.id)
  850. .collect::<Vec<String>>();
  851. let mut views = folder.get_workspace_views(workspace_id);
  852. views.retain(|view| !trash_ids.contains(&view.id));
  853. views
  854. .into_iter()
  855. .map(|view| {
  856. // Get child views
  857. let child_views = folder
  858. .views
  859. .get_views_belong_to(&view.id)
  860. .into_iter()
  861. .collect();
  862. view_pb_with_child_views(view, child_views)
  863. })
  864. .collect()
  865. }
  866. fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
  867. let repeated_view: RepeatedViewPB = get_workspace_view_pbs(workspace_id, folder).into();
  868. tracing::trace!("Did update workspace views: {:?}", repeated_view);
  869. send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
  870. .payload(repeated_view)
  871. .send();
  872. }
  873. /// Notify the the list of parent view ids that its child views were changed.
  874. #[tracing::instrument(level = "debug", skip(folder, parent_view_ids))]
  875. fn notify_parent_view_did_change<T: AsRef<str>>(
  876. folder: Arc<MutexFolder>,
  877. parent_view_ids: Vec<T>,
  878. ) -> Option<()> {
  879. let folder = folder.lock();
  880. let folder = folder.as_ref()?;
  881. let workspace_id = folder.get_current_workspace_id()?;
  882. let trash_ids = folder
  883. .get_all_trash()
  884. .into_iter()
  885. .map(|trash| trash.id)
  886. .collect::<Vec<String>>();
  887. for parent_view_id in parent_view_ids {
  888. let parent_view_id = parent_view_id.as_ref();
  889. // if the view's parent id equal to workspace id. Then it will fetch the current
  890. // workspace views. Because the the workspace is not a view stored in the views map.
  891. if parent_view_id == workspace_id {
  892. notify_did_update_workspace(&workspace_id, folder)
  893. } else {
  894. // Parent view can contain a list of child views. Currently, only get the first level
  895. // child views.
  896. let parent_view = folder.views.get_view(parent_view_id)?;
  897. let mut child_views = folder.views.get_views_belong_to(parent_view_id);
  898. child_views.retain(|view| !trash_ids.contains(&view.id));
  899. event!(Level::DEBUG, child_views_count = child_views.len());
  900. // Post the notification
  901. let parent_view_pb = view_pb_with_child_views(parent_view, child_views);
  902. send_notification(parent_view_id, FolderNotification::DidUpdateView)
  903. .payload(parent_view_pb)
  904. .send();
  905. }
  906. }
  907. None
  908. }
  909. pub enum ChildViewChangeReason {
  910. DidCreateView,
  911. DidDeleteView,
  912. DidUpdateView,
  913. }
  914. /// Notify the the list of parent view ids that its child views were changed.
  915. #[tracing::instrument(level = "debug", skip_all)]
  916. fn notify_child_views_changed(view_pb: ViewPB, reason: ChildViewChangeReason) {
  917. let parent_view_id = view_pb.parent_view_id.clone();
  918. let mut payload = ChildViewUpdatePB {
  919. parent_view_id: view_pb.parent_view_id.clone(),
  920. ..Default::default()
  921. };
  922. match reason {
  923. ChildViewChangeReason::DidCreateView => {
  924. payload.create_child_views.push(view_pb);
  925. },
  926. ChildViewChangeReason::DidDeleteView => {
  927. payload.delete_child_views.push(view_pb.id);
  928. },
  929. ChildViewChangeReason::DidUpdateView => {
  930. payload.update_child_views.push(view_pb);
  931. },
  932. }
  933. send_notification(&parent_view_id, FolderNotification::DidUpdateChildViews)
  934. .payload(payload)
  935. .send();
  936. }
  937. fn folder_not_init_error() -> FlowyError {
  938. FlowyError::internal().context("Folder not initialized")
  939. }
  940. #[derive(Clone, Default)]
  941. pub struct MutexFolder(Arc<Mutex<Option<Folder>>>);
  942. impl Deref for MutexFolder {
  943. type Target = Arc<Mutex<Option<Folder>>>;
  944. fn deref(&self) -> &Self::Target {
  945. &self.0
  946. }
  947. }
  948. unsafe impl Sync for MutexFolder {}
  949. unsafe impl Send for MutexFolder {}
  950. pub enum FolderInitializeData {
  951. Empty,
  952. Raw(CollabRawData),
  953. Data(FolderData),
  954. }