aboutsummaryrefslogtreecommitdiff
path: root/ui/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-20 03:38:27 +0100
committermetamuffin <metamuffin@disroot.org>2026-01-20 03:38:27 +0100
commit03f38fdc3bd45962be8555e50f18fd7761c17989 (patch)
tree6c81b060e548c9c85922949b5b8aa63ef364778f /ui/src
parent508be3ef912572d958c8a2b995d4d1d4d1451b1d (diff)
downloadjellything-03f38fdc3bd45962be8555e50f18fd7761c17989.tar
jellything-03f38fdc3bd45962be8555e50f18fd7761c17989.tar.bz2
jellything-03f38fdc3bd45962be8555e50f18fd7761c17989.tar.zst
more ui refactor
Diffstat (limited to 'ui/src')
-rw-r--r--ui/src/account/mod.rs51
-rw-r--r--ui/src/account/settings.rs36
-rw-r--r--ui/src/admin/import.rs18
-rw-r--r--ui/src/admin/mod.rs28
-rw-r--r--ui/src/admin/user.rs6
-rw-r--r--ui/src/filter_sort.rs2
-rw-r--r--ui/src/format.rs12
-rw-r--r--ui/src/items.rs4
-rw-r--r--ui/src/props.rs2
-rw-r--r--ui/src/scaffold.rs28
-rw-r--r--ui/src/search.rs6
-rw-r--r--ui/src/stats.rs2
12 files changed, 85 insertions, 110 deletions
diff --git a/ui/src/account/mod.rs b/ui/src/account/mod.rs
index 255f7a1..e7da26f 100644
--- a/ui/src/account/mod.rs
+++ b/ui/src/account/mod.rs
@@ -5,16 +5,13 @@
*/
pub mod settings;
-use crate::{
- Page,
- locale::{Language, tr, trs},
-};
+use crate::{Page, locale::tr, scaffold::RenderInfo};
use jellycommon::routes::{u_account_login, u_account_register};
impl Page for AccountLogin<'_> {
fn title(&self) -> String {
tr(
- *self.lang,
+ self.ri.lang,
if self.logged_in {
"account.login.switch"
} else {
@@ -30,7 +27,7 @@ impl Page for AccountLogin<'_> {
}
impl Page for AccountRegister<'_> {
fn title(&self) -> String {
- tr(*self.lang, "account.register").to_string()
+ tr(self.ri.lang, "account.register").to_string()
}
fn to_render(&self) -> markup::DynRender<'_> {
markup::new!(@self)
@@ -38,7 +35,7 @@ impl Page for AccountRegister<'_> {
}
impl Page for AccountRegisterSuccess<'_> {
fn title(&self) -> String {
- tr(*self.lang, "account.register").to_string()
+ tr(self.ri.lang, "account.register").to_string()
}
fn to_render(&self) -> markup::DynRender<'_> {
markup::new!(@self)
@@ -46,7 +43,7 @@ impl Page for AccountRegisterSuccess<'_> {
}
impl Page for AccountLogout<'_> {
fn title(&self) -> String {
- tr(*self.lang, "account.logout").to_string()
+ tr(self.ri.lang, "account.logout").to_string()
}
fn to_render(&self) -> markup::DynRender<'_> {
markup::new!(@self)
@@ -54,53 +51,53 @@ impl Page for AccountLogout<'_> {
}
markup::define! {
- AccountRegister<'a>(lang: &'a Language) {
+ AccountRegister<'a>(ri: &'a RenderInfo<'a>) {
form.account[method="POST", action=""] {
- h1 { @trs(lang, "account.register") }
+ h1 { @tr(ri.lang, "account.register") }
- label[for="inp-invitation"] { @trs(lang, "account.register.invitation") }
+ label[for="inp-invitation"] { @tr(ri.lang, "account.register.invitation") }
input[type="text", id="inp-invitation", name="invitation"]; br;
- label[for="inp-username"] { @trs(lang, "account.username") }
+ label[for="inp-username"] { @tr(ri.lang, "account.username") }
input[type="text", id="inp-username", name="username"]; br;
- label[for="inp-password"] { @trs(lang, "account.password") }
+ label[for="inp-password"] { @tr(ri.lang, "account.password") }
input[type="password", id="inp-password", name="password"]; br;
- input[type="submit", value=&*tr(**lang, "account.register.submit")];
+ input[type="submit", value=tr(ri.lang, "account.register.submit")];
- p { @trs(lang, "account.register.login") " " a[href=u_account_login()] { @trs(lang, "account.register.login_here") } }
+ p { @tr(ri.lang, "account.register.login") " " a[href=u_account_login()] { @tr(ri.lang, "account.register.login_here") } }
}
}
- AccountRegisterSuccess<'a>(lang: &'a Language, logged_in: bool) {
- h1 { @trs(lang, if *logged_in {
+ AccountRegisterSuccess<'a>(ri: &'a RenderInfo<'a>, logged_in: bool) {
+ h1 { @tr(ri.lang, if *logged_in {
"account.register.success.switch"
} else {
"account.register.success"
})}
}
- AccountLogin<'a>(lang: &'a Language, logged_in: bool) {
+ AccountLogin<'a>(ri: &'a RenderInfo<'a>, logged_in: bool) {
form.account[method="POST", action=""] {
h1 { @self.title() }
- label[for="inp-username"] { @trs(lang, "account.username") }
+ label[for="inp-username"] { @tr(ri.lang, "account.username") }
input[type="text", id="inp-username", name="username"]; br;
- label[for="inp-password"] { @trs(lang, "account.password") }
+ label[for="inp-password"] { @tr(ri.lang, "account.password") }
input[type="password", id="inp-password", name="password"]; br;
- input[type="submit", value=&*tr(**lang, if *logged_in { "account.login.submit.switch" } else { "account.login.submit" })];
+ input[type="submit", value=tr(ri.lang, if *logged_in { "account.login.submit.switch" } else { "account.login.submit" })];
@if *logged_in {
- p { @trs(lang, "account.login.register.switch") " " a[href=u_account_register()] { @trs(lang, "account.login.register_here") } }
+ p { @tr(ri.lang, "account.login.register.switch") " " a[href=u_account_register()] { @tr(ri.lang, "account.login.register_here") } }
} else {
- p { @trs(lang, "account.login.cookie_note") }
- p { @trs(lang, "account.login.register") " " a[href=u_account_register()] { @trs(lang, "account.login.register_here") } }
+ p { @tr(ri.lang, "account.login.cookie_note") }
+ p { @tr(ri.lang, "account.login.register") " " a[href=u_account_register()] { @tr(ri.lang, "account.login.register_here") } }
}
}
}
- AccountLogout<'a>(lang: &'a Language) {
+ AccountLogout<'a>(ri: &'a RenderInfo<'a>) {
form.account[method="POST", action=""] {
- h1 { @trs(lang, "account.logout") }
- input[type="submit", value=&*tr(**lang, "account.logout.submit")];
+ h1 { @tr(ri.lang, "account.logout") }
+ input[type="submit", value=tr(ri.lang, "account.logout.submit")];
}
}
}
diff --git a/ui/src/account/settings.rs b/ui/src/account/settings.rs
index f59dd3e..83f72b0 100644
--- a/ui/src/account/settings.rs
+++ b/ui/src/account/settings.rs
@@ -5,8 +5,8 @@
*/
use crate::{
FlashM, Page,
- locale::{Language, tr, trs},
- scaffold::{FlashDisplay, SessionInfo},
+ locale::tr,
+ scaffold::{FlashDisplay, RenderInfo, SessionInfo},
};
use jellycommon::routes::{u_account_login, u_account_settings};
use markup::RenderAttributeValue;
@@ -21,49 +21,49 @@ impl Page for SettingsPage<'_> {
}
markup::define! {
- SettingsPage<'a>(session: &'a SessionInfo, lang: &'a Language, flash: &'a FlashM) {
+ SettingsPage<'a>(ri: &'a RenderInfo<'a>, session: &'a SessionInfo, flash: &'a FlashM) {
h1 { "Settings" }
@FlashDisplay {flash}
- h2 { @trs(lang, "account") }
+ h2 { @tr(ri.lang, "account") }
a.switch_account[href=u_account_login()] { "Switch Account" }
form[method="POST", action=u_account_settings()] {
- label[for="username"] { @trs(lang, "account.username") }
+ label[for="username"] { @tr(ri.lang, "account.username") }
input[type="text", id="username", disabled, value=&session.user.name];
- input[type="submit", disabled, value=&*tr(**lang, "settings.immutable")];
+ input[type="submit", disabled, value=tr(ri.lang, "settings.immutable")];
}
form[method="POST", action=u_account_settings()] {
- label[for="display_name"] { @trs(lang, "account.display_name") }
+ label[for="display_name"] { @tr(ri.lang, "account.display_name") }
input[type="text", id="display_name", name="display_name", value=&session.user.display_name];
- input[type="submit", value=&*tr(**lang, "settings.update")];
+ input[type="submit", value=tr(ri.lang, "settings.update")];
}
form[method="POST", action=u_account_settings()] {
- label[for="password"] { @trs(lang, "account.password") }
+ label[for="password"] { @tr(ri.lang, "account.password") }
input[type="password", id="password", name="password"];
- input[type="submit", value=&*tr(**lang, "settings.update")];
+ input[type="submit", value=tr(ri.lang, "settings.update")];
}
- h2 { @trs(lang, "settings.appearance") }
+ h2 { @tr(ri.lang, "settings.appearance") }
form[method="POST", action=u_account_settings()] {
fieldset {
- legend { @trs(lang, "settings.appearance.theme") }
+ legend { @tr(ri.lang, "settings.appearance.theme") }
@for theme in Theme::ALL {
- label { input[type="radio", name="theme", value=A(*theme), checked=session.user.theme==*theme]; @trs(lang, &format!("theme.{theme}")) } br;
+ label { input[type="radio", name="theme", value=A(*theme), checked=session.user.theme==*theme]; @tr(ri.lang, &format!("theme.{theme}")) } br;
}
}
- input[type="submit", value=&*tr(**lang, "settings.apply")];
+ input[type="submit", value=tr(ri.lang, "settings.apply")];
}
form[method="POST", action=u_account_settings()] {
fieldset {
- legend { @trs(lang, "settings.player_preference") }
+ legend { @tr(ri.lang, "settings.player_preference") }
@for kind in PlayerKind::ALL {
- label { input[type="radio", name="player_preference", value=A(*kind), checked=session.user.player_preference==*kind]; @trs(lang, &format!("player_kind.{kind}")) } br;
+ label { input[type="radio", name="player_preference", value=A(*kind), checked=session.user.player_preference==*kind]; @tr(ri.lang, &format!("player_kind.{kind}")) } br;
}
}
- input[type="submit", value=&*tr(**lang, "settings.apply")];
+ input[type="submit", value=tr(ri.lang, "settings.apply")];
}
form[method="POST", action=u_account_settings()] {
label[for="native_secret"] { "Native Secret" }
input[type="password", id="native_secret", name="native_secret"];
- input[type="submit", value=&*tr(**lang, "settings.update")];
+ input[type="submit", value=tr(ri.lang, "settings.update")];
p { "The secret can be found in " code{"$XDG_CONFIG_HOME/jellynative_secret"} " or by clicking " a.button[href="jellynative://show-secret-v1"] { "Show Secret" } "." }
}
}
diff --git a/ui/src/admin/import.rs b/ui/src/admin/import.rs
index 1233c7d..805d787 100644
--- a/ui/src/admin/import.rs
+++ b/ui/src/admin/import.rs
@@ -4,11 +4,7 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{
- FlashM, Page,
- locale::{Language, tr, trs},
- scaffold::FlashDisplay,
-};
+use crate::{FlashM, Page, locale::tr, scaffold::{FlashDisplay, RenderInfo}};
use jellycommon::routes::u_admin_import_post;
impl Page for AdminImportPage<'_> {
@@ -21,18 +17,18 @@ impl Page for AdminImportPage<'_> {
}
markup::define!(
- AdminImportPage<'a>(lang: &'a Language, busy: bool, last_import_err: &'a [String], flash: &'a FlashM) {
+ AdminImportPage<'a>(ri: &'a RenderInfo<'a>, busy: bool, last_import_err: &'a [String], flash: &'a FlashM) {
@FlashDisplay { flash }
@if *busy {
- h1 { @trs(lang, "admin.import.running") }
+ h1 { @tr(ri.lang, "admin.import.running") }
noscript { "Live import progress needs javascript." }
div[id="admin_import"] {}
} else {
- h1 { @trs(lang, "admin.import.title") }
+ h1 { @tr(ri.lang, "admin.import.title") }
@if !last_import_err.is_empty() {
section.message.error {
details {
- summary { p.error { @tr(**lang, "admin.import_errors").replace("{n}", &last_import_err.len().to_string()) } }
+ summary { p.error { @tr(ri.lang, "admin.import_errors").replace("{n}", &last_import_err.len().to_string()) } }
ol { @for e in *last_import_err {
li.error { pre.error { @e } }
}}
@@ -40,10 +36,10 @@ markup::define!(
}
}
form[method="POST", action=u_admin_import_post(true)] {
- input[type="submit", value=tr(**lang, "admin.dashboard.import.inc").to_string()];
+ input[type="submit", value=tr(ri.lang, "admin.dashboard.import.inc").to_string()];
}
form[method="POST", action=u_admin_import_post(false)] {
- input[type="submit", value=tr(**lang, "admin.dashboard.import.full").to_string()];
+ input[type="submit", value=tr(ri.lang, "admin.dashboard.import.full").to_string()];
}
}
}
diff --git a/ui/src/admin/mod.rs b/ui/src/admin/mod.rs
index 632bcd4..f42ba76 100644
--- a/ui/src/admin/mod.rs
+++ b/ui/src/admin/mod.rs
@@ -8,11 +8,7 @@ pub mod import;
pub mod log;
pub mod user;
-use crate::{
- FlashM, Page,
- locale::{Language, tr, trs},
- scaffold::FlashDisplay,
-};
+use crate::{FlashM, Page, locale::tr, scaffold::{FlashDisplay, RenderInfo}};
use jellycommon::routes::{
u_admin_import, u_admin_invite_create, u_admin_invite_remove, u_admin_log,
u_admin_update_search, u_admin_users,
@@ -28,33 +24,33 @@ impl Page for AdminDashboardPage<'_> {
}
markup::define!(
- AdminDashboardPage<'a>(lang: &'a Language, busy: Option<&'static str>, flash: &'a FlashM, invites: &'a [String]) {
- h1 { @trs(lang, "admin.dashboard.title") }
+ AdminDashboardPage<'a>(ri: &'a RenderInfo<'a>, busy: Option<&'static str>, flash: &'a FlashM, invites: &'a [String]) {
+ h1 { @tr(ri.lang, "admin.dashboard.title") }
@FlashDisplay { flash }
ul {
- li{a[href=u_admin_log(true)] { @trs(lang, "admin.log.warnonly") }}
- li{a[href=u_admin_log(false)] { @trs(lang, "admin.log.full") }}
+ li{a[href=u_admin_log(true)] { @tr(ri.lang, "admin.log.warnonly") }}
+ li{a[href=u_admin_log(false)] { @tr(ri.lang, "admin.log.full") }}
}
- a[href=u_admin_import()] { h2 { @trs(lang, "admin.import.title") }}
+ a[href=u_admin_import()] { h2 { @tr(ri.lang, "admin.import.title") }}
@if let Some(text) = busy {
section.message { p.warn { @text } }
}
form[method="POST", action=u_admin_update_search()] {
- input[type="submit", value=tr(**lang, "admin.dashboard.update_search").to_string()];
+ input[type="submit", value=tr(ri.lang, "admin.dashboard.update_search").to_string()];
}
- h2 { @trs(lang, "admin.dashboard.users") }
- p { a[href=u_admin_users()] { @trs(lang, "admin.dashboard.manage_users") } }
- h2 { @trs(lang, "admin.dashboard.invites") }
+ h2 { @tr(ri.lang, "admin.dashboard.users") }
+ p { a[href=u_admin_users()] { @tr(ri.lang, "admin.dashboard.manage_users") } }
+ h2 { @tr(ri.lang, "admin.dashboard.invites") }
form[method="POST", action=u_admin_invite_create()] {
- input[type="submit", value=tr(**lang, "admin.dashboard.create_invite").to_string()];
+ input[type="submit", value=tr(ri.lang, "admin.dashboard.create_invite").to_string()];
}
ul { @for t in *invites {
li {
form[method="POST", action=u_admin_invite_remove()] {
span { @t }
input[type="text", name="invite", value=&t, hidden];
- input[type="submit", value=tr(**lang, "admin.dashboard.create_invite").to_string()];
+ input[type="submit", value=tr(ri.lang, "admin.dashboard.create_invite").to_string()];
}
}
}}
diff --git a/ui/src/admin/user.rs b/ui/src/admin/user.rs
index a582d41..e4a8975 100644
--- a/ui/src/admin/user.rs
+++ b/ui/src/admin/user.rs
@@ -4,11 +4,7 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{
- FlashM, Page,
- locale::{Language, trs},
- scaffold::FlashDisplay,
-};
+use crate::{FlashM, Page, scaffold::FlashDisplay};
use jellycommon::routes::{
u_admin_user, u_admin_user_permission, u_admin_user_remove, u_admin_users,
};
diff --git a/ui/src/filter_sort.rs b/ui/src/filter_sort.rs
index 70e6b8a..80395e5 100644
--- a/ui/src/filter_sort.rs
+++ b/ui/src/filter_sort.rs
@@ -3,8 +3,6 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-
-use crate::locale::trs;
use markup::RenderAttributeValue;
const SORT_CATS: &[(&str, &[(SortProperty, &str)])] = {
diff --git a/ui/src/format.rs b/ui/src/format.rs
index 3b49695..11040de 100644
--- a/ui/src/format.rs
+++ b/ui/src/format.rs
@@ -4,13 +4,13 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use jellycommon::LANG_ENG;
+use jellycommon::{LANG_ENG, Language};
-use crate::locale::{TrString, tr, trs};
+use crate::locale::tr;
use std::fmt::Write;
pub fn format_duration(d: f64) -> String {
- format_duration_mode(d, false, Language::English)
+ format_duration_mode(d, false, LANG_ENG.0)
}
pub fn format_duration_long(d: f64, lang: Language) -> String {
format_duration_mode(d, true, lang)
@@ -55,15 +55,15 @@ fn test_duration_short() {
#[test]
fn test_duration_long() {
assert_eq!(
- format_duration_long(61., LANG_ENG).as_str(),
+ format_duration_long(61., LANG_ENG.0).as_str(),
"1 minute and 1 second"
);
assert_eq!(
- format_duration_long(121., LANG_ENG).as_str(),
+ format_duration_long(121., LANG_ENG.0).as_str(),
"2 minutes and 1 second"
);
assert_eq!(
- format_duration_long(3661., LANG_ENG).as_str(),
+ format_duration_long(3661., LANG_ENG.0).as_str(),
"1 hour, 1 minute and 1 second"
);
}
diff --git a/ui/src/items.rs b/ui/src/items.rs
index 4bce43c..447ec01 100644
--- a/ui/src/items.rs
+++ b/ui/src/items.rs
@@ -3,11 +3,11 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{Page, locale::tr};
+use crate::{Page, locale::tr, scaffold::RenderInfo};
use markup::DynRender;
markup::define! {
- ItemsPage<'a>(lang: &'a Language, r: ApiItemsResponse, filter: &'a NodeFilterSort, page: usize) {
+ ItemsPage<'a>(ri: &'a RenderInfo<'a>, r: ApiItemsResponse, filter: &'a NodeFilterSort, page: usize) {
.page.dir {
h1 { "All Items" }
// @NodeFilterSortForm { f: filter, lang }
diff --git a/ui/src/props.rs b/ui/src/props.rs
index 2860933..3547045 100644
--- a/ui/src/props.rs
+++ b/ui/src/props.rs
@@ -5,7 +5,7 @@
*/
use crate::{
format::{MediaInfoExt, format_count, format_duration},
- locale::{tr, trs},
+ locale::tr,
};
markup::define! {
diff --git a/ui/src/scaffold.rs b/ui/src/scaffold.rs
index 0962f6e..8b96f9f 100644
--- a/ui/src/scaffold.rs
+++ b/ui/src/scaffold.rs
@@ -6,7 +6,7 @@
use crate::{
CONF, FlashM,
- locale::{escape, tr, trs},
+ locale::{escape, tr},
};
use jellycommon::{
jellyobject::{Object, Tag},
@@ -26,10 +26,6 @@ pub struct RenderInfo<'a> {
pub status_message: Option<&'a str>,
}
-pub struct SessionInfo {
- pub user: User,
-}
-
markup::define! {
Scaffold<'a, Main: Render>(ri: &'a RenderInfo<'a>, title: String, main: Main, class: &'a str) {
@markup::doctype()
@@ -54,23 +50,23 @@ markup::define! {
nav {
h1 { a[href=if ri.user.is_some() {u_home()} else {"/".to_string()}] { @if *LOGO_ENABLED { img.logo[src="/assets/logo.svg"]; } else { @CONF.brand } } } " "
@if ri.user.is_some() {
- a.library[href=u_node_slug("library")] { @trs(lang, "nav.root") } " "
- a.library[href=u_items()] { @trs(lang, "nav.all") } " "
- a.library[href=u_search()] { @trs(lang, "nav.search") } " "
- a.library[href=u_stats()] { @trs(lang, "nav.stats") } " "
- @if renderinfo.importing { span.warn { @trs(lang, "nav.importing") } }
+ a.library[href=u_node_slug("library")] { @tr(ri.lang, "nav.root") } " "
+ a.library[href=u_items()] { @tr(ri.lang, "nav.all") } " "
+ a.library[href=u_search()] { @tr(ri.lang, "nav.search") } " "
+ a.library[href=u_stats()] { @tr(ri.lang, "nav.stats") } " "
+ @if let Some(m) = ri.status_message { span.warn { @tr(ri.lang, m) } }
}
div.account {
@if let Some(user) = &ri.user {
- span { @raw(tr(*lang, "nav.username").replace("{name}", &format!("<b class=\"username\">{}</b>", escape(&session.user.display_name)))) } " "
+ span { @raw(tr(ri.lang, "nav.username").replace("{name}", &format!("<b class=\"username\">{}</b>", escape(&ri.user.display_name)))) } " "
@if session.user.admin {
- a.admin.hybrid_button[href=u_admin_dashboard()] { p {@trs(lang, "nav.admin")} } " "
+ a.admin.hybrid_button[href=u_admin_dashboard()] { p {@tr(ri.lang, "nav.admin")} } " "
}
- a.settings.hybrid_button[href=u_account_settings()] { p {@trs(lang, "nav.settings")} } " "
- a.logout.hybrid_button[href=u_account_logout()] { p {@trs(lang, "nav.logout")} }
+ a.settings.hybrid_button[href=u_account_settings()] { p {@tr(ri.lang, "nav.settings")} } " "
+ a.logout.hybrid_button[href=u_account_logout()] { p {@tr(ri.lang, "nav.logout")} }
} else {
- a.register.hybrid_button[href=u_account_register()] { p {@trs(lang, "nav.register")} } " "
- a.login.hybrid_button[href=u_account_login()] { p {@trs(lang, "nav.login")} }
+ a.register.hybrid_button[href=u_account_register()] { p {@tr(ri.lang, "nav.register")} } " "
+ a.login.hybrid_button[href=u_account_login()] { p {@tr(ri.lang, "nav.login")} }
}
}
}
diff --git a/ui/src/search.rs b/ui/src/search.rs
index 5b9909c..dd5aa0c 100644
--- a/ui/src/search.rs
+++ b/ui/src/search.rs
@@ -4,11 +4,7 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{
- Page,
- locale::{tr, trs},
- node_card::NodeCard,
-};
+use crate::{Page, locale::tr, node_card::NodeCard};
use markup::DynRender;
impl Page for SearchPage<'_> {
diff --git a/ui/src/stats.rs b/ui/src/stats.rs
index 0a88fac..c9001b0 100644
--- a/ui/src/stats.rs
+++ b/ui/src/stats.rs
@@ -7,7 +7,7 @@
use crate::{
Page,
format::{format_duration, format_duration_long, format_kind, format_size},
- locale::{tr, trs},
+ locale::tr,
};
use jellycommon::routes::u_node_slug;
use markup::raw;