pagination: Directly pass list of interactions to render

This commit is contained in:
Natsu Kagami 2025-02-20 20:07:17 +01:00
parent 8c5135bfc9
commit 5fde2f343a
Signed by: nki
GPG key ID: 55A032EB38B49ADB
7 changed files with 59 additions and 47 deletions

View file

@ -222,9 +222,7 @@ pub async fn ranks(ctx: &Context, m: &Message) -> CommandResult {
)) ))
.build(); .build();
Ok(Some( Ok(Some(EditMessage::new().content(content).components(btns)))
EditMessage::new().content(content).components(vec![btns]),
))
}) })
}) })
.with_page_count(total_pages), .with_page_count(total_pages),
@ -390,9 +388,7 @@ pub(crate) async fn contest_rank_table(
.push_line(format!("Page **{}/{}**", page + 1, total_pages)) .push_line(format!("Page **{}/{}**", page + 1, total_pages))
.build(); .build();
Ok(Some( Ok(Some(EditMessage::new().content(content).components(btns)))
EditMessage::new().content(content).components(vec![btns]),
))
}) })
}) })
.with_page_count(total_pages), .with_page_count(total_pages),

View file

@ -77,9 +77,7 @@ async fn list(ctx: &Context, m: &Message, _: Args) -> CommandResult {
.push_line(format!("Page **{}/{}**", page + 1, pages)) .push_line(format!("Page **{}/{}**", page + 1, pages))
.build(); .build();
Ok(Some( Ok(Some(EditMessage::new().content(content).components(btns)))
EditMessage::new().content(content).components(vec![btns]),
))
}) })
}) })
.with_page_count(pages), .with_page_count(pages),

View file

@ -81,7 +81,7 @@ async fn message_command(
images.len(), images.len(),
images[page] images[page]
)) ))
.components(vec![btns]), .components(btns),
)) ))
} }
}) })

View file

@ -53,7 +53,7 @@ mod scores {
mod grid { mod grid {
use pagination::paginate_with_first_message; use pagination::paginate_with_first_message;
use serenity::all::GuildId; use serenity::all::{CreateActionRow, GuildId};
use serenity::builder::EditMessage; use serenity::builder::EditMessage;
use serenity::model::channel::Message; use serenity::model::channel::Message;
@ -97,6 +97,7 @@ mod scores {
page: u8, page: u8,
ctx: &Context, ctx: &Context,
msg: &Message, msg: &Message,
btns: Vec<CreateActionRow>,
) -> Result<Option<EditMessage>> { ) -> Result<Option<EditMessage>> {
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone(); let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
let page = page as usize; let page = page as usize;
@ -127,7 +128,12 @@ mod scores {
.footer(format!("Page {}/{}", page + 1, self.scores.len())) .footer(format!("Page {}/{}", page + 1, self.scores.len()))
.build() .build()
}) })
.components(vec![score_components(self.guild_id), self.pagination_row()]), .components(
vec![score_components(self.guild_id)]
.into_iter()
.chain(btns)
.collect(),
),
)) ))
} }
@ -141,6 +147,7 @@ mod scores {
use std::borrow::Cow; use std::borrow::Cow;
use pagination::paginate_with_first_message; use pagination::paginate_with_first_message;
use serenity::all::CreateActionRow;
use serenity::builder::EditMessage; use serenity::builder::EditMessage;
use serenity::model::channel::Message; use serenity::model::channel::Message;
@ -196,6 +203,7 @@ mod scores {
page: u8, page: u8,
ctx: &Context, ctx: &Context,
_: &Message, _: &Message,
btns: Vec<CreateActionRow>,
) -> Result<Option<EditMessage>> { ) -> Result<Option<EditMessage>> {
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone(); let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
@ -323,11 +331,7 @@ mod scores {
.push_line("[?] means pp was predicted by oppai-rs.") .push_line("[?] means pp was predicted by oppai-rs.")
.build(); .build();
Ok(Some( Ok(Some(EditMessage::new().content(content).components(btns)))
EditMessage::new()
.content(content)
.components(vec![self.pagination_row()]),
))
} }
fn len(&self) -> Option<usize> { fn len(&self) -> Option<usize> {
@ -339,7 +343,7 @@ mod scores {
mod beatmapset { mod beatmapset {
use serenity::{ use serenity::{
all::{CreateButton, GuildId}, all::{CreateActionRow, CreateButton, GuildId},
builder::{CreateEmbedFooter, EditMessage}, builder::{CreateEmbedFooter, EditMessage},
model::channel::{Message, ReactionType}, model::channel::{Message, ReactionType},
}; };
@ -433,6 +437,7 @@ mod beatmapset {
page: u8, page: u8,
ctx: &Context, ctx: &Context,
msg: &Message, msg: &Message,
btns: Vec<CreateActionRow>,
) -> Result<Option<EditMessage>> { ) -> Result<Option<EditMessage>> {
let page = page as usize; let page = page as usize;
if page == self.maps.len() { if page == self.maps.len() {
@ -442,7 +447,7 @@ mod beatmapset {
&self.maps[..], &self.maps[..],
self.mode, self.mode,
)) ))
.components(vec![self.pagination_row()]), .components(btns),
)); ));
} }
if page > self.maps.len() { if page > self.maps.len() {
@ -489,7 +494,7 @@ mod beatmapset {
)) ))
}) })
) )
.components(vec![beatmap_components(map.mode, self.guild_id), self.pagination_row()]), .components(std::iter::once(beatmap_components(map.mode, self.guild_id)).chain(btns).collect()),
)) ))
} }

View file

@ -199,7 +199,7 @@ pub fn dot_osu_hook<'a>(
let mut edit = EditMessage::new() let mut edit = EditMessage::new()
.content(format!("Attached beatmaps ({}/{})", page + 1, embed_len)) .content(format!("Attached beatmaps ({}/{})", page + 1, embed_len))
.embed(embed.clone()) .embed(embed.clone())
.components(vec![btns]); .components(btns);
for att in attachments { for att in attachments {
edit = edit.new_attachment(att.clone()); edit = edit.new_attachment(att.clone());
} }

View file

@ -317,9 +317,7 @@ where
last_update.format("<t:%s:R>"), last_update.format("<t:%s:R>"),
)) ))
.build(); .build();
Ok(Some( Ok(Some(EditMessage::new().content(content).components(btns)))
EditMessage::new().content(content).components(vec![btns]),
))
}) })
}) })
.with_page_count(total_pages), .with_page_count(total_pages),
@ -719,9 +717,7 @@ pub async fn display_rankings_table(
)) ))
.build(); .build();
Ok(Some( Ok(Some(EditMessage::new().content(content).components(btns)))
EditMessage::new().content(content).components(vec![btns]),
))
}) })
}) })
.with_page_count(total_pages), .with_page_count(total_pages),

View file

@ -1,4 +1,4 @@
use crate::{Context, Result}; use crate::{Context, OkPrint, Result};
use futures_util::future::Future; use futures_util::future::Future;
use serenity::{ use serenity::{
all::{ all::{
@ -30,13 +30,17 @@ const FAST_PREV: &str = "youmubot_pagination_fast_prev";
pub trait Paginate: Send + Sized { pub trait Paginate: Send + Sized {
/// Render the given page. /// Render the given page.
/// Remember to add the [[interaction_buttons]] as an action row! /// Remember to add the [[interaction_buttons]] as an action row!
async fn render(&mut self, page: u8, ctx: &Context, m: &Message) async fn render(
-> Result<Option<EditMessage>>; &mut self,
page: u8,
ctx: &Context,
m: &Message,
btns: Vec<CreateActionRow>,
) -> Result<Option<EditMessage>>;
/// The [[CreateActionRow]] for pagination. // /// The [[CreateActionRow]] for pagination.
fn pagination_row(&self) -> CreateActionRow { // fn pagination_row(&self) -> CreateActionRow {
CreateActionRow::Buttons(self.interaction_buttons()) // }
}
/// A list of buttons to create that would interact with pagination logic. /// A list of buttons to create that would interact with pagination logic.
fn interaction_buttons(&self) -> Vec<CreateButton> { fn interaction_buttons(&self) -> Vec<CreateButton> {
@ -84,7 +88,18 @@ pub async fn do_render(
ctx: &Context, ctx: &Context,
m: &mut Message, m: &mut Message,
) -> Result<bool> { ) -> Result<bool> {
if let Some(edit) = p.render(page, ctx, m).await? { let btns = vec![CreateActionRow::Buttons(p.interaction_buttons())];
do_render_with_btns(p, page, ctx, m, btns).await
}
async fn do_render_with_btns(
p: &mut impl Paginate,
page: u8,
ctx: &Context,
m: &mut Message,
btns: Vec<CreateActionRow>,
) -> Result<bool> {
if let Some(edit) = p.render(page, ctx, m, btns).await? {
m.edit(ctx, edit).await?; m.edit(ctx, edit).await?;
Ok(true) Ok(true)
} else { } else {
@ -97,7 +112,7 @@ pub fn paginate_from_fn(
u8, u8,
&'m Context, &'m Context,
&'m Message, &'m Message,
CreateActionRow, Vec<CreateActionRow>,
) -> std::pin::Pin< ) -> std::pin::Pin<
Box<dyn Future<Output = Result<Option<EditMessage>>> + Send + 'm>, Box<dyn Future<Output = Result<Option<EditMessage>>> + Send + 'm>,
> + Send, > + Send,
@ -132,11 +147,12 @@ impl<Inner: Paginate> Paginate for WithPageCount<Inner> {
page: u8, page: u8,
ctx: &Context, ctx: &Context,
m: &Message, m: &Message,
btns: Vec<CreateActionRow>,
) -> Result<Option<EditMessage>> { ) -> Result<Option<EditMessage>> {
if page as usize >= self.page_count { if page as usize >= self.page_count {
return Ok(None); return Ok(None);
} }
self.inner.render(page, ctx, m).await self.inner.render(page, ctx, m, btns).await
} }
async fn handle_reaction( async fn handle_reaction(
@ -167,7 +183,7 @@ where
u8, u8,
&'m Context, &'m Context,
&'m Message, &'m Message,
CreateActionRow, Vec<CreateActionRow>,
) -> std::pin::Pin< ) -> std::pin::Pin<
Box<dyn Future<Output = Result<Option<EditMessage>>> + Send + 'm>, Box<dyn Future<Output = Result<Option<EditMessage>>> + Send + 'm>,
> + Send, > + Send,
@ -177,8 +193,8 @@ where
page: u8, page: u8,
ctx: &Context, ctx: &Context,
m: &Message, m: &Message,
btns: Vec<CreateActionRow>,
) -> Result<Option<EditMessage>> { ) -> Result<Option<EditMessage>> {
let btns = self.pagination_row();
self(page, ctx, m, btns).await self(page, ctx, m, btns).await
} }
} }
@ -249,6 +265,10 @@ pub async fn paginate_with_first_message(
} }
}; };
// Render one last time with no buttons
do_render_with_btns(&mut pager, page, ctx, &mut message, vec![])
.await
.pls_ok();
Paginator::pop(ctx, &message).await?; Paginator::pop(ctx, &message).await?;
res res
@ -279,14 +299,11 @@ pub async fn handle_pagination_reaction(
FAST_NEXT => (pages.unwrap() as u8 - 1).min(page + fast), FAST_NEXT => (pages.unwrap() as u8 - 1).min(page + fast),
_ => return Ok(page), _ => return Ok(page),
}; };
Ok( Ok(if do_render(pager, new_page, ctx, message).await? {
if let Some(edit) = pager.render(new_page, ctx, message).await? { new_page
message.edit(ctx, edit).await?; } else {
new_page page
} else { })
page
},
)
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]