pinakes-ui: supply local_state to Conditional and Progress; remove last_refresh
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ib513b5846d6c74bfe821da195b7080af6a6a6964
This commit is contained in:
parent
220dfa6506
commit
63954fdb2f
2 changed files with 62 additions and 20 deletions
|
|
@ -41,15 +41,13 @@ pub struct PluginPage {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct PluginRegistry {
|
||||
/// API client for fetching pages from server
|
||||
client: ApiClient,
|
||||
client: ApiClient,
|
||||
/// Cached pages: (`plugin_id`, `page_id`) -> `PluginPage`
|
||||
pages: HashMap<(String, String), PluginPage>,
|
||||
pages: HashMap<(String, String), PluginPage>,
|
||||
/// Cached widgets: (`plugin_id`, `widget_id`) -> `UiWidget`
|
||||
widgets: Vec<(String, UiWidget)>,
|
||||
widgets: Vec<(String, UiWidget)>,
|
||||
/// Merged CSS custom property overrides from all enabled plugins
|
||||
theme_vars: HashMap<String, String>,
|
||||
/// Last refresh timestamp
|
||||
last_refresh: Option<chrono::DateTime<chrono::Utc>>,
|
||||
theme_vars: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl PluginRegistry {
|
||||
|
|
@ -60,7 +58,6 @@ impl PluginRegistry {
|
|||
pages: HashMap::new(),
|
||||
widgets: Vec::new(),
|
||||
theme_vars: HashMap::new(),
|
||||
last_refresh: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -206,14 +203,8 @@ impl PluginRegistry {
|
|||
self.pages = tmp.pages;
|
||||
self.widgets = tmp.widgets;
|
||||
self.theme_vars = tmp.theme_vars;
|
||||
self.last_refresh = Some(chrono::Utc::now());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get last refresh time
|
||||
pub const fn last_refresh(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||
self.last_refresh
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PluginRegistry {
|
||||
|
|
@ -346,7 +337,6 @@ mod tests {
|
|||
let registry = PluginRegistry::default();
|
||||
assert!(registry.is_empty());
|
||||
assert_eq!(registry.all_pages().len(), 0);
|
||||
assert!(registry.last_refresh().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -708,7 +708,8 @@ pub fn render_element(
|
|||
} else if let Some(arr) = items.and_then(|v| v.as_array()) {
|
||||
for item in arr {
|
||||
{
|
||||
let url_opt = media_grid_image_url(item);
|
||||
let base = ctx.client.peek().base_url().to_string();
|
||||
let url_opt = media_grid_image_url(item, &base);
|
||||
let label = media_grid_label(item);
|
||||
rsx! {
|
||||
div { class: "media-grid-item",
|
||||
|
|
@ -795,7 +796,16 @@ pub fn render_element(
|
|||
.map(|obj| {
|
||||
obj
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), value_to_display_string(v)))
|
||||
.filter_map(|(k, v)| {
|
||||
match v {
|
||||
// Skip nested objects and arrays; they are not meaningful as
|
||||
// single-line description terms.
|
||||
serde_json::Value::Object(_) | serde_json::Value::Array(_) => {
|
||||
None
|
||||
},
|
||||
_ => Some((format_key_name(k), value_to_display_string(v))),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
|
@ -1044,7 +1054,7 @@ pub fn render_element(
|
|||
max,
|
||||
show_percentage,
|
||||
} => {
|
||||
let eval_ctx = data.as_json();
|
||||
let eval_ctx = build_ctx(data, &ctx.local_state.read());
|
||||
let pct = evaluate_expression_as_f64(value, &eval_ctx);
|
||||
let fraction = if *max > 0.0 {
|
||||
(pct / max).clamp(0.0, 1.0)
|
||||
|
|
@ -1116,7 +1126,7 @@ pub fn render_element(
|
|||
then,
|
||||
else_element,
|
||||
} => {
|
||||
let eval_ctx = data.as_json();
|
||||
let eval_ctx = build_ctx(data, &ctx.local_state.read());
|
||||
if evaluate_expression_as_bool(condition, &eval_ctx) {
|
||||
render_element(then, data, actions, ctx)
|
||||
} else if let Some(else_el) = else_element {
|
||||
|
|
@ -1244,7 +1254,10 @@ fn render_chart_data(
|
|||
// MediaGrid helpers
|
||||
|
||||
/// Probe a JSON object for common image URL fields.
|
||||
fn media_grid_image_url(item: &serde_json::Value) -> Option<String> {
|
||||
fn media_grid_image_url(
|
||||
item: &serde_json::Value,
|
||||
base_url: &str,
|
||||
) -> Option<String> {
|
||||
for key in &[
|
||||
"thumbnail_url",
|
||||
"thumbnail",
|
||||
|
|
@ -1260,12 +1273,22 @@ fn media_grid_image_url(item: &serde_json::Value) -> Option<String> {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Pinakes media items: construct absolute thumbnail URL from id when
|
||||
// has_thumbnail is true. Relative paths don't work for <img src> in the
|
||||
// desktop WebView context.
|
||||
if item.get("has_thumbnail").and_then(|v| v.as_bool()) == Some(true) {
|
||||
if let Some(id) = item.get("id").and_then(|v| v.as_str()) {
|
||||
if !id.is_empty() {
|
||||
return Some(format!("{base_url}/api/v1/media/{id}/thumbnail"));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Probe a JSON object for a human-readable label.
|
||||
fn media_grid_label(item: &serde_json::Value) -> String {
|
||||
for key in &["title", "name", "label", "caption"] {
|
||||
for key in &["title", "name", "label", "caption", "file_name"] {
|
||||
if let Some(s) = item.get(*key).and_then(|v| v.as_str()) {
|
||||
if !s.is_empty() {
|
||||
return s.to_string();
|
||||
|
|
@ -1601,12 +1624,41 @@ fn safe_col_width_css(w: &str) -> Option<String> {
|
|||
None
|
||||
}
|
||||
|
||||
/// Convert a `snake_case` JSON key to a human-readable title.
|
||||
/// `avg_file_size_bytes` -> `Avg File Size Bytes`
|
||||
fn format_key_name(key: &str) -> String {
|
||||
key
|
||||
.split('_')
|
||||
.map(|word| {
|
||||
let mut chars = word.chars();
|
||||
match chars.next() {
|
||||
None => String::new(),
|
||||
Some(first) => {
|
||||
first.to_uppercase().collect::<String>() + chars.as_str()
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pinakes_plugin_api::Expression;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_format_key_name() {
|
||||
assert_eq!(
|
||||
format_key_name("avg_file_size_bytes"),
|
||||
"Avg File Size Bytes"
|
||||
);
|
||||
assert_eq!(format_key_name("total_media"), "Total Media");
|
||||
assert_eq!(format_key_name("id"), "Id");
|
||||
assert_eq!(format_key_name(""), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_cell_string() {
|
||||
let row = serde_json::json!({ "name": "Alice", "count": 5 });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue