Merge pull request #926 from AppFlowy-IO/favorite-recent-views

Add section timestamp for get recent/favorite/trash section endpoints
This commit is contained in:
Khor Shu Heng 2024-10-24 16:13:53 +08:00 committed by GitHub
commit dd23612d49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 156 additions and 50 deletions

View File

@ -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<SectionItems, AppResponseError> {
) -> Result<FavoriteSectionItems, AppResponseError> {
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::<SectionItems>::from_response(resp)
AppResponse::<FavoriteSectionItems>::from_response(resp)
.await?
.into_data()
}
@ -744,7 +744,7 @@ impl Client {
pub async fn get_workspace_recent(
&self,
workspace_id: &str,
) -> Result<SectionItems, AppResponseError> {
) -> Result<RecentSectionItems, AppResponseError> {
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::<SectionItems>::from_response(resp)
AppResponse::<RecentSectionItems>::from_response(resp)
.await?
.into_data()
}
@ -761,7 +761,7 @@ impl Client {
pub async fn get_workspace_trash(
&self,
workspace_id: &str,
) -> Result<SectionItems, AppResponseError> {
) -> Result<TrashSectionItems, AppResponseError> {
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::<SectionItems>::from_response(resp)
AppResponse::<TrashSectionItems>::from_response(resp)
.await?
.into_data()
}

View File

@ -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<Utc>,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct FavoriteFolderView {
#[serde(flatten)]
pub view: FolderView,
pub favorited_at: DateTime<Utc>,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct TrashFolderView {
#[serde(flatten)]
pub view: FolderView,
pub deleted_at: DateTime<Utc>,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RecentSectionItems {
pub views: Vec<RecentFolderView>,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct FavoriteSectionItems {
pub views: Vec<FavoriteFolderView>,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct TrashSectionItems {
pub views: Vec<TrashFolderView>,
}
#[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<FolderView>,
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum IconType {

View File

@ -1575,7 +1575,7 @@ async fn get_recent_views_handler(
user_uuid: UserUuid,
workspace_id: web::Path<Uuid>,
state: Data<AppState>,
) -> Result<Json<AppResponse<SectionItems>>> {
) -> Result<Json<AppResponse<RecentSectionItems>>> {
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<Uuid>,
state: Data<AppState>,
) -> Result<Json<AppResponse<SectionItems>>> {
) -> Result<Json<AppResponse<FavoriteSectionItems>>> {
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<Uuid>,
state: Data<AppState>,
) -> Result<Json<AppResponse<SectionItems>>> {
) -> Result<Json<AppResponse<TrashSectionItems>>> {
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)))

View File

@ -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<String>,
) -> Vec<FolderView> {
) -> Vec<FavoriteFolderView> {
section_items
.iter()
.filter_map(|section_item| {
let view = folder.get_view(&section_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<String>,
) -> Vec<RecentFolderView> {
section_items
.iter()
.filter_map(|section_item| {
let view = folder.get_view(&section_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<TrashFolderView> {
section_items
.iter()
.filter_map(|section_item| {
let view = folder.get_view(&section_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()

View File

@ -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<Vec<FolderView>, AppError> {
) -> Result<Vec<FavoriteFolderView>, 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<Vec<FolderView>, AppError> {
) -> Result<Vec<RecentFolderView>, 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<Vec<FolderView>, AppError> {
) -> Result<Vec<TrashFolderView>, 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(
&section_items,
&folder,
&HashSet::default(),
))
Ok(section_items_to_trash_folder_view(&section_items, &folder))
}
pub async fn get_user_workspace_structure(

View File

@ -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);
}