summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorMartin Hafskjold Thoresen <martin@vind.ai>2025-01-11 01:01:10 +0100
committerMartin Hafskjold Thoresen <martin@vind.ai>2025-01-11 01:01:10 +0100
commit42fd8cd3560047c88a2167a4d5c5845dd81eb3ec (patch)
treecdbd3817c28ae5264f8cf394684014f7bd4d509f /src/main.rs
parent2f2beb79720ec9fd35b993e7ad907ecf32005cde (diff)
downloadmusicgame-42fd8cd3560047c88a2167a4d5c5845dd81eb3ec.tar.gz
musicgame-42fd8cd3560047c88a2167a4d5c5845dd81eb3ec.zip
Add styling and hot reload
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs76
1 files changed, 61 insertions, 15 deletions
diff --git a/src/main.rs b/src/main.rs
index a6963c4..773c0e3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,6 @@
use std::{
collections::{HashMap, HashSet},
+ env,
net::SocketAddr,
sync::Arc,
};
@@ -50,6 +51,23 @@ fn handle_panic(err: Box<dyn std::any::Any + Send + 'static>) -> Response {
#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Deserialize)]
struct UserId(u64);
+impl UserId {
+ fn funny_name(&self) -> String {
+ #[rustfmt::skip]
+ const ADJECTIVES: [&'static str; 32] = [ "Beautiful", "Brave", "Bright", "Clever", "Calm", "Dull", "Eager", "Fancy", "Gentle", "Harsh", "Humble", "Kind", "Lazy", "Loud", "Narrow", "Quiet", "Quick", "Rare", "Rough", "Shiny", "Smart", "Smooth", "Strong", "Sweet", "Tall", "Tense", "Ugly", "Vast", "Weak", "Wise", "Young", "Zealous", ];
+ #[rustfmt::skip]
+ const VERBS: [&'static str; 32]= [ "Running", "Jumping", "Walking", "Talking", "Eating", "Drinking", "Laughing", "Crying", "Singing", "Dancing", "Writing", "Reading", "Sleeping", "Driving", "Cooking", "Building", "Breaking", "Thinking", "Speaking", "Listening", "Watching", "Throwing", "Catching", "Swimming", "Playing", "Fighting", "Working", "Learning", "Teaching", "Growing", "Loving", "Sharing", ];
+ #[rustfmt::skip]
+ const NOUNS: [&'static str; 32]= [ "Clock", "Cloud", "Car", "Tree", "Book", "Candle", "Mirror", "Robot", "Star", "Moon", "Sun", "River", "Rock", "Mountain", "Flower", "Train", "Shoe", "House", "Boat", "Bridge", "Key", "Pencil", "Cup", "Chair", "Phone", "Lamp", "Blanket", "Door", "Toy", "Kite", "Basket", "Balloon", ];
+
+ let u = self.0 as usize;
+ let noun = NOUNS[(u >> 0) & 31];
+ let verb = VERBS[(u >> 5) & 31];
+ let ajd = ADJECTIVES[(u >> 10) & 31];
+ format!("{} {} {}", ajd, verb, noun)
+ }
+}
+
#[derive(Serialize, Deserialize, Debug, Clone)]
struct NoteDatum {
// Epoch time in ms
@@ -248,18 +266,21 @@ impl Game {
html! {
script { ({PreEscaped("document.notes = []; ")})}
h1 { "Playing Game" }
- p { "Were playing here" }
+ p { "Play a tune" }
canvas
#canvas
- height=(256) width=(256)
- style="position:relative; background: cyan;"
+ height=(512) width=(256)
+ style="position:relative;"
{};
- script { (PreEscaped(r#"document.getElementById("canvas").onclick = (e) => {
+ script { (PreEscaped(r#"cv = document.getElementById("canvas");
+ cv.onclick = (e) => {
const notes = JSON.parse(e.target.dataset.notes ?? "[]");
notes.push({ time: Date.now(), y: e.layerY / e.target.height});
e.target.dataset.notes = JSON.stringify(notes);
- }"#))}
+ }
+ drawNoteLines(cv);
+ "#))}
button hx-post=(format!("/game/{gid}/submit"))
hx-vals=r#"js:{ "notes": document.getElementById("canvas").dataset.notes }"#
@@ -301,7 +322,7 @@ impl Game {
html! {
h1 { (format!("Round {i}/{n}"))}
- p { "Author was " (r.author.0)}
+ p { "Author was " (r.author.funny_name())}
form #form-guess {
input placeholder="Your guess..." name="guess" {};
input type="submit" hx-post=(format!("/game/{gid}/guess")) hx-target="#form-guess" {};
@@ -349,7 +370,7 @@ loadSample(soundUrl).then((sample) => {
data-accept=(accept)
hx-post=(post)
hx-swap="outerHTML"
- { (g) " (click to toggle)"}
+ { (g) }
}
}
@@ -360,6 +381,7 @@ loadSample(soundUrl).then((sample) => {
html! {
h1 { "Mark correct answers" }
+ span { "Click an answer to toggle correctness"}
ul #guesslist {
@for &u in v {
(self.guess_li(u))
@@ -394,7 +416,7 @@ loadSample(soundUrl).then((sample) => {
h1 { "Leaderboard" }
ol {
@for (uid, score) in v {
- li { span { (uid.0) ": " (score) " points" } }
+ li { span { (uid.funny_name()) ": " (score) " points" } }
}
}
@@ -465,8 +487,8 @@ async fn route_index(State(st): State<Server>) -> Response {
html! { "No current games"}
} else {
html! {
- h3 { "Games" }
- ul {
+ h3 { "Current games" }
+ ul .games-list {
@for id in &game_ids {
li { a href=(format!("/game/{id}")) { (id) } }
}
@@ -475,10 +497,12 @@ async fn route_index(State(st): State<Server>) -> Response {
};
let html = html! {
- h1 { "Good game "}
- (game_list)
+ section .main-page {
+ h1 { "Good game "}
+ (game_list)
- button hx-post="/game" { "Create game" }
+ button hx-post="/game" { "Create game" }
+ }
};
html_response(html! {
@@ -489,6 +513,7 @@ async fn route_index(State(st): State<Server>) -> Response {
meta name="viewport" content="width=device-width, initial-scale=1" {};
script src="https://unpkg.com/htmx.org@2.0.4" {};
link rel="stylesheet" type="text/css" href="static/style.css";
+ @if let Some(n) = hot_reload_script() { (n) }
}
body {
(html)
@@ -597,13 +622,14 @@ async fn get_game(
link rel="stylesheet" type="text/css" href="/static/style.css";
script src="/static/main.js" {};
+ @if let Some(n) = hot_reload_script() { (n) }
}
body hx-ext="ws" ws-connect=(format!("/game/{gid}/ws")) {
- footer { }
+ header { }
section #content {
(html)
}
- footer { span { "You are " (uid.0) } }
+ footer { span { "You are " (uid.funny_name()) } }
}
})
}
@@ -804,6 +830,25 @@ async fn identity_middleware(jar: CookieJar, mut req: Request, next: Next) -> Re
res
}
+#[cfg(debug_assertions)]
+mod hot_reload;
+#[cfg(not(debug_assertions))]
+mod hot_reload {
+ use axum::Router;
+ pub fn router(_: &str) -> Router<()> {
+ Router::new()
+ }
+}
+
+pub fn hot_reload_script() -> Option<Markup> {
+ let is_prod = env::var("STAGE").is_ok_and(|stage| stage == "prod");
+ if is_prod {
+ None
+ } else {
+ Some(html! { script { (PreEscaped(include_str!("./hot_reload.js"))) } })
+ }
+}
+
#[tokio::main]
async fn main() -> Result<()> {
let subscriber = FmtSubscriber::builder()
@@ -831,6 +876,7 @@ async fn main() -> Result<()> {
.route("/game/{gid}/judge", post(submit_judge))
.route("/game/{gid}/ws", get(game_ws))
.nest_service("/static", ServeDir::new("static"))
+ .nest_service("/dev-hr", hot_reload::router("static"))
.layer(CatchPanicLayer::custom(handle_panic))
.layer(
TraceLayer::new_for_http()