manager.rs 40 KB

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