manager.rs 42 KB

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