fix: rendering of avatar thumbnails

This commit is contained in:
Evan Carroll 2026-01-22 23:48:13 -06:00
parent a2a0fe5510
commit 23630b19b2
8 changed files with 931 additions and 24 deletions

View file

@ -72,6 +72,192 @@ pub async fn list_public_realm_avatars<'e>(
Ok(avatars)
}
use crate::models::RealmAvatarWithPaths;
/// Row type for realm avatar with paths query.
#[derive(Debug, sqlx::FromRow)]
struct RealmAvatarWithPathsRow {
id: Uuid,
name: String,
description: Option<String>,
// Skin layer paths
skin_0: Option<String>,
skin_1: Option<String>,
skin_2: Option<String>,
skin_3: Option<String>,
skin_4: Option<String>,
skin_5: Option<String>,
skin_6: Option<String>,
skin_7: Option<String>,
skin_8: Option<String>,
// Clothes layer paths
clothes_0: Option<String>,
clothes_1: Option<String>,
clothes_2: Option<String>,
clothes_3: Option<String>,
clothes_4: Option<String>,
clothes_5: Option<String>,
clothes_6: Option<String>,
clothes_7: Option<String>,
clothes_8: Option<String>,
// Accessories layer paths
accessories_0: Option<String>,
accessories_1: Option<String>,
accessories_2: Option<String>,
accessories_3: Option<String>,
accessories_4: Option<String>,
accessories_5: Option<String>,
accessories_6: Option<String>,
accessories_7: Option<String>,
accessories_8: Option<String>,
// Happy emotion layer paths (e1 - more inviting for store display)
emotion_0: Option<String>,
emotion_1: Option<String>,
emotion_2: Option<String>,
emotion_3: Option<String>,
emotion_4: Option<String>,
emotion_5: Option<String>,
emotion_6: Option<String>,
emotion_7: Option<String>,
emotion_8: Option<String>,
}
impl From<RealmAvatarWithPathsRow> for RealmAvatarWithPaths {
fn from(row: RealmAvatarWithPathsRow) -> Self {
Self {
id: row.id,
name: row.name,
description: row.description,
skin_layer: [
row.skin_0, row.skin_1, row.skin_2,
row.skin_3, row.skin_4, row.skin_5,
row.skin_6, row.skin_7, row.skin_8,
],
clothes_layer: [
row.clothes_0, row.clothes_1, row.clothes_2,
row.clothes_3, row.clothes_4, row.clothes_5,
row.clothes_6, row.clothes_7, row.clothes_8,
],
accessories_layer: [
row.accessories_0, row.accessories_1, row.accessories_2,
row.accessories_3, row.accessories_4, row.accessories_5,
row.accessories_6, row.accessories_7, row.accessories_8,
],
emotion_layer: [
row.emotion_0, row.emotion_1, row.emotion_2,
row.emotion_3, row.emotion_4, row.emotion_5,
row.emotion_6, row.emotion_7, row.emotion_8,
],
}
}
}
/// List all active public realm avatars with resolved asset paths.
///
/// Joins with the props table to resolve prop UUIDs to asset paths,
/// suitable for client-side rendering without additional lookups.
pub async fn list_public_realm_avatars_with_paths<'e>(
executor: impl PgExecutor<'e>,
realm_id: Uuid,
) -> Result<Vec<RealmAvatarWithPaths>, AppError> {
let rows = sqlx::query_as::<_, RealmAvatarWithPathsRow>(
r#"
SELECT
a.id,
a.name,
a.description,
-- Skin layer
p_skin_0.asset_path AS skin_0,
p_skin_1.asset_path AS skin_1,
p_skin_2.asset_path AS skin_2,
p_skin_3.asset_path AS skin_3,
p_skin_4.asset_path AS skin_4,
p_skin_5.asset_path AS skin_5,
p_skin_6.asset_path AS skin_6,
p_skin_7.asset_path AS skin_7,
p_skin_8.asset_path AS skin_8,
-- Clothes layer
p_clothes_0.asset_path AS clothes_0,
p_clothes_1.asset_path AS clothes_1,
p_clothes_2.asset_path AS clothes_2,
p_clothes_3.asset_path AS clothes_3,
p_clothes_4.asset_path AS clothes_4,
p_clothes_5.asset_path AS clothes_5,
p_clothes_6.asset_path AS clothes_6,
p_clothes_7.asset_path AS clothes_7,
p_clothes_8.asset_path AS clothes_8,
-- Accessories layer
p_acc_0.asset_path AS accessories_0,
p_acc_1.asset_path AS accessories_1,
p_acc_2.asset_path AS accessories_2,
p_acc_3.asset_path AS accessories_3,
p_acc_4.asset_path AS accessories_4,
p_acc_5.asset_path AS accessories_5,
p_acc_6.asset_path AS accessories_6,
p_acc_7.asset_path AS accessories_7,
p_acc_8.asset_path AS accessories_8,
-- Happy emotion layer (e1 - more inviting for store display)
p_emo_0.asset_path AS emotion_0,
p_emo_1.asset_path AS emotion_1,
p_emo_2.asset_path AS emotion_2,
p_emo_3.asset_path AS emotion_3,
p_emo_4.asset_path AS emotion_4,
p_emo_5.asset_path AS emotion_5,
p_emo_6.asset_path AS emotion_6,
p_emo_7.asset_path AS emotion_7,
p_emo_8.asset_path AS emotion_8
FROM realm.avatars a
-- Skin layer joins
LEFT JOIN realm.props p_skin_0 ON a.l_skin_0 = p_skin_0.id
LEFT JOIN realm.props p_skin_1 ON a.l_skin_1 = p_skin_1.id
LEFT JOIN realm.props p_skin_2 ON a.l_skin_2 = p_skin_2.id
LEFT JOIN realm.props p_skin_3 ON a.l_skin_3 = p_skin_3.id
LEFT JOIN realm.props p_skin_4 ON a.l_skin_4 = p_skin_4.id
LEFT JOIN realm.props p_skin_5 ON a.l_skin_5 = p_skin_5.id
LEFT JOIN realm.props p_skin_6 ON a.l_skin_6 = p_skin_6.id
LEFT JOIN realm.props p_skin_7 ON a.l_skin_7 = p_skin_7.id
LEFT JOIN realm.props p_skin_8 ON a.l_skin_8 = p_skin_8.id
-- Clothes layer joins
LEFT JOIN realm.props p_clothes_0 ON a.l_clothes_0 = p_clothes_0.id
LEFT JOIN realm.props p_clothes_1 ON a.l_clothes_1 = p_clothes_1.id
LEFT JOIN realm.props p_clothes_2 ON a.l_clothes_2 = p_clothes_2.id
LEFT JOIN realm.props p_clothes_3 ON a.l_clothes_3 = p_clothes_3.id
LEFT JOIN realm.props p_clothes_4 ON a.l_clothes_4 = p_clothes_4.id
LEFT JOIN realm.props p_clothes_5 ON a.l_clothes_5 = p_clothes_5.id
LEFT JOIN realm.props p_clothes_6 ON a.l_clothes_6 = p_clothes_6.id
LEFT JOIN realm.props p_clothes_7 ON a.l_clothes_7 = p_clothes_7.id
LEFT JOIN realm.props p_clothes_8 ON a.l_clothes_8 = p_clothes_8.id
-- Accessories layer joins
LEFT JOIN realm.props p_acc_0 ON a.l_accessories_0 = p_acc_0.id
LEFT JOIN realm.props p_acc_1 ON a.l_accessories_1 = p_acc_1.id
LEFT JOIN realm.props p_acc_2 ON a.l_accessories_2 = p_acc_2.id
LEFT JOIN realm.props p_acc_3 ON a.l_accessories_3 = p_acc_3.id
LEFT JOIN realm.props p_acc_4 ON a.l_accessories_4 = p_acc_4.id
LEFT JOIN realm.props p_acc_5 ON a.l_accessories_5 = p_acc_5.id
LEFT JOIN realm.props p_acc_6 ON a.l_accessories_6 = p_acc_6.id
LEFT JOIN realm.props p_acc_7 ON a.l_accessories_7 = p_acc_7.id
LEFT JOIN realm.props p_acc_8 ON a.l_accessories_8 = p_acc_8.id
-- Happy emotion layer joins (e1 - more inviting for store display)
LEFT JOIN realm.props p_emo_0 ON a.e_happy_0 = p_emo_0.id
LEFT JOIN realm.props p_emo_1 ON a.e_happy_1 = p_emo_1.id
LEFT JOIN realm.props p_emo_2 ON a.e_happy_2 = p_emo_2.id
LEFT JOIN realm.props p_emo_3 ON a.e_happy_3 = p_emo_3.id
LEFT JOIN realm.props p_emo_4 ON a.e_happy_4 = p_emo_4.id
LEFT JOIN realm.props p_emo_5 ON a.e_happy_5 = p_emo_5.id
LEFT JOIN realm.props p_emo_6 ON a.e_happy_6 = p_emo_6.id
LEFT JOIN realm.props p_emo_7 ON a.e_happy_7 = p_emo_7.id
LEFT JOIN realm.props p_emo_8 ON a.e_happy_8 = p_emo_8.id
WHERE a.realm_id = $1 AND a.is_active = true AND a.is_public = true
ORDER BY a.name ASC
"#,
)
.bind(realm_id)
.fetch_all(executor)
.await?;
Ok(rows.into_iter().map(RealmAvatarWithPaths::from).collect())
}
/// Row type for prop asset lookup.
#[derive(Debug, sqlx::FromRow)]
struct PropAssetRow {