slack status without the slack status.zzstoatzz.io/
quickslice

fix(emoji): restore legacy response shapes\n- /api/frequent-emojis returns raw array\n- /api/custom-emojis returns [{name, filename}] from /emojis dir with png>gif preference

+35 -10
+35 -10
src/api/status_read.rs
··· 360 360 let emojis = db::get_frequent_emojis(&db_pool, 20) 361 361 .await 362 362 .unwrap_or_default(); 363 - Ok(web::Json(json!({ "emojis": emojis }))) 363 + // Legacy response shape: raw array, not wrapped 364 + Ok(web::Json(emojis)) 364 365 } 365 366 366 367 #[get("/api/custom-emojis")] 367 368 pub async fn get_custom_emojis(app_config: web::Data<Config>) -> Result<impl Responder> { 368 - // Serve emojis from configured directory 369 + // Response shape expected by UI: 370 + // [ { "name": "sparkle", "filename": "sparkle.png" }, ... ] 369 371 let dir = app_config.emoji_dir.clone(); 370 - let entries = 371 - std::fs::read_dir(dir).unwrap_or_else(|_| std::fs::read_dir("static/emojis").unwrap()); 372 - let mut names = vec![]; 373 - for entry in entries.flatten() { 374 - if let Some(stem) = entry.path().file_stem().and_then(|s| s.to_str()) { 375 - names.push(stem.to_string()); 372 + let fs_dir = std::path::Path::new(&dir); 373 + let fallback = std::path::Path::new("static/emojis"); 374 + 375 + let mut map: std::collections::BTreeMap<String, String> = std::collections::BTreeMap::new(); 376 + let read_dirs = [fs_dir, fallback]; 377 + for d in read_dirs.iter() { 378 + if let Ok(entries) = std::fs::read_dir(d) { 379 + for entry in entries.flatten() { 380 + let p = entry.path(); 381 + if let (Some(stem), Some(ext)) = (p.file_stem(), p.extension()) { 382 + let name = stem.to_string_lossy().to_string(); 383 + let ext = ext.to_string_lossy().to_ascii_lowercase(); 384 + if ext == "png" || ext == "gif" { 385 + // prefer png over gif if duplicates 386 + let filename = format!("{}.{ext}", name); 387 + map.entry(name) 388 + .and_modify(|v| { 389 + if v.ends_with(".gif") && ext == "png" { 390 + *v = filename.clone(); 391 + } 392 + }) 393 + .or_insert(filename); 394 + } 395 + } 396 + } 376 397 } 377 398 } 378 - names.sort(); 379 - Ok(web::Json(json!({ "custom": names }))) 399 + 400 + let custom: Vec<serde_json::Value> = map 401 + .into_iter() 402 + .map(|(name, filename)| json!({ "name": name, "filename": filename })) 403 + .collect(); 404 + Ok(web::Json(custom)) 380 405 } 381 406 382 407 #[get("/api/following")]