This commit is contained in:
2025-10-13 14:49:37 +02:00
parent 159577dff9
commit 9060905e7a
3 changed files with 79 additions and 56 deletions
+62 -36
View File
@@ -77,6 +77,20 @@ impl From<Tag> for TagResponse {
} }
} }
#[derive(Serialize, Clone, Default)]
pub struct DocumentAssetGroupResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail: Option<DocumentAssetResponse>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preview: Option<DocumentAssetResponse>,
}
impl DocumentAssetGroupResponse {
pub fn is_empty(&self) -> bool {
self.thumbnail.is_none() && self.preview.is_none()
}
}
#[derive(Serialize)] #[derive(Serialize)]
pub struct DocumentResponse { pub struct DocumentResponse {
pub id: Uuid, pub id: Uuid,
@@ -92,8 +106,8 @@ pub struct DocumentResponse {
pub issued_at: Option<String>, pub issued_at: Option<String>,
pub metadata: Value, pub metadata: Value,
pub tags: Vec<TagResponse>, pub tags: Vec<TagResponse>,
pub thumbnail: Option<DocumentAssetResponse>, #[serde(default, skip_serializing_if = "DocumentAssetGroupResponse::is_empty")]
pub preview: Option<DocumentAssetResponse>, pub assets: DocumentAssetGroupResponse,
pub download_path: String, pub download_path: String,
} }
@@ -119,11 +133,7 @@ pub struct DocumentAssetResponse {
pub created_at: String, pub created_at: String,
} }
#[derive(Clone, Default)] type PrimaryDocumentAssets = DocumentAssetGroupResponse;
pub struct PrimaryDocumentAssets {
pub thumbnail: Option<DocumentAssetResponse>,
pub preview: Option<DocumentAssetResponse>,
}
#[derive(Serialize)] #[derive(Serialize)]
pub struct DocumentDetailResponse { pub struct DocumentDetailResponse {
@@ -251,18 +261,13 @@ pub async fn list_documents(
let mut response = Vec::with_capacity(doc_ids.len()); let mut response = Vec::with_capacity(doc_ids.len());
for doc in docs { for doc in docs {
let tags = tags_map.get(&doc.id).cloned(); let tags = tags_map.get(&doc.id).cloned();
let (thumbnail, preview) = if let Some(assets) = primary_assets.get(&doc.id) { let assets = primary_assets.get(&doc.id).cloned();
(assets.thumbnail.clone(), assets.preview.clone())
} else {
(None, None)
};
response.push(to_document_response( response.push(to_document_response(
&state, &state,
user.user_id, user.user_id,
doc, doc,
tags, tags,
thumbnail, assets,
preview,
)?); )?);
} }
@@ -291,14 +296,21 @@ pub async fn get_document(
drop(conn); drop(conn);
let assets = load_asset_responses(&state, version_id).await?; let assets = load_asset_responses(&state, version_id).await?;
let thumbnail = assets let mut grouped_assets = DocumentAssetGroupResponse::default();
if let Some(asset) = assets
.iter() .iter()
.find(|asset| asset.asset_type == "thumbnail") .find(|asset| asset.asset_type == "thumbnail")
.cloned(); .cloned()
let preview = assets {
grouped_assets.thumbnail = Some(asset);
}
if let Some(asset) = assets
.iter() .iter()
.find(|asset| asset.asset_type == "preview") .find(|asset| asset.asset_type == "preview")
.cloned(); .cloned()
{
grouped_assets.preview = Some(asset);
}
Ok(Json(DocumentDetailResponse { Ok(Json(DocumentDetailResponse {
document: to_document_response( document: to_document_response(
@@ -306,8 +318,7 @@ pub async fn get_document(
user.user_id, user.user_id,
doc, doc,
tags_map.get(&document_id).cloned(), tags_map.get(&document_id).cloned(),
thumbnail, Some(grouped_assets),
preview,
)?, )?,
current_version: to_version_response(current_version), current_version: to_version_response(current_version),
assets, assets,
@@ -695,14 +706,21 @@ pub async fn update_document(
drop(conn); drop(conn);
let assets = load_asset_responses(&state, version_id).await?; let assets = load_asset_responses(&state, version_id).await?;
let thumbnail = assets let mut grouped_assets = DocumentAssetGroupResponse::default();
if let Some(asset) = assets
.iter() .iter()
.find(|asset| asset.asset_type == "thumbnail") .find(|asset| asset.asset_type == "thumbnail")
.cloned(); .cloned()
let preview = assets {
grouped_assets.thumbnail = Some(asset);
}
if let Some(asset) = assets
.iter() .iter()
.find(|asset| asset.asset_type == "preview") .find(|asset| asset.asset_type == "preview")
.cloned(); .cloned()
{
grouped_assets.preview = Some(asset);
}
Ok(Json(DocumentDetailResponse { Ok(Json(DocumentDetailResponse {
document: to_document_response( document: to_document_response(
@@ -710,8 +728,7 @@ pub async fn update_document(
user.user_id, user.user_id,
document, document,
tags_map.get(&document_id).cloned(), tags_map.get(&document_id).cloned(),
thumbnail, Some(grouped_assets),
preview,
)?, )?,
current_version: to_version_response(current_version), current_version: to_version_response(current_version),
assets, assets,
@@ -991,14 +1008,21 @@ async fn process_upload(
let tags = tags_map.get(&document.id).cloned(); let tags = tags_map.get(&document.id).cloned();
drop(conn); drop(conn);
let assets = load_asset_responses(state, version.id).await?; let assets = load_asset_responses(state, version.id).await?;
let thumbnail = assets let mut grouped_assets = DocumentAssetGroupResponse::default();
if let Some(asset) = assets
.iter() .iter()
.find(|asset| asset.asset_type == "thumbnail") .find(|asset| asset.asset_type == "thumbnail")
.cloned(); .cloned()
let preview = assets {
grouped_assets.thumbnail = Some(asset);
}
if let Some(asset) = assets
.iter() .iter()
.find(|asset| asset.asset_type == "preview") .find(|asset| asset.asset_type == "preview")
.cloned(); .cloned()
{
grouped_assets.preview = Some(asset);
}
info!( info!(
document_id = %document.id, document_id = %document.id,
@@ -1009,7 +1033,11 @@ async fn process_upload(
return Ok(UploadOutcome { return Ok(UploadOutcome {
detail: DocumentDetailResponse { detail: DocumentDetailResponse {
document: to_document_response( document: to_document_response(
state, user_id, document, tags, thumbnail, preview, state,
user_id,
document,
tags,
Some(grouped_assets),
)?, )?,
current_version: to_version_response(version), current_version: to_version_response(version),
assets, assets,
@@ -1081,7 +1109,7 @@ async fn process_upload(
}; };
let detail = DocumentDetailResponse { let detail = DocumentDetailResponse {
document: to_document_response(state, user_id, document, None, None, None)?, document: to_document_response(state, user_id, document, None, None)?,
current_version: to_version_response(version.clone()), current_version: to_version_response(version.clone()),
assets: Vec::new(), assets: Vec::new(),
}; };
@@ -1255,8 +1283,7 @@ pub(crate) fn to_document_response(
user_id: Uuid, user_id: Uuid,
doc: Document, doc: Document,
tags: Option<Vec<Tag>>, tags: Option<Vec<Tag>>,
thumbnail: Option<DocumentAssetResponse>, assets: Option<DocumentAssetGroupResponse>,
preview: Option<DocumentAssetResponse>,
) -> AppResult<DocumentResponse> { ) -> AppResult<DocumentResponse> {
let download_path = build_download_path(state, doc.id, user_id)?; let download_path = build_download_path(state, doc.id, user_id)?;
@@ -1278,8 +1305,7 @@ pub(crate) fn to_document_response(
.into_iter() .into_iter()
.map(TagResponse::from) .map(TagResponse::from)
.collect(), .collect(),
thumbnail, assets: assets.unwrap_or_default(),
preview,
download_path, download_path,
}) })
} }
+4 -14
View File
@@ -220,18 +220,13 @@ pub async fn list_folder_contents(
let mut documents = Vec::with_capacity(doc_ids.len()); let mut documents = Vec::with_capacity(doc_ids.len());
for doc in docs { for doc in docs {
let tags = tags_map.get(&doc.id).cloned(); let tags = tags_map.get(&doc.id).cloned();
let (thumbnail, preview) = if let Some(assets) = primary_assets.get(&doc.id) { let assets = primary_assets.get(&doc.id).cloned();
(assets.thumbnail.clone(), assets.preview.clone())
} else {
(None, None)
};
documents.push(to_document_response( documents.push(to_document_response(
&state, &state,
user.user_id, user.user_id,
doc, doc,
tags, tags,
thumbnail, assets,
preview,
)?); )?);
} }
@@ -408,18 +403,13 @@ pub async fn search_documents(
let mut response = Vec::with_capacity(doc_ids.len()); let mut response = Vec::with_capacity(doc_ids.len());
for doc in docs { for doc in docs {
let tags = tags_map.get(&doc.id).cloned(); let tags = tags_map.get(&doc.id).cloned();
let (thumbnail, preview) = if let Some(assets) = primary_assets.get(&doc.id) { let assets = primary_assets.get(&doc.id).cloned();
(assets.thumbnail.clone(), assets.preview.clone())
} else {
(None, None)
};
response.push(to_document_response( response.push(to_document_response(
&state, &state,
user.user_id, user.user_id,
doc, doc,
tags, tags,
thumbnail, assets,
preview,
)?); )?);
} }
+13 -6
View File
@@ -731,6 +731,8 @@ const DocumentsTable = ({
rowClasses.push('dragging'); rowClasses.push('dragging');
} }
const thumbnailAsset = doc?.assets?.thumbnail || null;
return ( return (
<tr <tr
key={doc.id} key={doc.id}
@@ -753,9 +755,9 @@ const DocumentsTable = ({
onDragEnd={onDocumentDragEnd} onDragEnd={onDocumentDragEnd}
> >
<td className="thumb-cell"> <td className="thumb-cell">
{doc.thumbnail ? ( {thumbnailAsset ? (
<img <img
src={doc.thumbnail.url} src={thumbnailAsset.url}
alt={`Thumbnail for ${doc.title || doc.original_name}`} alt={`Thumbnail for ${doc.title || doc.original_name}`}
className="document-thumbnail" className="document-thumbnail"
/> />
@@ -1029,11 +1031,16 @@ const DetailPanel = ({
const makePreviewItem = useCallback((doc, detailEntry, fallbackUrl = null) => { const makePreviewItem = useCallback((doc, detailEntry, fallbackUrl = null) => {
if (!doc) return null; if (!doc) return null;
const url = doc.thumbnail?.url || detailEntry?.document?.thumbnail?.url || fallbackUrl; const detailDocument = detailEntry?.document || null;
const thumbnailAsset =
doc?.assets?.thumbnail ||
detailDocument?.assets?.thumbnail ||
detailEntry?.assets?.find((item) => item.asset_type === 'thumbnail') ||
null;
const url = thumbnailAsset?.url || fallbackUrl;
if (!url) return null; if (!url) return null;
const asset = detailEntry?.assets?.find((item) => item.asset_type === 'thumbnail'); const width = thumbnailAsset?.width || 0;
const width = asset?.width || 0; const height = thumbnailAsset?.height || 0;
const height = asset?.height || 0;
const orientation = width > 0 && height > 0 ? (width >= height ? 'landscape' : 'portrait') : 'landscape'; const orientation = width > 0 && height > 0 ? (width >= height ? 'landscape' : 'portrait') : 'landscape';
return { return {
id: doc.id, id: doc.id,