manager.rs 33 KB

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