view_operation.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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, 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. icon_url: Option<String>,
  49. cover_url: Option<String>,
  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. icon_url: None,
  61. cover_url: 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. layout: self.layout,
  98. icon_url: self.icon_url,
  99. cover_url: self.cover_url,
  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. _workspace_view_builder: Arc<RwLock<WorkspaceViewBuilder>>,
  141. ) -> FutureResult<(), FlowyError> {
  142. FutureResult::new(async { Ok(()) })
  143. }
  144. /// Closes the view and releases the resources that this view has in
  145. /// the backend
  146. fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError>;
  147. /// Called when the view is deleted.
  148. /// This will called after the view is deleted from the trash.
  149. fn delete_view(&self, view_id: &str) -> FutureResult<(), FlowyError>;
  150. /// Returns the [ViewData] that can be used to create the same view.
  151. fn duplicate_view(&self, view_id: &str) -> FutureResult<ViewData, FlowyError>;
  152. /// Create a view with the data.
  153. ///
  154. /// # Arguments
  155. ///
  156. /// * `user_id`: the user id
  157. /// * `view_id`: the view id
  158. /// * `name`: the name of the view
  159. /// * `data`: initial data of the view. The data should be parsed by the [FolderOperationHandler]
  160. /// implementation. For example, the data of the database will be [DatabaseData].
  161. /// * `layout`: the layout of the view
  162. /// * `meta`: use to carry extra information. For example, the database view will use this
  163. /// to carry the reference database id.
  164. fn create_view_with_view_data(
  165. &self,
  166. user_id: i64,
  167. view_id: &str,
  168. name: &str,
  169. data: Vec<u8>,
  170. layout: ViewLayout,
  171. meta: HashMap<String, String>,
  172. ) -> FutureResult<(), FlowyError>;
  173. /// Create a view with the pre-defined data.
  174. /// For example, the initial data of the grid/calendar/kanban board when
  175. /// you create a new view.
  176. fn create_built_in_view(
  177. &self,
  178. user_id: i64,
  179. view_id: &str,
  180. name: &str,
  181. layout: ViewLayout,
  182. ) -> FutureResult<(), FlowyError>;
  183. /// Create a view by importing data
  184. fn import_from_bytes(
  185. &self,
  186. view_id: &str,
  187. name: &str,
  188. import_type: ImportType,
  189. bytes: Vec<u8>,
  190. ) -> FutureResult<(), FlowyError>;
  191. /// Create a view by importing data from a file
  192. fn import_from_file_path(
  193. &self,
  194. view_id: &str,
  195. name: &str,
  196. path: String,
  197. ) -> FutureResult<(), FlowyError>;
  198. /// Called when the view is updated. The handler is the `old` registered handler.
  199. fn did_update_view(&self, _old: &View, _new: &View) -> FutureResult<(), FlowyError> {
  200. FutureResult::new(async move { Ok(()) })
  201. }
  202. }
  203. pub type FolderOperationHandlers =
  204. Arc<HashMap<ViewLayout, Arc<dyn FolderOperationHandler + Send + Sync>>>;
  205. impl From<ViewLayoutPB> for ViewLayout {
  206. fn from(pb: ViewLayoutPB) -> Self {
  207. match pb {
  208. ViewLayoutPB::Document => ViewLayout::Document,
  209. ViewLayoutPB::Grid => ViewLayout::Grid,
  210. ViewLayoutPB::Board => ViewLayout::Board,
  211. ViewLayoutPB::Calendar => ViewLayout::Calendar,
  212. }
  213. }
  214. }
  215. pub(crate) fn create_view(params: CreateViewParams, layout: ViewLayout) -> View {
  216. let time = timestamp();
  217. View {
  218. id: params.view_id,
  219. parent_view_id: params.parent_view_id,
  220. name: params.name,
  221. desc: params.desc,
  222. children: Default::default(),
  223. created_at: time,
  224. layout,
  225. icon_url: None,
  226. cover_url: None,
  227. }
  228. }
  229. pub fn gen_view_id() -> String {
  230. uuid::Uuid::new_v4().to_string()
  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. }