use chrono_tz::Tz; use poise::{ serenity_prelude::{ CreateActionRow, CreateEmbed, CreateEmbedFooter, CreateSelectMenu, CreateSelectMenuKind, CreateSelectMenuOption, }, CreateReply, }; use serde::{Deserialize, Serialize}; use crate::{ component_models::{ pager::{DelPager, Pager}, ComponentDataModel, DelSelector, }, consts::{EMBED_DESCRIPTION_MAX_LENGTH, SELECT_MAX_ENTRIES, THEME_COLOR}, models::{reminder::Reminder, CtxData}, utils::{Extract, Recordable}, Context, Error, }; pub fn max_delete_page(reminders: &[Reminder], timezone: &Tz) -> usize { let mut rows = 0; let mut char_count = 0; reminders .iter() .enumerate() .map(|(count, reminder)| reminder.display_del(count, timezone)) .fold(1, |mut pages, reminder| { rows += 1; char_count += reminder.len(); if char_count > EMBED_DESCRIPTION_MAX_LENGTH || rows > SELECT_MAX_ENTRIES { rows = 1; char_count = reminder.len(); pages += 1; } pages }) } pub fn show_delete_page(reminders: &[Reminder], page: usize, timezone: Tz) -> CreateReply { let pager = DelPager::new(page, timezone); if reminders.is_empty() { let embed = CreateEmbed::new() .title("Delete Reminders") .description("No Reminders") .color(*THEME_COLOR); return CreateReply::default().embed(embed).components(vec![pager.create_button_row(0)]); } let pages = max_delete_page(reminders, &timezone); let mut page = page; if page >= pages { page = pages - 1; } let mut char_count = 0; let mut rows = 0; let mut skipped_rows = 0; let mut skipped_char_count = 0; let mut first_num = 0; let mut skipped_pages = 0; let (shown_reminders, display_vec): (Vec<&Reminder>, Vec) = reminders .iter() .enumerate() .map(|(count, reminder)| (reminder, reminder.display_del(count, &timezone))) .skip_while(|(_, p)| { first_num += 1; skipped_rows += 1; skipped_char_count += p.len(); if skipped_char_count > EMBED_DESCRIPTION_MAX_LENGTH || skipped_rows > SELECT_MAX_ENTRIES { skipped_rows = 1; skipped_char_count = p.len(); skipped_pages += 1; } skipped_pages < page }) .take_while(|(_, p)| { rows += 1; char_count += p.len(); char_count < EMBED_DESCRIPTION_MAX_LENGTH && rows <= SELECT_MAX_ENTRIES }) .unzip(); let display = display_vec.join("\n"); let del_selector = ComponentDataModel::DelSelector(DelSelector { page, timezone }); let embed = CreateEmbed::new() .title("Delete Reminders") .description(display) .footer(CreateEmbedFooter::new(format!("Page {} of {}", page + 1, pages))) .color(*THEME_COLOR); let select_menu = CreateSelectMenu::new( del_selector.to_custom_id(), CreateSelectMenuKind::String { options: shown_reminders .iter() .enumerate() .map(|(count, reminder)| { let c = reminder.display_content(); let description = if c.len() > 100 { format!( "{}...", reminder.display_content().chars().take(97).collect::() ) } else { c.to_string() }; CreateSelectMenuOption::new( (count + first_num).to_string(), reminder.id.to_string(), ) .description(description) }) .collect(), }, ); CreateReply::default() .embed(embed) .components(vec![pager.create_button_row(pages), CreateActionRow::SelectMenu(select_menu)]) } #[derive(Serialize, Deserialize, Extract)] pub struct Options; impl Recordable for Options { async fn run(self, ctx: Context<'_>) -> Result<(), Error> { let timezone = ctx.timezone().await; let reminders = Reminder::from_guild(&ctx, &ctx.data().database, ctx.guild_id(), ctx.author().id).await; let resp = show_delete_page(&reminders, 0, timezone); ctx.send(resp).await?; Ok(()) } } /// Delete reminders #[poise::command( slash_command, rename = "delete", identifying_name = "delete", default_member_permissions = "MANAGE_GUILD" )] pub async fn command(ctx: Context<'_>) -> Result<(), Error> { (Options {}).run(ctx).await }