view_operation.rs 11 KB

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