Fix prop renders
* Incorporate prop scaling * Props now render to a canvas
This commit is contained in:
parent
af89394df1
commit
a2841c413d
21 changed files with 942 additions and 353 deletions
|
|
@ -9,6 +9,8 @@ pub mod config;
|
|||
#[cfg(feature = "ssr")]
|
||||
pub mod dashboard;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub mod loose_props;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub mod props;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub mod realms;
|
||||
|
|
|
|||
75
crates/chattyness-admin-ui/src/api/loose_props.rs
Normal file
75
crates/chattyness-admin-ui/src/api/loose_props.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
//! Loose props management API handlers for admin UI.
|
||||
|
||||
use axum::Json;
|
||||
use axum::extract::Path;
|
||||
use chattyness_db::{models::LooseProp, queries::loose_props};
|
||||
use chattyness_error::AppError;
|
||||
use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::auth::AdminConn;
|
||||
|
||||
// =============================================================================
|
||||
// API Types
|
||||
// =============================================================================
|
||||
|
||||
/// Request to update loose prop scale.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UpdateLoosePropScaleRequest {
|
||||
/// Scale factor (0.1 - 10.0).
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// API Handlers
|
||||
// =============================================================================
|
||||
|
||||
/// Get a loose prop by ID.
|
||||
pub async fn get_loose_prop(
|
||||
admin_conn: AdminConn,
|
||||
Path(loose_prop_id): Path<Uuid>,
|
||||
) -> Result<Json<LooseProp>, AppError> {
|
||||
let conn = admin_conn.0;
|
||||
let mut guard = conn.acquire().await;
|
||||
|
||||
let prop = loose_props::get_loose_prop_by_id(&mut *guard, loose_prop_id)
|
||||
.await?
|
||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
||||
|
||||
Ok(Json(prop))
|
||||
}
|
||||
|
||||
/// Update loose prop scale.
|
||||
///
|
||||
/// Server admins can update any loose prop scale.
|
||||
pub async fn update_loose_prop_scale(
|
||||
admin_conn: AdminConn,
|
||||
Path(loose_prop_id): Path<Uuid>,
|
||||
Json(req): Json<UpdateLoosePropScaleRequest>,
|
||||
) -> Result<Json<LooseProp>, AppError> {
|
||||
let conn = admin_conn.0;
|
||||
let mut guard = conn.acquire().await;
|
||||
|
||||
let prop = loose_props::update_loose_prop_scale(&mut *guard, loose_prop_id, req.scale).await?;
|
||||
|
||||
tracing::info!(
|
||||
"Updated loose prop {} scale to {}",
|
||||
loose_prop_id,
|
||||
req.scale
|
||||
);
|
||||
|
||||
Ok(Json(prop))
|
||||
}
|
||||
|
||||
/// List loose props in a scene/channel.
|
||||
pub async fn list_loose_props(
|
||||
admin_conn: AdminConn,
|
||||
Path(scene_id): Path<Uuid>,
|
||||
) -> Result<Json<Vec<LooseProp>>, AppError> {
|
||||
let conn = admin_conn.0;
|
||||
let mut guard = conn.acquire().await;
|
||||
|
||||
let props = loose_props::list_channel_loose_props(&mut *guard, scene_id).await?;
|
||||
|
||||
Ok(Json(props))
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ use axum::{
|
|||
routing::{delete, get, post, put},
|
||||
};
|
||||
|
||||
use super::{auth, avatars, config, dashboard, props, realms, scenes, spots, staff, users};
|
||||
use super::{auth, avatars, config, dashboard, loose_props, props, realms, scenes, spots, staff, users};
|
||||
use crate::app::AdminAppState;
|
||||
|
||||
/// Create the admin API router.
|
||||
|
|
@ -85,6 +85,19 @@ pub fn admin_api_router() -> Router<AdminAppState> {
|
|||
"/props/{prop_id}",
|
||||
get(props::get_prop).delete(props::delete_prop),
|
||||
)
|
||||
// API - Loose Props (scene props)
|
||||
.route(
|
||||
"/scenes/{scene_id}/loose_props",
|
||||
get(loose_props::list_loose_props),
|
||||
)
|
||||
.route(
|
||||
"/loose_props/{loose_prop_id}",
|
||||
get(loose_props::get_loose_prop),
|
||||
)
|
||||
.route(
|
||||
"/loose_props/{loose_prop_id}/scale",
|
||||
put(loose_props::update_loose_prop_scale),
|
||||
)
|
||||
// API - Server Avatars
|
||||
.route(
|
||||
"/avatars",
|
||||
|
|
|
|||
|
|
@ -239,6 +239,8 @@ pub struct PropDetail {
|
|||
pub default_layer: Option<String>,
|
||||
/// Grid position (0-8): top row 0,1,2 / middle 3,4,5 / bottom 6,7,8
|
||||
pub default_position: Option<i16>,
|
||||
/// Default scale factor (0.1 - 10.0) applied when prop is dropped to canvas.
|
||||
pub default_scale: f32,
|
||||
pub is_unique: bool,
|
||||
pub is_transferable: bool,
|
||||
pub is_portable: bool,
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ fn PropDetailView(prop: PropDetail) -> impl IntoView {
|
|||
None => "Not set".to_string(),
|
||||
}}
|
||||
</DetailItem>
|
||||
<DetailItem label="Default Scale">
|
||||
{format!("{}%", (prop.default_scale * 100.0) as i32)}
|
||||
</DetailItem>
|
||||
<DetailItem label="Status">
|
||||
{if prop.is_active {
|
||||
view! { <span class="status-badge status-active">"Active"</span> }.into_any()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue