mini-player, header: make it persistent, add music page button and check current route

This commit is contained in:
Agahnim 2026-03-20 14:48:52 +01:00
parent d06922b584
commit 5d32091553
11 changed files with 78 additions and 52 deletions

View file

@ -2,7 +2,7 @@ use std::sync::Arc;
use agahnim_web_v2::{
domain::AppState,
templates::{home::home, miniplayer::miniplayer, music::music, notfound::notfound},
templates::{home::home, music::music, notfound::notfound},
};
use axum::{Router, routing::get};
use tower_http::services::ServeDir;
@ -16,7 +16,6 @@ async fn main() {
let app = Router::new()
.route("/", get(home))
.route("/music", get(music))
.route("/miniplayer", get(miniplayer))
.nest_service("/static", ServeDir::new("static"))
.fallback(notfound)
.with_state(state);

View file

@ -1,21 +1,33 @@
use askama::Template;
use axum::{
extract::State,
http::HeaderMap,
response::{Html, IntoResponse},
};
use std::sync::Arc;
use crate::domain::AppState;
#[derive(Template)]
#[template(path = "home.html")]
struct HomeTemplate;
struct HomeTemplate {
tracks: Vec<crate::domain::Track>,
}
#[derive(Template)]
#[template(path = "partials/home.html")]
struct HomePartialTemplate;
pub async fn home(headers: HeaderMap) -> impl IntoResponse {
pub async fn home(headers: HeaderMap, state: State<Arc<AppState>>) -> impl IntoResponse {
if headers.contains_key("hx-request") {
Html(HomePartialTemplate.render().unwrap())
} else {
Html(HomeTemplate.render().unwrap())
Html(
HomeTemplate {
tracks: state.mp_tracks.clone(),
}
.render()
.unwrap(),
)
}
}

View file

@ -1,24 +0,0 @@
use std::sync::Arc;
use askama::Template;
use axum::{
extract::State,
response::{Html, IntoResponse},
};
use crate::domain::{AppState, Track};
#[derive(Template)]
#[template(path = "partials/miniplayer.html")]
struct MiniPlayerTemplate {
tracks: Vec<Track>,
}
pub async fn miniplayer(State(state): State<Arc<AppState>>) -> impl IntoResponse {
Html(
MiniPlayerTemplate {
tracks: state.mp_tracks.clone(),
}
.render()
.unwrap(),
)
}

View file

@ -1,4 +1,3 @@
pub mod home;
pub mod miniplayer;
pub mod music;
pub mod notfound;

View file

@ -1,21 +1,33 @@
use askama::Template;
use axum::{
extract::State,
http::HeaderMap,
response::{Html, IntoResponse},
};
use std::sync::Arc;
use crate::domain::AppState;
#[derive(Template)]
#[template(path = "music.html")]
struct MusicTemplate;
struct MusicTemplate {
tracks: Vec<crate::domain::Track>,
}
#[derive(Template)]
#[template(path = "partials/music.html")]
struct MusicPartialTemplate;
pub async fn music(headers: HeaderMap) -> impl IntoResponse {
pub async fn music(headers: HeaderMap, state: State<Arc<AppState>>) -> impl IntoResponse {
if headers.contains_key("hx-request") {
Html(MusicPartialTemplate.render().unwrap())
} else {
Html(MusicTemplate.render().unwrap())
Html(
MusicTemplate {
tracks: state.p_tracks.clone(),
}
.render()
.unwrap(),
)
}
}

View file

@ -111,4 +111,3 @@ function initMiniPlayer() {
}
initMiniPlayer();
document.addEventListener("htmx:afterSwap", initMiniPlayer);

View file

@ -79,13 +79,14 @@ navbar {
navbar-content {
display: flex;
gap: 1rem;
gap: 0.3rem;
label {
cursor: pointer;
padding: 4px 8px;
border: 2px solid black;
background: var(--win-bg-grey);
border-color: #ffffff #808080 #808080 #ffffff;
&:has(input:checked) {
background: #ff75a7;
@ -94,8 +95,9 @@ navbar {
}
&:active {
border-color: #808080 #ffffff #ffffff #808080;
border-color: #ffffff #808080 #808080 #ffffff;
padding: 5px 7px 3px 9px;
border-color: #808080 #ffffff #ffffff #808080;
}
&:hover {

View file

@ -12,20 +12,19 @@
<script src="/static/vendor/htmx.min.js"></script>
</head>
<body hx-boost="true" hx-target="#content">
<crt>
<body hx-boost="true" hx-target="#content" {% block bodyattrs %}{% endblock %}>
<persistent-ui>
{% include "partials/header.html" %}
<miniplayer-container id="miniplayer-container" hx-get="/miniplayer" hx-trigger="load" hx-swap="innerHTML"
hx-target="#miniplayer-container">
<miniplayer>
<p>Chargement...</p>
</miniplayer>
<miniplayer-container id="miniplayer-container">
{% block miniplayer %}{% endblock %}
</miniplayer-container>
</persistent-ui>
<main id="content">
{% block content %}{% endblock %}
</main>
<main id="content">
{% block content %}{% endblock %}
</main>
</crt>
<script src="/static/miniplayer.js"></script>
<script>
@ -37,11 +36,28 @@
}
}
updateMiniplayerVisibility();
function updateNavState() {
const path = window.location.pathname;
document.querySelectorAll('navbar input[type="radio"]').forEach(radio => {
radio.checked = false;
});
if (path === '/') {
const homeRadio = document.getElementById('nav-home');
if (homeRadio) homeRadio.checked = true;
} else if (path === '/music') {
const musicRadio = document.getElementById('nav-music');
if (musicRadio) musicRadio.checked = true;
}
}
document.body.addEventListener('htmx:afterSwap', updateMiniplayerVisibility);
updateMiniplayerVisibility();
updateNavState();
document.body.addEventListener('htmx:afterSwap', () => {
updateMiniplayerVisibility();
updateNavState();
});
</script>
</body>
</crt>
</html>

View file

@ -1,6 +1,9 @@
{% extends "base.html" %}
{% block content %}
{% include "partials/home.html" %}
{% endblock %}
{% block miniplayer %}
{% include "partials/miniplayer.html" %}
{% endblock %}

View file

@ -3,3 +3,7 @@
{% block content %}
{% include "partials/music.html" %}
{% endblock %}
{% block miniplayer %}
{% include "partials/miniplayer.html" %}
{% endblock %}

View file

@ -4,5 +4,9 @@
<input type="radio" name="nav" id="nav-home" checked>
<a href="/">Accueil</a>
</label>
<label>
<input type="radio" name="nav" id="nav-music">
<a href="/music">Music</a>
</label>
</navbar-content>
</navbar>