Merge commit '9919ae93c21395043f7f0eac6e54668a70cad146'

This commit is contained in:
Marie Birner
2025-08-02 21:35:29 +02:00
6 changed files with 93 additions and 99 deletions

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
# TODOs
- [ ] limit names to 25(?) chars

View File

@@ -29,29 +29,35 @@ async fn index(State(backend): State<Arc<Backend>>, cookies: CookieJar) -> Respo
} }
form { form {
fieldset role="group" { fieldset role="group" {
input name="name" placeholder=(format!("Rename {}", client.get_display_name())) aria-label="Name"; input
input type="submit" value="Save"; name="name"
} placeholder=(format!("Rename {}", client.get_display_name()))
aria-label="Name";
input type="submit" value="Save";
}
} }
p { "You have found " (sightings.len()) "/" (amount_total_cameras) " cameras." } p {
"You have found "
(sightings.len())
"/"
(amount_total_cameras)
" cameras."
progress value=(sightings.len()) max=(amount_total_cameras);
}
p { p {
h2 { "Highscore" } h2 { "Highscore" }
ul.iterated { ul.iterated {
@for rank in highscore { @for rank in highscore {
li.card { li.card {
span { span {
span.font-headline.rank.text-muted { span.font-headline.rank.text-muted { (rank.rank) "." }
(rank.rank)"." @if rank.client == client { (PreEscaped("<mark>")) }
} (rank.client.get_display_name())
@if rank.uuid == client.uuid { (PreEscaped("<mark>")) } @if rank.client == client { (PreEscaped("</mark>")) }
(rank.name)
@if rank.uuid == client.uuid { (PreEscaped("</mark>")) }
}
span.font-headline.cam {
(rank.amount)(PreEscaped("&nbsp;"))"📸"
} }
span.font-headline.cam { (rank.amount) (PreEscaped("&nbsp;")) "📸" }
} }
} }
} }

View File

@@ -3,9 +3,7 @@ use maud::{html, Markup};
pub(super) async fn index() -> Markup { pub(super) async fn index() -> Markup {
new(html! { new(html! {
h1 { h1 { "Digital Shadows" }
"Digital Shadows"
}
hgroup { hgroup {
h2 { h2 {
"Who owns your " "Who owns your "
@@ -22,9 +20,7 @@ pub(super) async fn index() -> Markup {
blockquote { blockquote {
"Digital Shadows confronts visitors with their digital self copied, measured, analyzed. An experiment on data power, visibility, and control in the digital age." "Digital Shadows confronts visitors with their digital self copied, measured, analyzed. An experiment on data power, visibility, and control in the digital age."
footer { footer {
cite { cite { "— René Mayrhofer" }
"— René Mayrhofer"
}
} }
} }
p { p {

View File

@@ -9,6 +9,12 @@ pub struct Client {
pub name: Option<String>, pub name: Option<String>,
} }
impl PartialEq for Client {
fn eq(&self, other: &Self) -> bool {
self.uuid == other.uuid
}
}
impl Client { impl Client {
pub(crate) fn get_display_name(&self) -> String { pub(crate) fn get_display_name(&self) -> String {
match &self.name { match &self.name {

View File

@@ -1,56 +1,42 @@
use crate::{random_names::get_name_by_uuid, Backend}; use crate::{model::client::Client, Backend};
struct RankDb {
pub(crate) rank: i64,
pub(crate) name: Option<String>,
pub(crate) uuid: String,
pub(crate) amount: i64,
}
pub(crate) struct Rank { pub(crate) struct Rank {
pub(crate) rank: i64, pub(crate) rank: i64,
pub(crate) name: String, pub(crate) client: Client,
pub(crate) uuid: String,
pub(crate) amount: i64, pub(crate) amount: i64,
} }
impl From<RankDb> for Rank {
fn from(value: RankDb) -> Self {
let name = match value.name {
Some(name) => name,
None => get_name_by_uuid(&value.uuid).to_string(),
};
Self {
rank: value.rank,
name,
uuid: value.uuid,
amount: value.amount,
}
}
}
impl Backend { impl Backend {
pub(crate) async fn highscore(&self) -> Vec<Rank> { pub(crate) async fn highscore(&self) -> Vec<Rank> {
match self { match self {
Backend::Sqlite(db) => sqlx::query_as!( Backend::Sqlite(db) => {
RankDb, let rows = sqlx::query!(
"SELECT "SELECT
RANK() OVER (ORDER BY COUNT(s.client_uuid) DESC) as rank, RANK() OVER (ORDER BY COUNT(s.client_uuid) DESC) as rank,
c.name, c.name,
c.uuid, c.uuid,
COUNT(s.client_uuid) as amount COUNT(s.client_uuid) as amount
FROM client c FROM client c
LEFT JOIN sightings s ON c.uuid = s.client_uuid LEFT JOIN sightings s ON c.uuid = s.client_uuid
GROUP BY c.uuid, c.name GROUP BY c.uuid, c.name
ORDER BY amount DESC" ORDER BY amount DESC"
) )
.fetch_all(db) .fetch_all(db)
.await .await
.unwrap_or_default() .unwrap_or_default();
.into_iter()
.map(|r| r.into()) rows.into_iter()
.collect(), .map(|row| Rank {
rank: row.rank,
client: Client {
uuid: row.uuid,
name: row.name,
},
amount: row.amount,
})
.map(|r| r.into())
.collect()
}
} }
} }
} }

View File

@@ -10,52 +10,50 @@ pub fn new(content: Markup) -> Markup {
meta name="viewport" content="width=device-width, initial-scale=1.0"; meta name="viewport" content="width=device-width, initial-scale=1.0";
link rel="stylesheet" href="/static/pico.min.css"; link rel="stylesheet" href="/static/pico.min.css";
link rel="stylesheet" href="/static/style.css"; link rel="stylesheet" href="/static/style.css";
title { title { "Digital Shadows" }
"Digital Shadows"
}
} }
body { body {
header.container { header.container {
nav { nav {
ul { ul {
li { a href="/" { strong { "Digital Shadows" } } } li {
a href="/" {
strong { "Digital Shadows" }
}
} }
ul { }
li { ul {
a href="/"{ li {
span role="img" aria-label="home" { a href="/" {
"🏠" span role="img" aria-label="home" { "🏠" }
}
}
} }
li { }
a href="/game" { li {
span role="img" aria-label="camera" { a href="/game" {
"📸" span role="img" aria-label="camera" { "📸" }
}
}
} }
li { span id="theme_switcher" {} } }
li {
span id="theme_switcher" {}
} }
} }
} }
}
main.container { main.container {
section { section { (content) }
(content) }
}
}
footer.container { footer.container {
small { small {
"Footer " "Footer "
mark { "to be completed" } mark { "to be completed" }
a href="#" { "with links" } a href="#" { "with links" }
"" ""
a target="_blank" href="https://www.digidow.eu/impressum/" { "Impressum" } a target="_blank" href="https://www.digidow.eu/impressum/" { "Impressum" }
}
} }
script src="/static/theme.js" {} }
script src="/static/theme.js" {}
} }
} }
} }