diff --git a/libs/client-api/src/http.rs b/libs/client-api/src/http.rs index 01c720a5..f65dee59 100644 --- a/libs/client-api/src/http.rs +++ b/libs/client-api/src/http.rs @@ -3,10 +3,10 @@ use app_error::AppError; use app_error::ErrorCode; use client_api_entity::auth_dto::DeleteUserQuery; use client_api_entity::server_info_dto::ServerInfoResponseItem; -use client_api_entity::workspace_dto::FolderView; -use client_api_entity::workspace_dto::QueryWorkspaceFolder; -use client_api_entity::workspace_dto::QueryWorkspaceParam; -use client_api_entity::workspace_dto::SectionItems; +use client_api_entity::workspace_dto::FavoriteSectionItems; +use client_api_entity::workspace_dto::RecentSectionItems; +use client_api_entity::workspace_dto::TrashSectionItems; +use client_api_entity::workspace_dto::{FolderView, QueryWorkspaceFolder, QueryWorkspaceParam}; use client_api_entity::AuthProvider; use client_api_entity::CollabType; use gotrue::grant::PasswordGrant; @@ -727,7 +727,7 @@ impl Client { pub async fn get_workspace_favorite( &self, workspace_id: &str, - ) -> Result { + ) -> Result { let url = format!("{}/api/workspace/{}/favorite", self.base_url, workspace_id); let resp = self .http_client_with_auth(Method::GET, &url) @@ -735,7 +735,7 @@ impl Client { .send() .await?; log_request_id(&resp); - AppResponse::::from_response(resp) + AppResponse::::from_response(resp) .await? .into_data() } @@ -744,7 +744,7 @@ impl Client { pub async fn get_workspace_recent( &self, workspace_id: &str, - ) -> Result { + ) -> Result { let url = format!("{}/api/workspace/{}/recent", self.base_url, workspace_id); let resp = self .http_client_with_auth(Method::GET, &url) @@ -752,7 +752,7 @@ impl Client { .send() .await?; log_request_id(&resp); - AppResponse::::from_response(resp) + AppResponse::::from_response(resp) .await? .into_data() } @@ -761,7 +761,7 @@ impl Client { pub async fn get_workspace_trash( &self, workspace_id: &str, - ) -> Result { + ) -> Result { let url = format!("{}/api/workspace/{}/trash", self.base_url, workspace_id); let resp = self .http_client_with_auth(Method::GET, &url) @@ -769,7 +769,7 @@ impl Client { .send() .await?; log_request_id(&resp); - AppResponse::::from_response(resp) + AppResponse::::from_response(resp) .await? .into_data() } diff --git a/libs/shared-entity/src/dto/workspace_dto.rs b/libs/shared-entity/src/dto/workspace_dto.rs index f3b539f9..ca7eadfe 100644 --- a/libs/shared-entity/src/dto/workspace_dto.rs +++ b/libs/shared-entity/src/dto/workspace_dto.rs @@ -142,6 +142,42 @@ pub struct PublishedDuplicate { pub dest_view_id: String, } +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct RecentFolderView { + #[serde(flatten)] + pub view: FolderView, + pub last_viewed_at: DateTime, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct FavoriteFolderView { + #[serde(flatten)] + pub view: FolderView, + pub favorited_at: DateTime, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct TrashFolderView { + #[serde(flatten)] + pub view: FolderView, + pub deleted_at: DateTime, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct RecentSectionItems { + pub views: Vec, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct FavoriteSectionItems { + pub views: Vec, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct TrashSectionItems { + pub views: Vec, +} + #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct FolderView { pub view_id: String, @@ -173,11 +209,6 @@ pub struct PublishInfoView { pub info: PublishInfo, } -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct SectionItems { - pub views: Vec, -} - #[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize_repr, Deserialize_repr)] #[repr(u8)] pub enum IconType { diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 0dca9987..e7470ebf 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -1575,7 +1575,7 @@ async fn get_recent_views_handler( user_uuid: UserUuid, workspace_id: web::Path, state: Data, -) -> Result>> { +) -> Result>> { let uid = state.user_cache.get_user_uid(&user_uuid).await?; let workspace_id = workspace_id.into_inner(); state @@ -1589,7 +1589,7 @@ async fn get_recent_views_handler( workspace_id, ) .await?; - let section_items = SectionItems { + let section_items = RecentSectionItems { views: folder_views, }; Ok(Json(AppResponse::Ok().with_data(section_items))) @@ -1599,7 +1599,7 @@ async fn get_favorite_views_handler( user_uuid: UserUuid, workspace_id: web::Path, state: Data, -) -> Result>> { +) -> Result>> { let uid = state.user_cache.get_user_uid(&user_uuid).await?; let workspace_id = workspace_id.into_inner(); state @@ -1613,7 +1613,7 @@ async fn get_favorite_views_handler( workspace_id, ) .await?; - let section_items = SectionItems { + let section_items = FavoriteSectionItems { views: folder_views, }; Ok(Json(AppResponse::Ok().with_data(section_items))) @@ -1623,7 +1623,7 @@ async fn get_trash_views_handler( user_uuid: UserUuid, workspace_id: web::Path, state: Data, -) -> Result>> { +) -> Result>> { let uid = state.user_cache.get_user_uid(&user_uuid).await?; let workspace_id = workspace_id.into_inner(); state @@ -1632,7 +1632,7 @@ async fn get_trash_views_handler( .await?; let folder_views = get_user_trash_folder_views(&state.collab_access_control_storage, uid, workspace_id).await?; - let section_items = SectionItems { + let section_items = TrashSectionItems { views: folder_views, }; Ok(Json(AppResponse::Ok().with_data(section_items))) diff --git a/src/biz/collab/folder_view.rs b/src/biz/collab/folder_view.rs index 2082f10d..f1051f35 100644 --- a/src/biz/collab/folder_view.rs +++ b/src/biz/collab/folder_view.rs @@ -3,7 +3,9 @@ use std::collections::HashSet; use app_error::AppError; use chrono::DateTime; use collab_folder::{Folder, SectionItem, ViewLayout as CollabFolderViewLayout}; -use shared_entity::dto::workspace_dto::{FolderView, FolderViewMinimal, ViewLayout}; +use shared_entity::dto::workspace_dto::{ + FavoriteFolderView, FolderView, FolderViewMinimal, RecentFolderView, TrashFolderView, ViewLayout, +}; /// Return all folders belonging to a workspace, excluding private sections which the user does not have access to. pub fn collab_folder_to_folder_view( @@ -116,27 +118,96 @@ fn to_folder_view( }) } -pub fn section_items_to_folder_view( +pub fn section_items_to_favorite_folder_view( section_items: &[SectionItem], folder: &Folder, published_view_ids: &HashSet, -) -> Vec { +) -> Vec { section_items .iter() .filter_map(|section_item| { let view = folder.get_view(§ion_item.id); - view.map(|v| FolderView { - view_id: v.id.clone(), - name: v.name.clone(), - icon: v.icon.as_ref().map(|icon| to_dto_view_icon(icon.clone())), - is_space: false, - is_private: false, - is_published: published_view_ids.contains(&v.id), - created_at: DateTime::from_timestamp(v.created_at, 0).unwrap_or_default(), - last_edited_time: DateTime::from_timestamp(v.last_edited_time, 0).unwrap_or_default(), - layout: to_dto_view_layout(&v.layout), - extra: v.extra.as_ref().map(|e| parse_extra_field_as_json(e)), - children: vec![], + view.map(|v| { + let folder_view = FolderView { + view_id: v.id.clone(), + name: v.name.clone(), + icon: v.icon.as_ref().map(|icon| to_dto_view_icon(icon.clone())), + is_space: false, + is_private: false, + is_published: published_view_ids.contains(&v.id), + created_at: DateTime::from_timestamp(v.created_at, 0).unwrap_or_default(), + last_edited_time: DateTime::from_timestamp(v.last_edited_time, 0).unwrap_or_default(), + layout: to_dto_view_layout(&v.layout), + extra: v.extra.as_ref().map(|e| parse_extra_field_as_json(e)), + children: vec![], + }; + FavoriteFolderView { + view: folder_view, + favorited_at: DateTime::from_timestamp(section_item.timestamp, 0).unwrap_or_default(), + } + }) + }) + .collect() +} + +pub fn section_items_to_recent_folder_view( + section_items: &[SectionItem], + folder: &Folder, + published_view_ids: &HashSet, +) -> Vec { + section_items + .iter() + .filter_map(|section_item| { + let view = folder.get_view(§ion_item.id); + view.map(|v| { + let folder_view = FolderView { + view_id: v.id.clone(), + name: v.name.clone(), + icon: v.icon.as_ref().map(|icon| to_dto_view_icon(icon.clone())), + is_space: false, + is_private: false, + is_published: published_view_ids.contains(&v.id), + created_at: DateTime::from_timestamp(v.created_at, 0).unwrap_or_default(), + last_edited_time: DateTime::from_timestamp(v.last_edited_time, 0).unwrap_or_default(), + layout: to_dto_view_layout(&v.layout), + extra: v.extra.as_ref().map(|e| parse_extra_field_as_json(e)), + children: vec![], + }; + RecentFolderView { + view: folder_view, + last_viewed_at: DateTime::from_timestamp(section_item.timestamp, 0).unwrap_or_default(), + } + }) + }) + .collect() +} + +pub fn section_items_to_trash_folder_view( + section_items: &[SectionItem], + folder: &Folder, +) -> Vec { + section_items + .iter() + .filter_map(|section_item| { + let view = folder.get_view(§ion_item.id); + view.map(|v| { + let folder_view = FolderView { + view_id: v.id.clone(), + name: v.name.clone(), + icon: v.icon.as_ref().map(|icon| to_dto_view_icon(icon.clone())), + is_space: false, + is_private: false, + is_published: false, + created_at: DateTime::from_timestamp(v.created_at, 0).unwrap_or_default(), + last_edited_time: DateTime::from_timestamp(v.last_edited_time, 0).unwrap_or_default(), + layout: to_dto_view_layout(&v.layout), + extra: v.extra.as_ref().map(|e| parse_extra_field_as_json(e)), + children: vec![], + }; + TrashFolderView { + view: folder_view, + deleted_at: DateTime::from_timestamp(section_item.timestamp, 0).unwrap_or_default(), + } }) }) .collect() diff --git a/src/biz/collab/ops.rs b/src/biz/collab/ops.rs index 0904f8d7..efc9abbe 100644 --- a/src/biz/collab/ops.rs +++ b/src/biz/collab/ops.rs @@ -10,6 +10,9 @@ use database::collab::{CollabStorage, GetCollabOrigin}; use database::publish::select_published_view_ids_for_workspace; use database::publish::select_workspace_id_for_publish_namespace; use database_entity::dto::{QueryCollab, QueryCollabParams}; +use shared_entity::dto::workspace_dto::FavoriteFolderView; +use shared_entity::dto::workspace_dto::RecentFolderView; +use shared_entity::dto::workspace_dto::TrashFolderView; use sqlx::PgPool; use std::ops::DerefMut; @@ -28,7 +31,9 @@ use database_entity::dto::{ }; use super::folder_view::collab_folder_to_folder_view; -use super::folder_view::section_items_to_folder_view; +use super::folder_view::section_items_to_favorite_folder_view; +use super::folder_view::section_items_to_recent_folder_view; +use super::folder_view::section_items_to_trash_folder_view; use super::publish_outline::collab_folder_to_published_outline; /// Create a new collab member @@ -163,7 +168,7 @@ pub async fn get_user_favorite_folder_views( pg_pool: &PgPool, uid: i64, workspace_id: Uuid, -) -> Result, AppError> { +) -> Result, AppError> { let folder = get_latest_collab_folder( collab_storage, GetCollabOrigin::User { uid }, @@ -185,7 +190,7 @@ pub async fn get_user_favorite_folder_views( .into_iter() .filter(|s| !deleted_section_item_ids.contains(&s.id)) .collect(); - Ok(section_items_to_folder_view( + Ok(section_items_to_favorite_folder_view( &favorite_section_items, &folder, &publish_view_ids, @@ -197,7 +202,7 @@ pub async fn get_user_recent_folder_views( pg_pool: &PgPool, uid: i64, workspace_id: Uuid, -) -> Result, AppError> { +) -> Result, AppError> { let folder = get_latest_collab_folder( collab_storage, GetCollabOrigin::User { uid }, @@ -219,7 +224,7 @@ pub async fn get_user_recent_folder_views( .into_iter() .map(|id| id.to_string()) .collect(); - Ok(section_items_to_folder_view( + Ok(section_items_to_recent_folder_view( &recent_section_items, &folder, &publish_view_ids, @@ -230,7 +235,7 @@ pub async fn get_user_trash_folder_views( collab_storage: &CollabAccessControlStorage, uid: i64, workspace_id: Uuid, -) -> Result, AppError> { +) -> Result, AppError> { let folder = get_latest_collab_folder( collab_storage, GetCollabOrigin::User { uid }, @@ -238,11 +243,7 @@ pub async fn get_user_trash_folder_views( ) .await?; let section_items = folder.get_my_trash_sections(); - Ok(section_items_to_folder_view( - §ion_items, - &folder, - &HashSet::default(), - )) + Ok(section_items_to_trash_folder_view(§ion_items, &folder)) } pub async fn get_user_workspace_structure( diff --git a/tests/workspace/workspace_folder.rs b/tests/workspace/workspace_folder.rs index 1c4303a8..e5478b68 100644 --- a/tests/workspace/workspace_folder.rs +++ b/tests/workspace/workspace_folder.rs @@ -85,14 +85,17 @@ async fn get_section_items() { .unwrap(); let favorite_section_items = c.get_workspace_favorite(&workspace_id).await.unwrap(); assert_eq!(favorite_section_items.views.len(), 1); - assert_eq!(favorite_section_items.views[0].view_id, new_favorite_id); + assert_eq!( + favorite_section_items.views[0].view.view_id, + new_favorite_id + ); let trash_section_items = c.get_workspace_trash(&workspace_id).await.unwrap(); assert_eq!(trash_section_items.views.len(), 1); assert_eq!( - trash_section_items.views[0].view_id, + trash_section_items.views[0].view.view_id, to_be_deleted_favorite_id ); let recent_section_items = c.get_workspace_recent(&workspace_id).await.unwrap(); assert_eq!(recent_section_items.views.len(), 1); - assert_eq!(recent_section_items.views[0].view_id, recent_id); + assert_eq!(recent_section_items.views[0].view.view_id, recent_id); }