feat: add publish info to publish outline items (#1156)
This commit is contained in:
parent
505b4ca8fc
commit
a5d94a09d6
|
|
@ -46,6 +46,7 @@ jobs:
|
||||||
- name: Build Docker Images
|
- name: Build Docker Images
|
||||||
run: |
|
run: |
|
||||||
export DOCKER_DEFAULT_PLATFORM=linux/amd64
|
export DOCKER_DEFAULT_PLATFORM=linux/amd64
|
||||||
|
cp deploy.env .env
|
||||||
docker compose build appflowy_cloud appflowy_worker admin_frontend
|
docker compose build appflowy_cloud appflowy_worker admin_frontend
|
||||||
|
|
||||||
- name: Push docker images to docker hub
|
- name: Push docker images to docker hub
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT\n apc.view_id,\n apc.publish_name,\n au.email AS publisher_email,\n apc.created_at AS publish_timestamp\n FROM af_published_collab apc\n JOIN af_user au ON apc.published_by = au.uid\n WHERE workspace_id = $1\n AND unpublished_at IS NULL\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "view_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "publish_name",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "publisher_email",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "publish_timestamp",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "4787139d2189fc33ac25ac07bb9f585f578bde4cd3f75a7da95aa849a25bca9d"
|
||||||
|
}
|
||||||
|
|
@ -686,3 +686,10 @@ impl From<AFQuickNoteRow> for QuickNote {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AFPublishViewWithPublishInfo {
|
||||||
|
pub view_id: Uuid,
|
||||||
|
pub publish_name: String,
|
||||||
|
pub publisher_email: String,
|
||||||
|
pub publish_timestamp: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ use database_entity::dto::{
|
||||||
use sqlx::{Executor, PgPool, Postgres};
|
use sqlx::{Executor, PgPool, Postgres};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::pg_row::AFPublishViewWithPublishInfo;
|
||||||
|
|
||||||
pub async fn select_user_is_collab_publisher_for_all_views(
|
pub async fn select_user_is_collab_publisher_for_all_views(
|
||||||
pg_pool: &PgPool,
|
pg_pool: &PgPool,
|
||||||
user_uuid: &Uuid,
|
user_uuid: &Uuid,
|
||||||
|
|
@ -678,3 +680,31 @@ pub async fn select_published_view_ids_for_workspace<'a, E: Executor<'a, Databas
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn select_published_view_ids_with_publish_info_for_workspace<
|
||||||
|
'a,
|
||||||
|
E: Executor<'a, Database = Postgres>,
|
||||||
|
>(
|
||||||
|
executor: E,
|
||||||
|
workspace_id: Uuid,
|
||||||
|
) -> Result<Vec<AFPublishViewWithPublishInfo>, AppError> {
|
||||||
|
let res = sqlx::query_as!(
|
||||||
|
AFPublishViewWithPublishInfo,
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
apc.view_id,
|
||||||
|
apc.publish_name,
|
||||||
|
au.email AS publisher_email,
|
||||||
|
apc.created_at AS publish_timestamp
|
||||||
|
FROM af_published_collab apc
|
||||||
|
JOIN af_user au ON apc.published_by = au.uid
|
||||||
|
WHERE workspace_id = $1
|
||||||
|
AND unpublished_at IS NULL
|
||||||
|
"#,
|
||||||
|
workspace_id,
|
||||||
|
)
|
||||||
|
.fetch_all(executor)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -389,11 +389,20 @@ pub struct PublishedView {
|
||||||
pub icon: Option<ViewIcon>,
|
pub icon: Option<ViewIcon>,
|
||||||
pub layout: ViewLayout,
|
pub layout: ViewLayout,
|
||||||
pub is_published: bool,
|
pub is_published: bool,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub info: Option<PublishedViewInfo>,
|
||||||
/// contains fields like `is_space`, and font information
|
/// contains fields like `is_space`, and font information
|
||||||
pub extra: Option<serde_json::Value>,
|
pub extra: Option<serde_json::Value>,
|
||||||
pub children: Vec<PublishedView>,
|
pub children: Vec<PublishedView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PublishedViewInfo {
|
||||||
|
pub publisher_email: String,
|
||||||
|
pub publish_name: String,
|
||||||
|
pub publish_timestamp: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct AFDatabase {
|
pub struct AFDatabase {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ use database::collab::select_last_updated_database_row_ids;
|
||||||
use database::collab::select_workspace_database_oid;
|
use database::collab::select_workspace_database_oid;
|
||||||
use database::collab::{CollabStorage, GetCollabOrigin};
|
use database::collab::{CollabStorage, GetCollabOrigin};
|
||||||
use database::publish::select_published_view_ids_for_workspace;
|
use database::publish::select_published_view_ids_for_workspace;
|
||||||
|
use database::publish::select_published_view_ids_with_publish_info_for_workspace;
|
||||||
use database::publish::select_workspace_id_for_publish_namespace;
|
use database::publish::select_workspace_id_for_publish_namespace;
|
||||||
use database_entity::dto::QueryCollab;
|
use database_entity::dto::QueryCollab;
|
||||||
use database_entity::dto::QueryCollabResult;
|
use database_entity::dto::QueryCollabResult;
|
||||||
|
|
@ -46,6 +47,7 @@ use shared_entity::dto::workspace_dto::AFInsertDatabaseField;
|
||||||
use shared_entity::dto::workspace_dto::DatabaseRowUpdatedItem;
|
use shared_entity::dto::workspace_dto::DatabaseRowUpdatedItem;
|
||||||
use shared_entity::dto::workspace_dto::FavoriteFolderView;
|
use shared_entity::dto::workspace_dto::FavoriteFolderView;
|
||||||
use shared_entity::dto::workspace_dto::FolderViewMinimal;
|
use shared_entity::dto::workspace_dto::FolderViewMinimal;
|
||||||
|
use shared_entity::dto::workspace_dto::PublishedViewInfo;
|
||||||
use shared_entity::dto::workspace_dto::RecentFolderView;
|
use shared_entity::dto::workspace_dto::RecentFolderView;
|
||||||
use shared_entity::dto::workspace_dto::TrashFolderView;
|
use shared_entity::dto::workspace_dto::TrashFolderView;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
@ -457,13 +459,28 @@ pub async fn get_published_view(
|
||||||
&workspace_id.to_string(),
|
&workspace_id.to_string(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let publish_view_ids = select_published_view_ids_for_workspace(pg_pool, workspace_id).await?;
|
let publish_view_ids_with_publish_info =
|
||||||
let publish_view_ids: HashSet<String> = publish_view_ids
|
select_published_view_ids_with_publish_info_for_workspace(pg_pool, workspace_id).await?;
|
||||||
.into_iter()
|
let publish_view_id_to_info_map: HashMap<String, PublishedViewInfo> =
|
||||||
.map(|id| id.to_string())
|
publish_view_ids_with_publish_info
|
||||||
.collect();
|
.into_iter()
|
||||||
let published_view: PublishedView =
|
.map(|pv| {
|
||||||
collab_folder_to_published_outline(&workspace_id.to_string(), &folder, &publish_view_ids)?;
|
(
|
||||||
|
pv.view_id.to_string(),
|
||||||
|
PublishedViewInfo {
|
||||||
|
publisher_email: pv.publisher_email.clone(),
|
||||||
|
publish_name: pv.publish_name.clone(),
|
||||||
|
publish_timestamp: pv.publish_timestamp,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let published_view: PublishedView = collab_folder_to_published_outline(
|
||||||
|
&workspace_id.to_string(),
|
||||||
|
&folder,
|
||||||
|
&publish_view_id_to_info_map,
|
||||||
|
)?;
|
||||||
Ok(published_view)
|
Ok(published_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use app_error::AppError;
|
use app_error::AppError;
|
||||||
use collab_folder::Folder;
|
use collab_folder::Folder;
|
||||||
use shared_entity::dto::workspace_dto::PublishedView;
|
use shared_entity::dto::workspace_dto::{PublishedView, PublishedViewInfo};
|
||||||
|
|
||||||
use super::folder_view::{to_dto_view_icon, to_dto_view_layout};
|
use super::folder_view::{to_dto_view_icon, to_dto_view_layout};
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ use super::folder_view::{to_dto_view_icon, to_dto_view_layout};
|
||||||
pub fn collab_folder_to_published_outline(
|
pub fn collab_folder_to_published_outline(
|
||||||
root_view_id: &str,
|
root_view_id: &str,
|
||||||
folder: &Folder,
|
folder: &Folder,
|
||||||
publish_view_ids: &HashSet<String>,
|
publish_view_id_to_info_map: &HashMap<String, PublishedViewInfo>,
|
||||||
) -> Result<PublishedView, AppError> {
|
) -> Result<PublishedView, AppError> {
|
||||||
let mut unviewable = HashSet::new();
|
let mut unviewable = HashSet::new();
|
||||||
for trash_view in folder.get_all_trash_sections() {
|
for trash_view in folder.get_all_trash_sections() {
|
||||||
|
|
@ -24,7 +24,7 @@ pub fn collab_folder_to_published_outline(
|
||||||
root_view_id,
|
root_view_id,
|
||||||
folder,
|
folder,
|
||||||
&unviewable,
|
&unviewable,
|
||||||
publish_view_ids,
|
publish_view_id_to_info_map,
|
||||||
0,
|
0,
|
||||||
max_depth,
|
max_depth,
|
||||||
)
|
)
|
||||||
|
|
@ -39,7 +39,7 @@ fn to_publish_view(
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
folder: &Folder,
|
folder: &Folder,
|
||||||
unviewable: &HashSet<String>,
|
unviewable: &HashSet<String>,
|
||||||
publish_view_ids: &HashSet<String>,
|
publish_view_id_to_info_map: &HashMap<String, PublishedViewInfo>,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
) -> Option<PublishedView> {
|
) -> Option<PublishedView> {
|
||||||
|
|
@ -65,6 +65,8 @@ fn to_publish_view(
|
||||||
serde_json::Value::Null
|
serde_json::Value::Null
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
// If pruned_view is not empty, then one or more of the children is published.
|
||||||
|
// Hence, this view should be included in the published outline, even if it is not published itself.
|
||||||
let pruned_view: Vec<PublishedView> = view
|
let pruned_view: Vec<PublishedView> = view
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -74,13 +76,13 @@ fn to_publish_view(
|
||||||
&child_view_id.id,
|
&child_view_id.id,
|
||||||
folder,
|
folder,
|
||||||
unviewable,
|
unviewable,
|
||||||
publish_view_ids,
|
publish_view_id_to_info_map,
|
||||||
depth + 1,
|
depth + 1,
|
||||||
max_depth,
|
max_depth,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let is_published = publish_view_ids.contains(view_id);
|
let is_published = publish_view_id_to_info_map.contains_key(view_id);
|
||||||
if parent_view_id.is_empty() || is_published || !pruned_view.is_empty() {
|
if parent_view_id.is_empty() || is_published || !pruned_view.is_empty() {
|
||||||
Some(PublishedView {
|
Some(PublishedView {
|
||||||
view_id: view.id.clone(),
|
view_id: view.id.clone(),
|
||||||
|
|
@ -93,6 +95,7 @@ fn to_publish_view(
|
||||||
layout: to_dto_view_layout(&view.layout),
|
layout: to_dto_view_layout(&view.layout),
|
||||||
extra,
|
extra,
|
||||||
children: pruned_view,
|
children: pruned_view,
|
||||||
|
info: publish_view_id_to_info_map.get(view_id).cloned(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue