Implémentation du menu déroulant pour les actions d'éléments dans le ServerDrawer

Ajout d'un menu déroulant avec les actions 'Ajouter à la queue' et 'Ajouter après' pour les éléments dans le ServerDrawer.

- Remplacement des fonctions handlePlayItem et handleQueueItem par des fonctions plus spécifiques (handlePlayItem, handleAddToQueue, handleAddAfterCurrent)
- Ajout d'une gestion du menu déroulant avec ouverture/fermeture
- Intégration du composant useRenderers pour les actions de lecture
- Mise à jour des styles pour le menu déroulant avec animations
- Correction de l'alignement du backdrop sur mobile
- Amélioration des transitions et animations du drawer
- Ajout de la fonction addAfterCurrent dans l'API et le control point
- Support de l'ajout d'éléments après le morceau actuel dans les queues
This commit is contained in:
2026-01-09 14:30:53 +01:00
parent d708bb6035
commit 38f49d4439
11 changed files with 950 additions and 492 deletions

View File

@@ -38,7 +38,7 @@ use crate::{PlaybackItem, QueueSnapshot, errors::ControlPointError};
pub enum EnqueueMode {
/// Append new items at the end of the queue.
AppendToEnd,
/// Insert new items immediately after the current index
/// Insert new items immediately after the current playing index
/// (or at the beginning if there is no current index).
InsertAfterCurrent,
/// Replace the whole queue with the new items.
@@ -161,7 +161,7 @@ pub trait QueueBackend {
/// Returns the current item (or the first pending item if no index is set)
/// along with the count of remaining items.
fn peek_current(&self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
fn peek_current(&mut self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
let snapshot = self.queue_snapshot()?;
let QueueSnapshot {
items,

View File

@@ -15,7 +15,6 @@
//! - maintains a `current_index`,
//! - never starts playback (transport control is handled elsewhere).
use crate::{
DeviceId, DeviceIdentity, RendererInfo,
errors::ControlPointError,
@@ -72,27 +71,16 @@ impl QueueBackend for InternalQueue {
}
fn position_to_id(&self, id: usize) -> Result<u32, ControlPointError> {
u32::try_from(id).map_err(|_| {
ControlPointError::QueueError(format!(
"Position {} exceeds u32::MAX",
id
))
})
u32::try_from(id)
.map_err(|_| ControlPointError::QueueError(format!("Position {} exceeds u32::MAX", id)))
}
fn current_track(&self) -> Result<Option<u32>, ControlPointError> {
match self.current_index {
None => Ok(None),
Some(i) => {
u32::try_from(i)
.map(Some)
.map_err(|_| {
ControlPointError::QueueError(format!(
"Current index {} exceeds u32::MAX",
i
))
})
}
Some(i) => u32::try_from(i).map(Some).map_err(|_| {
ControlPointError::QueueError(format!("Current index {} exceeds u32::MAX", i))
}),
}
}
@@ -142,16 +130,14 @@ impl QueueBackend for InternalQueue {
Ok(())
}
fn sync_queue(
&mut self,
items: Vec<PlaybackItem>
) -> Result<(), ControlPointError> {
fn sync_queue(&mut self, items: Vec<PlaybackItem>) -> Result<(), ControlPointError> {
if items.is_empty() {
return self.replace_queue(Vec::new(), None);
}
// Récupérer l'item actuel
let current = self.current_index
let current = self
.current_index
.and_then(|idx| self.items.get(idx).map(|item| (idx, item.uri.clone())));
if let Some((_current_idx, current_uri)) = current {
@@ -187,7 +173,8 @@ impl QueueBackend for InternalQueue {
self.items.extend(items);
}
EnqueueMode::InsertAfterCurrent => {
let insert_pos = self.current_index
let insert_pos = self
.current_index
.map(|i| (i + 1).min(self.items.len()))
.unwrap_or(0);
@@ -242,7 +229,7 @@ impl QueueBackend for InternalQueue {
Ok(items)
}
fn peek_current(&self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
fn peek_current(&mut self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
if self.items.is_empty() {
return Ok(None);
}
@@ -250,7 +237,11 @@ impl QueueBackend for InternalQueue {
let len = self.items.len();
let (item, resolved_index) = match self.current_index {
Some(idx) if idx < len => (self.items.get(idx).cloned(), Some(idx)),
_ => (self.items.first().cloned(), None),
_ => {
// Si current_index est None ou invalide, initialiser à 0
self.current_index = Some(0);
(self.items.first().cloned(), Some(0))
}
};
let item = match item {

View File

@@ -156,7 +156,7 @@ impl QueueBackend for MusicQueue {
}
}
fn peek_current(&self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
fn peek_current(&mut self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
match self {
MusicQueue::Internal(q) => q.peek_current(),
MusicQueue::OpenHome(q) => q.peek_current(),

View File

@@ -778,7 +778,7 @@ impl QueueBackend for OpenHomeQueue {
}
/// Optimized peek_current: use primitives instead of full snapshot.
fn peek_current(&self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
fn peek_current(&mut self) -> Result<Option<(PlaybackItem, usize)>, ControlPointError> {
let len = self.len()?;
if len == 0 {
return Ok(None);