From b90bfa6252c64389876ce4337e33f6f173534af7 Mon Sep 17 00:00:00 2001 From: ayamdobhal Date: Tue, 26 Nov 2024 04:51:57 +0530 Subject: [PATCH] feat(pagination): added pagination to queue --- src/commands/now.rs | 54 ++++++++++++++----------------- src/commands/skip.rs | 30 ++++++----------- src/models/mod.rs | 1 + src/models/pagination.rs | 70 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 51 deletions(-) create mode 100644 src/models/pagination.rs diff --git a/src/commands/now.rs b/src/commands/now.rs index b31bb74..400647b 100644 --- a/src/commands/now.rs +++ b/src/commands/now.rs @@ -1,12 +1,14 @@ use poise::CreateReply; -use serenity::all::{Colour, CreateEmbed}; +use serenity::all::{Colour, CreateEmbed, CreateEmbedFooter}; -use crate::commands::utils::Error; +use crate::{commands::utils::Error, models::pagination::PaginatedQueue}; use super::utils::Context; +// TODO: make a button to change pages instead of entering page number #[poise::command(prefix_command, aliases("queue"))] -pub async fn now(ctx: Context<'_>) -> Result<(), Error> { +pub async fn now(ctx: Context<'_>, n: Option) -> Result<(), Error> { + let n = n.unwrap_or(1); let (guild_id, channel_id) = { let guild = ctx.guild().expect("Guild only command"); let channel_id = guild @@ -57,36 +59,28 @@ pub async fn now(ctx: Context<'_>) -> Result<(), Error> { .await?; return Ok(()); } + let paginated_queue = PaginatedQueue::new(queue, len, n); + let pages = paginated_queue.total_pages(); + + if n > pages { + let embed = CreateEmbed::new() + .title(format!( + "❌ Number cannot be larger than total pages({})", + pages + )) + .color(Colour::from_rgb(255, 0, 0)); + ctx.send(CreateReply { + embeds: vec![embed], + ..Default::default() + }) + .await?; + return Ok(()); + } let embed = CreateEmbed::new() .title("📋 **Currently Playing**") .title("".to_string()) - .fields(queue.iter().enumerate().map(|(index, song)| { - if index == 0 { - ( - format!( - "{}. {} - {}[{}] ⬅️", - index + 1, - song.artist, - song.name, - song.duration - ), - "", - false, - ) - } else { - ( - format!( - "{}. {} - {}[{}]", - index + 1, - song.artist, - song.name, - song.duration - ), - "", - false, - ) - } - })) + .fields(paginated_queue.get_fields()) + .footer(CreateEmbedFooter::new(format!("Total Pages: {}", pages))) .color(Colour::from_rgb(0, 236, 255)); ctx.send(CreateReply { embeds: vec![embed], diff --git a/src/commands/skip.rs b/src/commands/skip.rs index 0c4fa1b..802ade3 100644 --- a/src/commands/skip.rs +++ b/src/commands/skip.rs @@ -1,7 +1,7 @@ use poise::CreateReply; use serenity::all::{Colour, CreateEmbed}; -use crate::{commands::utils::Error, state::Track}; +use crate::commands::utils::Error; use super::utils::Context; @@ -54,7 +54,6 @@ pub async fn skip(ctx: Context<'_>, n: Option) -> Result<(), Error> { n.unwrap_or(1) }; let k = &format!("{},{}", guild_id.get(), channel_id.get()); - let mut skipped_songs: Vec = vec![]; for _ in 0..n_times { queue.skip()?; let pop = { @@ -67,17 +66,13 @@ pub async fn skip(ctx: Context<'_>, n: Option) -> Result<(), Error> { .pop_front() }; if let None = pop { - let embed = - CreateEmbed::new() - .title(format!( - "⏩ Skipped {} {}", - n_times, - if n_times > 1 { "tracks" } else { "track" } - )) - .fields(skipped_songs.iter().enumerate().map(|(index, song)| { - (format!("{}. {}", index + 1, song.name), "", false) - })) - .color(Colour::from_rgb(0, 255, 0)); + let embed = CreateEmbed::new() + .title(format!( + "⏩ Skipped {} {}", + n_times, + if n_times > 1 { "tracks" } else { "track" } + )) + .color(Colour::from_rgb(0, 255, 0)); ctx.send(CreateReply { embeds: vec![embed], ..Default::default() @@ -111,7 +106,6 @@ pub async fn skip(ctx: Context<'_>, n: Option) -> Result<(), Error> { } return Ok(()); } - skipped_songs.push(pop.unwrap()); } let embed = CreateEmbed::new() .title(format!( @@ -119,12 +113,6 @@ pub async fn skip(ctx: Context<'_>, n: Option) -> Result<(), Error> { n_times, if n_times > 1 { "tracks" } else { "track" } )) - .fields( - skipped_songs - .iter() - .enumerate() - .map(|(index, song)| (format!("{}. {}", index + 1, song.name), "", false)), - ) .color(Colour::from_rgb(0, 255, 0)); ctx.send(CreateReply { embeds: vec![embed], @@ -146,12 +134,12 @@ pub async fn skip(ctx: Context<'_>, n: Option) -> Result<(), Error> { if let Some(next_track) = next_track { let embed = CreateEmbed::new() .title("**⏯️ Now Playing**") + .image(next_track.thumbnail) .field( next_track.artist, format!("{} [{}]", next_track.name, next_track.duration), true, ) - .image(next_track.thumbnail) .color(Colour::from_rgb(0, 255, 0)); ctx.send(CreateReply { embeds: vec![embed], diff --git a/src/models/mod.rs b/src/models/mod.rs index 16b75bc..f12fb8a 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1 +1,2 @@ +pub mod pagination; pub mod spotify; diff --git a/src/models/pagination.rs b/src/models/pagination.rs new file mode 100644 index 0000000..45ffe92 --- /dev/null +++ b/src/models/pagination.rs @@ -0,0 +1,70 @@ +use std::collections::VecDeque; + +use crate::state::Track; + +pub struct PaginatedQueue<'a> { + queue: &'a VecDeque, + page: usize, + limit: usize, + total: usize, +} + +impl<'a> PaginatedQueue<'a> { + pub fn new(queue: &'a VecDeque, total: usize, page: usize) -> Self { + Self { + queue, + page, + limit: 10, + total, + } + } + + fn start_idx(&self) -> usize { + (self.page - 1) * self.limit + } + + fn end_idx(&self) -> usize { + (self.start_idx() + self.limit).min(self.total) + } + + pub fn total_pages(&self) -> usize { + (self.total + self.limit - 1) / self.limit + } + + pub fn get_fields(&self) -> impl IntoIterator + '_ { + let start = self.start_idx(); + let end = self.end_idx(); + self.queue + .iter() + .skip(start) + .take(end - start) + .enumerate() + .map(move |(index, song)| { + if index == 0 { + ( + format!( + "{}. {} - {}[{}] ⬅️", + index + 1 + start, + song.artist, + song.name, + song.duration + ), + "".to_string(), + false, + ) + } else { + ( + format!( + "{}. {} - {}[{}]", + index + 1 + start, + song.artist, + song.name, + song.duration + ), + "".to_string(), + false, + ) + } + }) + } +}