From 70f2389b17dcec097dfe0cacad2618cb96e61f0f Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Sat, 17 Jan 2026 18:35:19 +0100 Subject: [PATCH 1/2] Move configuration files and add large cover feature implementation Move config files from Todo to ToDiscuss directory Add implementation details for large cover feature: - Make metadata visible by default when clicking album cover - Add progress bar with draggable thumb - Implement glass metal style overlay with 10-20% transparency - Address closing button positioning issues for mobile navigation - Improve drawer navigation for mobile users --- Blackboard/{Todo => ToDiscuss}/config_ext.md | 0 Blackboard/{Todo => ToDiscuss}/music_source.md | 0 Blackboard/Todo/large_cover.md | 11 +++++++++++ 3 files changed, 11 insertions(+) rename Blackboard/{Todo => ToDiscuss}/config_ext.md (100%) rename Blackboard/{Todo => ToDiscuss}/music_source.md (100%) create mode 100644 Blackboard/Todo/large_cover.md diff --git a/Blackboard/Todo/config_ext.md b/Blackboard/ToDiscuss/config_ext.md similarity index 100% rename from Blackboard/Todo/config_ext.md rename to Blackboard/ToDiscuss/config_ext.md diff --git a/Blackboard/Todo/music_source.md b/Blackboard/ToDiscuss/music_source.md similarity index 100% rename from Blackboard/Todo/music_source.md rename to Blackboard/ToDiscuss/music_source.md diff --git a/Blackboard/Todo/large_cover.md b/Blackboard/Todo/large_cover.md new file mode 100644 index 00000000..d5ca4c3a --- /dev/null +++ b/Blackboard/Todo/large_cover.md @@ -0,0 +1,11 @@ +**Il faut suivre les instructions générales placées dans le fichier : Blackboard/Rules.md** + +**Crate concernée pmoapp/webapp** + +Dans le composant de l'interface graphique du control point affichant la couverture de l'album en grand. + +**Point 1 :** +Quand on click sur l'image de couverture de l'album, elle s'agrandit pour prendre tout l'espace. Par défaut les métadonnées sont cachées et si on clique sur l'écran, il y a un effet toggle, un coup je les vois, un coup je les vois pas. Il faudrait que par défaut elles soient visible. Et peut-être qu'on pourrait juste poser la progress bar. Avec le thumb pour le faire déplacer si on a envie. J'imagine bien le tout dans une petite boîte avec un fond gris relativement transparent genre 10% ou 20% et bordé d'un petit effet d'ombre glass metal À la macOS, comme d'habitude. + +**Point 2 :** +Il y a un problème avec le bouton de fermeture en haut à gauche qui n'est pas super cool par rapport à une navigation sur un portable où on a les doigts plutôt en bas de l'écran. D'ailleurs c'est la même chose pour les drawers gauche et droite qui permettent de naviguer dans les serveurs. From 39dbf56416cde87420b09299dc2a8c0f5d18cf66 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Sat, 17 Jan 2026 19:01:12 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Am=C3=A9lioration=20de=20l'overlay=20de=20c?= =?UTF-8?q?ouverture=20d'album?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Modifications principales - Les métadonnées sont maintenant visibles par défaut à l'ouverture de l'overlay - Ajout d'une barre de progression interactive dans le panneau des métadonnées (support souris et tactile) - Style glassmorphism appliqué à l'overlay (fond transparent 35%, blur 30px, bordure subtile) - Panneau réduit à 70% de largeur pour un look plus élégant - Fermeture ergonomique sur mobile via swipe down (seuil 100px) avec feedback visuel - Bouton X conservé pour desktop - Blocage du pull-to-refresh Android ### Corrections supplémentaires - Support tactile de la barre de progression principale (hors overlay) - Utilisation de `100dvh` au lieu de `100vh` pour compatibilité avec les barres de navigation mobiles - Corrections TypeScript pour les événements tactiles --- Blackboard/Done/large_cover.md | 63 +++ Blackboard/Report/large_cover.md | 89 ++++ Blackboard/Todo/large_cover.md | 11 - Cargo.lock | 2 +- PMOMusic/Cargo.toml | 2 +- .../components/pmocontrol/CurrentTrack.vue | 380 +++++++++++++++++- version.txt | 2 +- 7 files changed, 517 insertions(+), 32 deletions(-) create mode 100644 Blackboard/Done/large_cover.md create mode 100644 Blackboard/Report/large_cover.md delete mode 100644 Blackboard/Todo/large_cover.md diff --git a/Blackboard/Done/large_cover.md b/Blackboard/Done/large_cover.md new file mode 100644 index 00000000..5291f083 --- /dev/null +++ b/Blackboard/Done/large_cover.md @@ -0,0 +1,63 @@ +# Tâche : Amélioration de l'overlay de couverture d'album + +**Crate concernée** : pmoapp/webapp + +**Statut** : Terminée + +--- + +## Demande initiale + +### Point 1 : Métadonnées et progress bar +Quand on clique sur l'image de couverture de l'album, elle s'agrandit pour prendre tout l'espace. Par défaut les métadonnées sont cachées et si on clique sur l'écran, il y a un effet toggle. Il fallait que par défaut elles soient visibles, avec une progress bar draggable dans une petite boîte au fond gris transparent avec un effet glass metal à la macOS. + +### Point 2 : Ergonomie mobile +Le bouton de fermeture en haut à droite n'était pas ergonomique sur mobile où les doigts sont plutôt en bas de l'écran. + +--- + +## Synthèse des modifications + +### Fichier modifié +- `pmoapp/webapp/src/components/pmocontrol/CurrentTrack.vue` + +### Point 1 : Résolu +- Métadonnées visibles par défaut à l'ouverture +- Progress bar interactive ajoutée dans le panneau des métadonnées (souris + tactile) +- Style glassmorphism : fond transparent 35%, blur 30px, bordure subtile, ombre +- Panneau réduit à 70% de largeur pour un look plus élégant + +### Point 2 : Résolu +- **Swipe down** pour fermer l'overlay sur tactile (seuil 100px) +- Feedback visuel pendant le geste (translation + fade) +- Bouton X conservé pour desktop +- Blocage du pull-to-refresh Android (`overscroll-behavior: contain`) + +### Corrections additionnelles +- Support tactile de la progress bar principale (hors overlay) +- Utilisation de `100dvh` au lieu de `100vh` pour compatibilité avec les barres de navigation mobiles (Brave, Safari, Chrome) +- Corrections TypeScript pour les événements tactiles + +--- + +## Éléments techniques clés + +### Swipe down +```typescript +const swipeStartY = ref(0); +const swipeCurrentY = ref(0); +const isSwiping = ref(false); +const swipeThreshold = 100; +``` +Listeners ajoutés sur `document` pour capturer le mouvement même sur les éléments enfants. + +### CSS mobile +```css +.cover-overlay { + overscroll-behavior: contain; + touch-action: none; +} +.cover-overlay-content { + height: calc(100dvh - 32px); +} +``` diff --git a/Blackboard/Report/large_cover.md b/Blackboard/Report/large_cover.md new file mode 100644 index 00000000..688c1f7b --- /dev/null +++ b/Blackboard/Report/large_cover.md @@ -0,0 +1,89 @@ +# Rapport : Amélioration de l'overlay de couverture d'album + +## Résumé + +Amélioration de l'interface de l'overlay affichant la couverture d'album en grand dans le Control Point PMOMusic, avec focus sur l'ergonomie mobile. + +## Fichier modifié + +- `pmoapp/webapp/src/components/pmocontrol/CurrentTrack.vue` + +## Modifications effectuées + +### Point 1 : Métadonnées visibles par défaut + Progress bar + +**Problème initial** : Les métadonnées étaient cachées par défaut à l'ouverture de l'overlay. + +**Solutions implémentées** : + +1. **Métadonnées visibles par défaut** : `showMetadata.value = true` dans `openCoverOverlay()` + +2. **Ajout de la progress bar** dans le panneau des métadonnées : + - Barre de progression interactive avec thumb draggable + - Affichage temps écoulé / durée totale + - Support souris et tactile complet + - Style glassmorphism (fond transparent 35%, blur, bordure subtile) + +3. **Panneau plus discret** : + - Transparence augmentée (35% au lieu de 60%) + - Largeur réduite (70% de l'écran avec `left: 15%; right: 15%`) + +### Point 2 : Fermeture ergonomique sur mobile + +**Problème initial** : Bouton X en haut à droite difficile d'accès au pouce. + +**Solutions implémentées** : + +1. **Swipe down pour fermer** : + - Geste naturel vers le bas pour fermer l'overlay + - Feedback visuel (translation + diminution opacité) + - Seuil de 100px pour déclencher la fermeture + - Listeners sur `document` pour capturer le mouvement même sur les éléments enfants + +2. **Bouton X conservé** pour desktop/souris + +3. **Blocage du pull-to-refresh Android** : + - `overscroll-behavior: contain` + - `touch-action: none` sur l'overlay + +### Corrections supplémentaires + +1. **Support tactile progress bar principale** : Ajout de `@touchstart` sur la progress bar hors overlay + +2. **Compatibilité viewport mobile** : Remplacement de `100vh` par `100dvh` (dynamic viewport height) pour éviter que la barre du navigateur masque le contenu + +3. **Corrections TypeScript** : Vérifications `if (!touch) return` pour les événements tactiles + +## Détails techniques + +### Nouvelles variables réactives + +```typescript +const swipeStartY = ref(0); +const swipeCurrentY = ref(0); +const isSwiping = ref(false); +const swipeThreshold = 100; +``` + +### Nouvelles fonctions + +- `handleOverlayTouchStart()` : Initialise le swipe et ajoute les listeners document +- `handleSwipeTouchMove()` : Suit le mouvement du doigt +- `handleSwipeTouchEnd()` : Ferme si seuil atteint, nettoie les listeners +- `handleOverlayProgressBarTouchStart()` : Gestion tactile du seek (réutilisée par les deux progress bars) +- `swipeOffset` / `swipeOpacity` : Computed pour le feedback visuel + +### Nouveaux styles CSS + +- `.overlay-metadata-text` : Conteneur du texte +- `.overlay-progress-section` : Section progress bar +- `.overlay-progress-bar` / `-fill` / `-thumb` : Style glassmorphism +- `.overlay-time-display` : Affichage temps +- `.cover-overlay-content.swiping` : État pendant le swipe +- `overscroll-behavior: contain` + `touch-action: none` : Blocage pull-to-refresh +- `100dvh` : Viewport dynamique pour mobile + +### Responsive + +- Mobile (< 768px) : Progress bar 10px, thumb 28px +- Mode kiosque (800x600) : Progress bar 6px diff --git a/Blackboard/Todo/large_cover.md b/Blackboard/Todo/large_cover.md deleted file mode 100644 index d5ca4c3a..00000000 --- a/Blackboard/Todo/large_cover.md +++ /dev/null @@ -1,11 +0,0 @@ -**Il faut suivre les instructions générales placées dans le fichier : Blackboard/Rules.md** - -**Crate concernée pmoapp/webapp** - -Dans le composant de l'interface graphique du control point affichant la couverture de l'album en grand. - -**Point 1 :** -Quand on click sur l'image de couverture de l'album, elle s'agrandit pour prendre tout l'espace. Par défaut les métadonnées sont cachées et si on clique sur l'écran, il y a un effet toggle, un coup je les vois, un coup je les vois pas. Il faudrait que par défaut elles soient visible. Et peut-être qu'on pourrait juste poser la progress bar. Avec le thumb pour le faire déplacer si on a envie. J'imagine bien le tout dans une petite boîte avec un fond gris relativement transparent genre 10% ou 20% et bordé d'un petit effet d'ombre glass metal À la macOS, comme d'habitude. - -**Point 2 :** -Il y a un problème avec le bouton de fermeture en haut à gauche qui n'est pas super cool par rapport à une navigation sur un portable où on a les doigts plutôt en bas de l'écran. D'ailleurs c'est la même chose pour les drawers gauche et droite qui permettent de naviguer dans les serveurs. diff --git a/Cargo.lock b/Cargo.lock index 4497244d..f0a2c995 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "PMOMusic" -version = "0.3.8" +version = "0.3.9" dependencies = [ "axum 0.8.7", "console-subscriber", diff --git a/PMOMusic/Cargo.toml b/PMOMusic/Cargo.toml index 71f7bba4..075035f5 100644 --- a/PMOMusic/Cargo.toml +++ b/PMOMusic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "PMOMusic" -version = "0.3.8" +version = "0.3.9" edition = "2024" [dependencies] diff --git a/pmoapp/webapp/src/components/pmocontrol/CurrentTrack.vue b/pmoapp/webapp/src/components/pmocontrol/CurrentTrack.vue index 548bd5a0..a6f3660d 100644 --- a/pmoapp/webapp/src/components/pmocontrol/CurrentTrack.vue +++ b/pmoapp/webapp/src/components/pmocontrol/CurrentTrack.vue @@ -24,7 +24,7 @@ const showMetadata = ref(false); function openCoverOverlay() { if (hasCover.value) { showCoverOverlay.value = true; - showMetadata.value = false; // Reset metadata visibility when opening + showMetadata.value = true; // Afficher les métadonnées par défaut } } @@ -186,6 +186,148 @@ async function handleProgressBarClick(event: MouseEvent) { isSeeking.value = false; } } + +// Fonctions pour la progress bar de l'overlay (réutilisent la même logique) +function handleOverlayProgressBarClick(event: MouseEvent) { + handleProgressBarClick(event); +} + +function handleOverlayProgressBarMouseDown(event: MouseEvent) { + handleProgressBarMouseDown(event); +} + +// Gestion tactile pour la progress bar de l'overlay +function handleOverlayProgressBarTouchStart(event: TouchEvent) { + const duration = state.value?.duration_ms; + if (!duration || duration === 0 || isSeeking.value) return; + + const progressBar = event.currentTarget as HTMLElement; + const touch = event.touches[0]; + if (!touch) return; + + isDragging.value = true; + dragProgress.value = calculateProgressFromX(touch.clientX, progressBar); + + const handleTouchMove = (e: TouchEvent) => { + if (!isDragging.value) return; + const moveTouch = e.touches[0]; + if (!moveTouch) return; + dragProgress.value = calculateProgressFromX( + moveTouch.clientX, + progressBar, + ); + }; + + const handleTouchEnd = async (e: TouchEvent) => { + if (!isDragging.value) return; + + const endTouch = e.changedTouches[0]; + if (!endTouch) return; + + const finalPercent = calculateProgressFromX( + endTouch.clientX, + progressBar, + ); + const newPositionMs = (finalPercent / 100) * duration; + const newPositionSeconds = Math.floor(newPositionMs / 1000); + + dragProgress.value = finalPercent; + seekTargetMs.value = newPositionMs; + isDragging.value = false; + + try { + isSeeking.value = true; + await api.seekTo(props.rendererId, newPositionSeconds); + } catch (error) { + uiStore.notifyError( + `Impossible de seek: ${error instanceof Error ? error.message : "Erreur inconnue"}`, + ); + seekTargetMs.value = null; + dragProgress.value = 0; + } finally { + isSeeking.value = false; + } + + document.removeEventListener("touchmove", handleTouchMove); + document.removeEventListener("touchend", handleTouchEnd); + }; + + document.addEventListener("touchmove", handleTouchMove, { passive: true }); + document.addEventListener("touchend", handleTouchEnd); +} + +// Gestion du swipe down pour fermer l'overlay +const swipeStartY = ref(0); +const swipeCurrentY = ref(0); +const isSwiping = ref(false); +const swipeThreshold = 100; // Distance minimale pour fermer + +function handleOverlayTouchStart(event: TouchEvent) { + const touch = event.touches[0]; + if (!touch) return; + swipeStartY.value = touch.clientY; + swipeCurrentY.value = touch.clientY; + isSwiping.value = true; + + // Ajouter les listeners sur le document pour capturer tous les mouvements + document.addEventListener("touchmove", handleSwipeTouchMove, { + passive: true, + }); + document.addEventListener("touchend", handleSwipeTouchEnd); + document.addEventListener("touchcancel", handleSwipeTouchEnd); +} + +function handleSwipeTouchMove(event: TouchEvent) { + if (!isSwiping.value) return; + const touch = event.touches[0]; + if (!touch) return; + swipeCurrentY.value = touch.clientY; +} + +function handleSwipeTouchEnd() { + if (!isSwiping.value) return; + + const swipeDistance = swipeCurrentY.value - swipeStartY.value; + + // Si swipe vers le bas suffisant, fermer l'overlay + if (swipeDistance > swipeThreshold) { + closeCoverOverlay(); + } + + isSwiping.value = false; + swipeStartY.value = 0; + swipeCurrentY.value = 0; + + // Retirer les listeners + document.removeEventListener("touchmove", handleSwipeTouchMove); + document.removeEventListener("touchend", handleSwipeTouchEnd); + document.removeEventListener("touchcancel", handleSwipeTouchEnd); +} + +// Ancienne fonction conservée pour compatibilité template (non utilisée) +function handleOverlayTouchMove(event: TouchEvent) { + handleSwipeTouchMove(event); +} + +function handleOverlayTouchEnd() { + handleSwipeTouchEnd(); +} + +// Calcul du décalage visuel pendant le swipe +const swipeOffset = computed(() => { + if (!isSwiping.value) return 0; + const offset = swipeCurrentY.value - swipeStartY.value; + // Ne permettre que le swipe vers le bas (positif) + return Math.max(0, offset); +}); + +// Opacité qui diminue pendant le swipe +const swipeOpacity = computed(() => { + if (!isSwiping.value) return 1; + const offset = swipeOffset.value; + // Réduire l'opacité progressivement + return Math.max(0.3, 1 - offset / 300); +});