diff --git a/youmubot-osu/src/discord/commands.rs b/youmubot-osu/src/discord/commands.rs index 8b24a9a..c5bdf31 100644 --- a/youmubot-osu/src/discord/commands.rs +++ b/youmubot-osu/src/discord/commands.rs @@ -303,20 +303,14 @@ async fn handle_listing( cache::save_beatmap(&env, ctx.channel_id(), &beatmap).await?; } Nth::All => { - let reply = ctx - .clone() - .reply(format!( - "Here are the {} plays by {}!", - listing_kind, - user.mention() - )) - .await?; + let header = format!("Here are the {} plays by {}!", listing_kind, user.mention()); + let reply = ctx.clone().reply(&header).await?; style .display_scores( plays.try_collect::>().await?, ctx.clone().serenity_context(), ctx.guild_id(), - (reply, ctx), + (reply, ctx).with_header(header), ) .await?; } @@ -480,14 +474,12 @@ async fn check( scores.reverse(); } - let msg = ctx - .clone() - .reply(format!( - "Here are the plays by {} on {}!", - args.user.mention(), - display - )) - .await?; + let header = format!( + "Here are the plays by {} on {}!", + args.user.mention(), + display + ); + let msg = ctx.clone().reply(&header).await?; let style = style.unwrap_or(if scores.len() <= 5 { ScoreListStyle::Grid @@ -500,7 +492,7 @@ async fn check( scores, ctx.clone().serenity_context(), ctx.guild_id(), - (msg, ctx), + (msg, ctx).with_header(header), ) .await?; diff --git a/youmubot-osu/src/discord/display.rs b/youmubot-osu/src/discord/display.rs index 7918021..74e0566 100644 --- a/youmubot-osu/src/discord/display.rs +++ b/youmubot-osu/src/discord/display.rs @@ -2,6 +2,8 @@ pub use beatmapset::display_beatmapset; pub use scores::ScoreListStyle; mod scores { + use std::future::Future; + use poise::ChoiceParameter; use serenity::all::GuildId; @@ -41,7 +43,7 @@ mod scores { impl ScoreListStyle { pub async fn display_scores( self, - scores: Vec, + scores: impl Future>>, ctx: &Context, guild_id: Option, m: impl CanEdit, @@ -55,6 +57,8 @@ mod scores { } mod grid { + use std::future::Future; + use pagination::paginate_with_first_message; use serenity::all::{CreateActionRow, GuildId}; @@ -65,7 +69,7 @@ mod scores { use crate::models::Score; pub async fn display_scores_grid( - scores: Vec, + scores: impl Future>>, ctx: &Context, guild_id: Option, mut on: impl CanEdit, @@ -81,7 +85,7 @@ mod scores { paginate_with_first_message( Paginate { env, - scores, + scores: scores.await?, guild_id, channel_id, }, @@ -175,14 +179,22 @@ mod scores { .await?; return Ok(()); } + let header = on.headers().unwrap_or("").to_owned(); + let content = format!("{}\n\nPreparing file...", header); + let first_edit = on + .apply_edit(CreateReply::default().content(content)) + .map(|v| v.pls_ok()); + let p = Paginate { env: ctx.data.read().await.get::().unwrap().clone(), - header: on.get_message().await?.content.clone(), + header: header.clone(), scores, }; - let content = p.to_table(0, p.scores.len()).await; + let (_, content) = future::join(first_edit, p.to_table(0, p.scores.len())).await; on.apply_edit( - CreateReply::default().attachment(CreateAttachment::bytes(content, "table.txt")), + CreateReply::default() + .content(header) + .attachment(CreateAttachment::bytes(content, "table.md")), ) .await?; Ok(()) @@ -202,7 +214,7 @@ mod scores { paginate_with_first_message( Paginate { env: ctx.data.read().await.get::().unwrap().clone(), - header: on.get_message().await?.content.clone(), + header: on.headers().unwrap_or("").to_owned(), scores, }, ctx, diff --git a/youmubot-osu/src/discord/interaction.rs b/youmubot-osu/src/discord/interaction.rs index 253c946..b1900db 100644 --- a/youmubot-osu/src/discord/interaction.rs +++ b/youmubot-osu/src/discord/interaction.rs @@ -95,20 +95,21 @@ pub fn handle_check_button<'a>( return Ok(()); } + let header = format!( + "Here are the scores by [`{}`]() on {}!", + user.username, + user.id, + embed.mention() + ); comp.create_followup( &ctx, - CreateInteractionResponseFollowup::new().content(format!( - "Here are the scores by [`{}`]() on {}!", - user.username, - user.id, - embed.mention() - )), + CreateInteractionResponseFollowup::new().content(&header), ) .await?; let guild_id = comp.guild_id; ScoreListStyle::Grid - .display_scores(scores, &ctx, guild_id, (comp, ctx)) + .display_scores(scores, &ctx, guild_id, (comp, ctx).with_header(header)) .await .pls_ok(); Ok(()) diff --git a/youmubot-osu/src/discord/mod.rs b/youmubot-osu/src/discord/mod.rs index 62af747..4f36513 100644 --- a/youmubot-osu/src/discord/mod.rs +++ b/youmubot-osu/src/discord/mod.rs @@ -681,18 +681,14 @@ pub async fn recent(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu let plays = osu_client.user_recent(UserID::ID(user.id), |f| f.mode(mode)); match nth { Nth::All => { - let reply = msg - .reply( - ctx, - format!("Here are the recent plays by {}!", user.mention()), - ) - .await?; + let header = format!("Here are the recent plays by {}!", user.mention()); + let reply = msg.reply(ctx, &header).await?; style .display_scores( plays.try_collect::>().await?, ctx, reply.guild_id, - (reply, ctx), + (reply, ctx).with_header(header), ) .await?; } @@ -758,18 +754,14 @@ pub async fn pins(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult let plays = osu_client.user_pins(UserID::ID(user.id), |f| f.mode(mode)); match nth { Nth::All => { - let reply = msg - .reply( - ctx, - format!("Here are the pinned plays by `{}`!", user.username), - ) - .await?; + let header = format!("Here are the pinned plays by `{}`!", user.username); + let reply = msg.reply(ctx, &header).await?; style .display_scores( plays.try_collect::>().await?, ctx, reply.guild_id, - (reply, ctx), + (reply, ctx).with_header(header), ) .await?; } @@ -1017,18 +1009,14 @@ pub async fn check(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul msg.reply(&ctx, "No scores found").await?; return Ok(()); } - let reply = msg - .reply( - &ctx, - format!( - "Here are the scores by `{}` on {}!", - &user.username, - embed.mention() - ), - ) - .await?; + let header = format!( + "Here are the scores by `{}` on {}!", + &user.username, + embed.mention() + ); + let reply = msg.reply(&ctx, &header).await?; style - .display_scores(scores, ctx, msg.guild_id, (reply, ctx)) + .display_scores(scores, ctx, msg.guild_id, (reply, ctx).with_header(header)) .await?; Ok(()) @@ -1130,18 +1118,14 @@ pub async fn top(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult cache::save_beatmap(&env, msg.channel_id, &beatmap).await?; } Nth::All => { - let reply = msg - .reply( - &ctx, - format!("Here are the top plays by {}!", user.mention()), - ) - .await?; + let header = format!("Here are the top plays by {}!", user.mention()); + let reply = msg.reply(&ctx, &header).await?; style .display_scores( plays.try_collect::>().await?, ctx, msg.guild_id, - (reply, ctx), + (reply, ctx).with_header(header), ) .await?; } diff --git a/youmubot-prelude/src/pagination.rs b/youmubot-prelude/src/pagination.rs index f302bad..eea9b6f 100644 --- a/youmubot-prelude/src/pagination.rs +++ b/youmubot-prelude/src/pagination.rs @@ -14,9 +14,33 @@ const PREV: &str = "youmubot_pagination_prev"; const FAST_NEXT: &str = "youmubot_pagination_fast_next"; const FAST_PREV: &str = "youmubot_pagination_fast_prev"; -pub trait CanEdit: Send { +pub trait CanEdit: Send + Sized { fn get_message(&self) -> impl Future> + Send; fn apply_edit(&mut self, edit: CreateReply) -> impl Future> + Send; + + fn headers(&self) -> Option<&str> { + None + } + + fn with_header(self, header: String) -> impl CanEdit { + WithHeaders(self, header) + } +} + +struct WithHeaders(T, String); + +impl CanEdit for WithHeaders { + fn get_message(&self) -> impl Future> + Send { + self.0.get_message() + } + + fn apply_edit(&mut self, edit: CreateReply) -> impl Future> + Send { + self.0.apply_edit(edit) + } + + fn headers(&self) -> Option<&str> { + Some(&self.1) + } } impl<'a> CanEdit for (Message, &'a Context) {