Implement Pagination

This commit is contained in:
Natsu Kagami 2020-02-07 12:42:27 -05:00
parent ad0f798531
commit 2913d92682
Signed by: nki
GPG key ID: 73376E117CD20735
2 changed files with 121 additions and 0 deletions

View file

@ -3,11 +3,13 @@ use std::sync::Arc;
pub mod announcer;
pub mod args;
pub mod pagination;
pub mod reaction_watch;
pub mod setup;
pub use announcer::Announcer;
pub use args::Duration;
pub use pagination::Pagination;
pub use reaction_watch::{ReactionHandler, ReactionWatcher};
/// The global app data.

View file

@ -0,0 +1,119 @@
use crate::{Context, ReactionHandler, ReactionWatcher};
use serenity::{
builder::EditMessage,
framework::standard::{CommandError, CommandResult},
model::{
channel::{Message, Reaction, ReactionType},
id::ChannelId,
},
};
impl ReactionWatcher {
/// Start a pagination.
///
/// Takes a copy of Context (which you can `clone`), a pager (see "Pagination") and a target channel id.
/// Pagination will handle all events on adding/removing an "arrow" emoji (⬅️ and ➡️).
/// This is a blocking call - it will block the thread until duration is over.
pub fn paginate<T: Pagination>(
&self,
ctx: Context,
channel: ChannelId,
pager: T,
duration: std::time::Duration,
) -> CommandResult {
let handler = PaginationHandler::new(pager, ctx, channel)?;
self.handle_reactions(handler, duration)
}
}
/// Pagination allows the bot to display content in multiple pages.
///
/// You need to implement the "render_page" function, which takes a dummy content and
/// embed assigning function.
/// Pagination is automatically implemented for functions with the same signature as `render_page`.
///
/// Pages start at 0.
pub trait Pagination {
/// Render a page.
///
/// This would either create or edit a message, but you should not be worry about it.
fn render_page<'a>(
&self,
page: u8,
target: &'a mut EditMessage,
) -> (&'a mut EditMessage, CommandResult);
}
impl<T> Pagination for T
where
T: for<'a> Fn(u8, &'a mut EditMessage) -> (&'a mut EditMessage, CommandResult),
{
fn render_page<'a>(
&self,
page: u8,
target: &'a mut EditMessage,
) -> (&'a mut EditMessage, CommandResult) {
self(page, target)
}
}
struct PaginationHandler<T: Pagination> {
pager: T,
message: Message,
page: u8,
ctx: Context,
}
impl<T: Pagination> PaginationHandler<T> {
pub fn new(pager: T, mut ctx: Context, channel: ChannelId) -> Result<Self, CommandError> {
let message = channel.send_message(&mut ctx, |e| {
e.content("Youmu is loading the first page...")
})?;
// React to the message
message.react(&mut ctx, "⬅️")?;
message.react(&mut ctx, "➡️️")?;
let mut p = Self {
pager,
message: message.clone(),
page: 0,
ctx,
};
p.call_pager()?;
Ok(p)
}
}
impl<T: Pagination> PaginationHandler<T> {
/// Call the pager, log the error (if any).
fn call_pager(&mut self) -> CommandResult {
let mut res: CommandResult = Ok(());
let mut msg = self.message.clone();
msg.edit(&self.ctx, |e| {
let (e, r) = self.pager.render_page(self.page, e);
res = r;
e
})?;
res
}
}
impl<T: Pagination> ReactionHandler for PaginationHandler<T> {
fn handle_reaction(&mut self, reaction: &Reaction, _is_add: bool) -> CommandResult {
match &reaction.emoji {
ReactionType::Unicode(ref s) => match s.as_str() {
"" if self.page == 0 => return Ok(()),
"" => {
self.page -= 1;
self.call_pager()?;
}
"" => {
self.page += 1;
self.call_pager()?;
}
_ => (),
},
_ => (),
}
Ok(())
}
}