view_operation.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. use std::collections::HashMap;
  2. use std::future::Future;
  3. use std::sync::Arc;
  4. use bytes::Bytes;
  5. pub use collab_folder::core::View;
  6. use collab_folder::core::{RepeatedViewIdentifier, ViewIcon, ViewIdentifier, ViewLayout};
  7. use tokio::sync::RwLock;
  8. use flowy_error::FlowyError;
  9. use flowy_folder_deps::cloud::gen_view_id;
  10. use lib_infra::future::FutureResult;
  11. use lib_infra::util::timestamp;
  12. use crate::entities::{CreateViewParams, ViewLayoutPB};
  13. use crate::share::ImportType;
  14. pub type ViewData = Bytes;
  15. /// A builder for creating a view for a workspace.
  16. /// The views created by this builder will be the first level views of the workspace.
  17. pub struct WorkspaceViewBuilder {
  18. pub workspace_id: String,
  19. pub views: Vec<ParentChildViews>,
  20. }
  21. impl WorkspaceViewBuilder {
  22. pub fn new(workspace_id: String) -> Self {
  23. Self {
  24. workspace_id,
  25. views: vec![],
  26. }
  27. }
  28. pub async fn with_view_builder<F, O>(&mut self, view_builder: F)
  29. where
  30. F: Fn(ViewBuilder) -> O,
  31. O: Future<Output = ParentChildViews>,
  32. {
  33. let builder = ViewBuilder::new(self.workspace_id.clone());
  34. self.views.push(view_builder(builder).await);
  35. }
  36. pub fn build(&mut self) -> Vec<ParentChildViews> {
  37. std::mem::take(&mut self.views)
  38. }
  39. }
  40. /// A builder for creating a view.
  41. /// The default layout of the view is [ViewLayout::Document]
  42. pub struct ViewBuilder {
  43. parent_view_id: String,
  44. view_id: String,
  45. name: String,
  46. desc: String,
  47. layout: ViewLayout,
  48. child_views: Vec<ParentChildViews>,
  49. is_favorite: bool,
  50. icon: Option<ViewIcon>,
  51. }
  52. impl ViewBuilder {
  53. pub fn new(parent_view_id: String) -> Self {
  54. Self {
  55. parent_view_id,
  56. view_id: gen_view_id().to_string(),
  57. name: Default::default(),
  58. desc: Default::default(),
  59. layout: ViewLayout::Document,
  60. child_views: vec![],
  61. is_favorite: false,
  62. icon: None,
  63. }
  64. }
  65. pub fn view_id(&self) -> &str {
  66. &self.view_id
  67. }
  68. pub fn with_layout(mut self, layout: ViewLayout) -> Self {
  69. self.layout = layout;
  70. self
  71. }
  72. pub fn with_name(mut self, name: &str) -> Self {
  73. self.name = name.to_string();
  74. self
  75. }
  76. pub fn with_desc(mut self, desc: &str) -> Self {
  77. self.desc = desc.to_string();
  78. self
  79. }
  80. /// Create a child view for the current view.
  81. /// The view created by this builder will be the next level view of the current view.
  82. pub async fn with_child_view_builder<F, O>(mut self, child_view_builder: F) -> Self
  83. where
  84. F: Fn(ViewBuilder) -> O,
  85. O: Future<Output = ParentChildViews>,
  86. {
  87. let builder = ViewBuilder::new(self.view_id.clone());
  88. self.child_views.push(child_view_builder(builder).await);
  89. self
  90. }
  91. pub fn build(self) -> ParentChildViews {
  92. let view = View {
  93. id: self.view_id,
  94. parent_view_id: self.parent_view_id,
  95. name: self.name,
  96. desc: self.desc,
  97. created_at: timestamp(),
  98. is_favorite: self.is_favorite,
  99. layout: self.layout,
  100. icon: self.icon,
  101. children: RepeatedViewIdentifier::new(
  102. self
  103. .child_views
  104. .iter()
  105. .map(|v| ViewIdentifier {
  106. id: v.parent_view.id.clone(),
  107. })
  108. .collect(),
  109. ),
  110. };
  111. ParentChildViews {
  112. parent_view: view,
  113. child_views: self.child_views,
  114. }
  115. }
  116. }
  117. pub struct ParentChildViews {
  118. pub parent_view: View,
  119. pub child_views: Vec<ParentChildViews>,
  120. }
  121. pub struct FlattedViews;
  122. impl FlattedViews {
  123. pub fn flatten_views(views: Vec<ParentChildViews>) -> Vec<View> {
  124. let mut result = vec![];
  125. for view in views {
  126. result.push(view.parent_view);
  127. result.append(&mut Self::flatten_views(view.child_views));
  128. }
  129. result
  130. }
  131. }
  132. /// The handler will be used to handler the folder operation for a specific
  133. /// view layout. Each [ViewLayout] will have a handler. So when creating a new
  134. /// view, the [ViewLayout] will be used to get the handler.
  135. ///
  136. pub trait FolderOperationHandler {
  137. /// Create the view for the workspace of new user.
  138. /// Only called once when the user is created.
  139. fn create_workspace_view(
  140. &self,
  141. _uid: i64,
  142. _workspace_view_builder: Arc<RwLock<WorkspaceViewBuilder>>,
  143. ) -> FutureResult<(), FlowyError> {
  144. FutureResult::new(async { Ok(()) })
  145. }
  146. /// Closes the view and releases the resources that this view has in
  147. /// the backend
  148. fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError>;
  149. /// Called when the view is deleted.
  150. /// This will called after the view is deleted from the trash.
  151. fn delete_view(&self, view_id: &str) -> FutureResult<(), FlowyError>;
  152. /// Returns the [ViewData] that can be used to create the same view.
  153. fn duplicate_view(&self, view_id: &str) -> FutureResult<ViewData, FlowyError>;
  154. /// Create a view with the data.
  155. ///
  156. /// # Arguments
  157. ///
  158. /// * `user_id`: the user id
  159. /// * `view_id`: the view id
  160. /// * `name`: the name of the view
  161. /// * `data`: initial data of the view. The data should be parsed by the [FolderOperationHandler]
  162. /// implementation. For example, the data of the database will be [DatabaseData].
  163. /// * `layout`: the layout of the view
  164. /// * `meta`: use to carry extra information. For example, the database view will use this
  165. /// to carry the reference database id.
  166. fn create_view_with_view_data(
  167. &self,
  168. user_id: i64,
  169. view_id: &str,
  170. name: &str,
  171. data: Vec<u8>,
  172. layout: ViewLayout,
  173. meta: HashMap<String, String>,
  174. ) -> FutureResult<(), FlowyError>;
  175. /// Create a view with the pre-defined data.
  176. /// For example, the initial data of the grid/calendar/kanban board when
  177. /// you create a new view.
  178. fn create_built_in_view(
  179. &self,
  180. user_id: i64,
  181. view_id: &str,
  182. name: &str,
  183. layout: ViewLayout,
  184. ) -> FutureResult<(), FlowyError>;
  185. /// Create a view by importing data
  186. fn import_from_bytes(
  187. &self,
  188. uid: i64,
  189. view_id: &str,
  190. name: &str,
  191. import_type: ImportType,
  192. bytes: Vec<u8>,
  193. ) -> FutureResult<(), FlowyError>;
  194. /// Create a view by importing data from a file
  195. fn import_from_file_path(
  196. &self,
  197. view_id: &str,
  198. name: &str,
  199. path: String,
  200. ) -> FutureResult<(), FlowyError>;
  201. /// Called when the view is updated. The handler is the `old` registered handler.
  202. fn did_update_view(&self, _old: &View, _new: &View) -> FutureResult<(), FlowyError> {
  203. FutureResult::new(async move { Ok(()) })
  204. }
  205. }
  206. pub type FolderOperationHandlers =
  207. Arc<HashMap<ViewLayout, Arc<dyn FolderOperationHandler + Send + Sync>>>;
  208. impl From<ViewLayoutPB> for ViewLayout {
  209. fn from(pb: ViewLayoutPB) -> Self {
  210. match pb {
  211. ViewLayoutPB::Document => ViewLayout::Document,
  212. ViewLayoutPB::Grid => ViewLayout::Grid,
  213. ViewLayoutPB::Board => ViewLayout::Board,
  214. ViewLayoutPB::Calendar => ViewLayout::Calendar,
  215. }
  216. }
  217. }
  218. pub(crate) fn create_view(params: CreateViewParams, layout: ViewLayout) -> View {
  219. let time = timestamp();
  220. View {
  221. id: params.view_id,
  222. parent_view_id: params.parent_view_id,
  223. name: params.name,
  224. desc: params.desc,
  225. children: Default::default(),
  226. created_at: time,
  227. is_favorite: false,
  228. layout,
  229. icon: None,
  230. }
  231. }
  232. #[cfg(test)]
  233. mod tests {
  234. use crate::view_operation::{FlattedViews, WorkspaceViewBuilder};
  235. #[tokio::test]
  236. async fn create_first_level_views_test() {
  237. let workspace_id = "w1".to_string();
  238. let mut builder = WorkspaceViewBuilder::new(workspace_id);
  239. builder
  240. .with_view_builder(|view_builder| async { view_builder.with_name("1").build() })
  241. .await;
  242. builder
  243. .with_view_builder(|view_builder| async { view_builder.with_name("2").build() })
  244. .await;
  245. builder
  246. .with_view_builder(|view_builder| async { view_builder.with_name("3").build() })
  247. .await;
  248. let workspace_views = builder.build();
  249. assert_eq!(workspace_views.len(), 3);
  250. let views = FlattedViews::flatten_views(workspace_views);
  251. assert_eq!(views.len(), 3);
  252. }
  253. #[tokio::test]
  254. async fn create_view_with_child_views_test() {
  255. let workspace_id = "w1".to_string();
  256. let mut builder = WorkspaceViewBuilder::new(workspace_id);
  257. builder
  258. .with_view_builder(|view_builder| async {
  259. view_builder
  260. .with_name("1")
  261. .with_child_view_builder(|child_view_builder| async {
  262. child_view_builder.with_name("1_1").build()
  263. })
  264. .await
  265. .with_child_view_builder(|child_view_builder| async {
  266. child_view_builder.with_name("1_2").build()
  267. })
  268. .await
  269. .build()
  270. })
  271. .await;
  272. builder
  273. .with_view_builder(|view_builder| async {
  274. view_builder
  275. .with_name("2")
  276. .with_child_view_builder(|child_view_builder| async {
  277. child_view_builder.with_name("2_1").build()
  278. })
  279. .await
  280. .build()
  281. })
  282. .await;
  283. let workspace_views = builder.build();
  284. assert_eq!(workspace_views.len(), 2);
  285. assert_eq!(workspace_views[0].parent_view.name, "1");
  286. assert_eq!(workspace_views[0].child_views.len(), 2);
  287. assert_eq!(workspace_views[0].child_views[0].parent_view.name, "1_1");
  288. assert_eq!(workspace_views[0].child_views[1].parent_view.name, "1_2");
  289. assert_eq!(workspace_views[1].child_views.len(), 1);
  290. assert_eq!(workspace_views[1].child_views[0].parent_view.name, "2_1");
  291. let views = FlattedViews::flatten_views(workspace_views);
  292. assert_eq!(views.len(), 5);
  293. }
  294. #[tokio::test]
  295. async fn create_three_level_view_test() {
  296. let workspace_id = "w1".to_string();
  297. let mut builder = WorkspaceViewBuilder::new(workspace_id);
  298. builder
  299. .with_view_builder(|view_builder| async {
  300. view_builder
  301. .with_name("1")
  302. .with_child_view_builder(|child_view_builder| async {
  303. child_view_builder
  304. .with_name("1_1")
  305. .with_child_view_builder(|b| async { b.with_name("1_1_1").build() })
  306. .await
  307. .with_child_view_builder(|b| async { b.with_name("1_1_2").build() })
  308. .await
  309. .build()
  310. })
  311. .await
  312. .with_child_view_builder(|child_view_builder| async {
  313. child_view_builder
  314. .with_name("1_2")
  315. .with_child_view_builder(|b| async { b.with_name("1_2_1").build() })
  316. .await
  317. .with_child_view_builder(|b| async { b.with_name("1_2_2").build() })
  318. .await
  319. .build()
  320. })
  321. .await
  322. .build()
  323. })
  324. .await;
  325. let workspace_views = builder.build();
  326. assert_eq!(workspace_views.len(), 1);
  327. assert_eq!(workspace_views[0].parent_view.name, "1");
  328. assert_eq!(workspace_views[0].child_views.len(), 2);
  329. assert_eq!(workspace_views[0].child_views[0].parent_view.name, "1_1");
  330. assert_eq!(workspace_views[0].child_views[1].parent_view.name, "1_2");
  331. assert_eq!(
  332. workspace_views[0].child_views[0].child_views[0]
  333. .parent_view
  334. .name,
  335. "1_1_1"
  336. );
  337. assert_eq!(
  338. workspace_views[0].child_views[0].child_views[1]
  339. .parent_view
  340. .name,
  341. "1_1_2"
  342. );
  343. assert_eq!(
  344. workspace_views[0].child_views[1].child_views[0]
  345. .parent_view
  346. .name,
  347. "1_2_1"
  348. );
  349. assert_eq!(
  350. workspace_views[0].child_views[1].child_views[1]
  351. .parent_view
  352. .name,
  353. "1_2_2"
  354. );
  355. let views = FlattedViews::flatten_views(workspace_views);
  356. assert_eq!(views.len(), 7);
  357. }
  358. }