pager improvements. deleting working
This commit is contained in:
parent
b2207e308a
commit
a9c91bee93
@ -1,26 +1,16 @@
|
|||||||
use std::{collections::HashMap, iter};
|
|
||||||
|
|
||||||
use chrono::offset::Utc;
|
use chrono::offset::Utc;
|
||||||
use chrono_tz::{Tz, TZ_VARIANTS};
|
use chrono_tz::{Tz, TZ_VARIANTS};
|
||||||
use levenshtein::levenshtein;
|
use levenshtein::levenshtein;
|
||||||
use regex::Regex;
|
|
||||||
use regex_command_attr::command;
|
use regex_command_attr::command;
|
||||||
use serenity::{
|
use serenity::{client::Context, model::misc::Mentionable};
|
||||||
client::Context,
|
|
||||||
model::{
|
|
||||||
channel::Message,
|
|
||||||
id::{ChannelId, MessageId, RoleId},
|
|
||||||
misc::Mentionable,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
component_models::{ComponentDataModel, Restrict},
|
component_models::{ComponentDataModel, Restrict},
|
||||||
consts::{REGEX_ALIAS, REGEX_COMMANDS, THEME_COLOR},
|
consts::THEME_COLOR,
|
||||||
framework::{
|
framework::{
|
||||||
CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue, PermissionLevel,
|
CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue, PermissionLevel,
|
||||||
},
|
},
|
||||||
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData, CtxData},
|
models::{channel_data::ChannelData, CtxData},
|
||||||
PopularTimezones, RegexFramework, SQLPool,
|
PopularTimezones, RegexFramework, SQLPool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
default::Default,
|
default::Default,
|
||||||
string::ToString,
|
string::ToString,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
@ -8,20 +7,14 @@ use std::{
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use num_integer::Integer;
|
use num_integer::Integer;
|
||||||
use regex_command_attr::command;
|
use regex_command_attr::command;
|
||||||
use serenity::{
|
use serenity::{client::Context, model::channel::Channel};
|
||||||
client::Context,
|
|
||||||
futures::StreamExt,
|
|
||||||
model::{
|
|
||||||
channel::{Channel, Message},
|
|
||||||
id::ChannelId,
|
|
||||||
interactions::message_component::ButtonStyle,
|
|
||||||
misc::Mentionable,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
check_subscription_on_message,
|
check_subscription_on_message,
|
||||||
component_models::{ComponentDataModel, LookPager, PageAction},
|
component_models::{
|
||||||
|
pager::{DelPager, LookPager, Pager},
|
||||||
|
ComponentDataModel, DelSelector,
|
||||||
|
},
|
||||||
consts::{
|
consts::{
|
||||||
EMBED_DESCRIPTION_MAX_LENGTH, REGEX_CHANNEL_USER, REGEX_NATURAL_COMMAND_1,
|
EMBED_DESCRIPTION_MAX_LENGTH, REGEX_CHANNEL_USER, REGEX_NATURAL_COMMAND_1,
|
||||||
REGEX_NATURAL_COMMAND_2, REGEX_REMIND_COMMAND, THEME_COLOR,
|
REGEX_NATURAL_COMMAND_2, REGEX_REMIND_COMMAND, THEME_COLOR,
|
||||||
@ -160,7 +153,7 @@ async fn offset(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args:
|
|||||||
let channels = guild
|
let channels = guild
|
||||||
.channels
|
.channels
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(channel_id, channel)| match channel {
|
.filter(|(_, channel)| match channel {
|
||||||
Channel::Guild(guild_channel) => guild_channel.is_text_based(),
|
Channel::Guild(guild_channel) => guild_channel.is_text_based(),
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
@ -333,30 +326,7 @@ async fn look(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: C
|
|||||||
.fold(0, |t, r| t + r.len())
|
.fold(0, |t, r| t + r.len())
|
||||||
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||||
|
|
||||||
let page_first = ComponentDataModel::LookPager(LookPager {
|
let pager = LookPager::new(flags, timezone);
|
||||||
flags: flags.clone(),
|
|
||||||
page: 0,
|
|
||||||
action: PageAction::First,
|
|
||||||
timezone,
|
|
||||||
});
|
|
||||||
let page_prev = ComponentDataModel::LookPager(LookPager {
|
|
||||||
flags: flags.clone(),
|
|
||||||
page: 0,
|
|
||||||
action: PageAction::Previous,
|
|
||||||
timezone,
|
|
||||||
});
|
|
||||||
let page_next = ComponentDataModel::LookPager(LookPager {
|
|
||||||
flags: flags.clone(),
|
|
||||||
page: 0,
|
|
||||||
action: PageAction::Next,
|
|
||||||
timezone,
|
|
||||||
});
|
|
||||||
let page_last = ComponentDataModel::LookPager(LookPager {
|
|
||||||
flags: flags.clone(),
|
|
||||||
page: 0,
|
|
||||||
action: PageAction::Last,
|
|
||||||
timezone,
|
|
||||||
});
|
|
||||||
|
|
||||||
invoke
|
invoke
|
||||||
.respond(
|
.respond(
|
||||||
@ -372,32 +342,9 @@ async fn look(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: C
|
|||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
})
|
})
|
||||||
.components(|comp| {
|
.components(|comp| {
|
||||||
comp.create_action_row(|row| {
|
pager.create_button_row(pages, comp);
|
||||||
row.create_button(|b| {
|
|
||||||
b.label("⏮️")
|
comp
|
||||||
.style(ButtonStyle::Primary)
|
|
||||||
.custom_id(page_first.to_custom_id())
|
|
||||||
.disabled(true)
|
|
||||||
})
|
|
||||||
.create_button(|b| {
|
|
||||||
b.label("◀️")
|
|
||||||
.style(ButtonStyle::Secondary)
|
|
||||||
.custom_id(page_prev.to_custom_id())
|
|
||||||
.disabled(true)
|
|
||||||
})
|
|
||||||
.create_button(|b| {
|
|
||||||
b.label("▶️")
|
|
||||||
.style(ButtonStyle::Secondary)
|
|
||||||
.custom_id(page_next.to_custom_id())
|
|
||||||
.disabled(pages == 1)
|
|
||||||
})
|
|
||||||
.create_button(|b| {
|
|
||||||
b.label("⏭️")
|
|
||||||
.style(ButtonStyle::Primary)
|
|
||||||
.custom_id(page_last.to_custom_id())
|
|
||||||
.disabled(pages == 1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -409,19 +356,92 @@ async fn look(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: C
|
|||||||
#[description("Delete reminders")]
|
#[description("Delete reminders")]
|
||||||
#[required_permissions(Managed)]
|
#[required_permissions(Managed)]
|
||||||
async fn delete(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
async fn delete(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
|
||||||
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
|
||||||
|
let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await;
|
||||||
|
|
||||||
let reminders = Reminder::from_guild(ctx, invoke.guild_id(), invoke.author_id()).await;
|
let reminders = Reminder::from_guild(ctx, invoke.guild_id(), invoke.author_id()).await;
|
||||||
|
|
||||||
let enumerated_reminders = reminders.iter().enumerate().map(|(count, reminder)| {
|
if reminders.is_empty() {
|
||||||
format!(
|
let _ = invoke
|
||||||
"**{}**: '{}' *<#{}>* at <t:{}>",
|
.respond(
|
||||||
count + 1,
|
ctx.http.clone(),
|
||||||
reminder.display_content(),
|
CreateGenericResponse::new().content("No reminders to delete!"),
|
||||||
reminder.channel,
|
|
||||||
reminder.utc_time.timestamp()
|
|
||||||
)
|
)
|
||||||
});
|
.await;
|
||||||
|
} else {
|
||||||
|
let mut char_count = 0;
|
||||||
|
|
||||||
let _ = invoke.respond(ctx.http.clone(), CreateGenericResponse::new().content("test")).await;
|
let (shown_reminders, display_vec): (Vec<&Reminder>, Vec<String>) = reminders
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(count, reminder)| (reminder, reminder.display_del(count, &timezone)))
|
||||||
|
.take_while(|(_, p)| {
|
||||||
|
char_count += p.len();
|
||||||
|
|
||||||
|
char_count < EMBED_DESCRIPTION_MAX_LENGTH
|
||||||
|
})
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
let display = display_vec.join("\n");
|
||||||
|
|
||||||
|
let pages = reminders
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(count, reminder)| reminder.display_del(count, &timezone))
|
||||||
|
.fold(0, |t, r| t + r.len())
|
||||||
|
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||||
|
|
||||||
|
let pager = DelPager::new(timezone);
|
||||||
|
|
||||||
|
let del_selector = ComponentDataModel::DelSelector(DelSelector { page: 0, timezone });
|
||||||
|
|
||||||
|
invoke
|
||||||
|
.respond(
|
||||||
|
ctx.http.clone(),
|
||||||
|
CreateGenericResponse::new()
|
||||||
|
.embed(|e| {
|
||||||
|
e.title("Delete Reminders")
|
||||||
|
.description(display)
|
||||||
|
.footer(|f| f.text(format!("Page {} of {}", 1, pages)))
|
||||||
|
.color(*THEME_COLOR)
|
||||||
|
})
|
||||||
|
.components(|comp| {
|
||||||
|
pager.create_button_row(pages, comp);
|
||||||
|
|
||||||
|
comp.create_action_row(|row| {
|
||||||
|
row.create_select_menu(|menu| {
|
||||||
|
menu.custom_id(del_selector.to_custom_id()).options(|opt| {
|
||||||
|
for (count, reminder) in shown_reminders.iter().enumerate() {
|
||||||
|
opt.create_option(|o| {
|
||||||
|
o.label(count + 1).value(reminder.id).description({
|
||||||
|
let c = reminder.display_content();
|
||||||
|
|
||||||
|
if c.len() > 100 {
|
||||||
|
format!(
|
||||||
|
"{}...",
|
||||||
|
reminder
|
||||||
|
.display_content()
|
||||||
|
.chars()
|
||||||
|
.take(97)
|
||||||
|
.collect::<String>()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
c.to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
opt
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command("timer")]
|
#[command("timer")]
|
||||||
|
@ -1,29 +1,25 @@
|
|||||||
|
pub(crate) mod pager;
|
||||||
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use rmp_serde::Serializer;
|
use rmp_serde::Serializer;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
builder::CreateEmbed,
|
builder::CreateEmbed,
|
||||||
client::Context,
|
client::Context,
|
||||||
model::{
|
model::{
|
||||||
channel::Channel,
|
channel::Channel,
|
||||||
id::{ChannelId, GuildId, RoleId, UserId},
|
id::{GuildId, RoleId, UserId},
|
||||||
interactions::{
|
interactions::{message_component::MessageComponentInteraction, InteractionResponseType},
|
||||||
message_component::{ButtonStyle, MessageComponentInteraction},
|
|
||||||
InteractionResponseType,
|
|
||||||
},
|
|
||||||
prelude::InteractionApplicationCommandCallbackDataFlags,
|
prelude::InteractionApplicationCommandCallbackDataFlags,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
component_models::pager::{DelPager, LookPager, Pager},
|
||||||
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
|
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
|
||||||
models::{
|
models::reminder::Reminder,
|
||||||
reminder::{look_flags::LookFlags, Reminder},
|
|
||||||
user_data::UserData,
|
|
||||||
},
|
|
||||||
SQLPool,
|
SQLPool,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,6 +29,8 @@ use crate::{
|
|||||||
pub enum ComponentDataModel {
|
pub enum ComponentDataModel {
|
||||||
Restrict(Restrict),
|
Restrict(Restrict),
|
||||||
LookPager(LookPager),
|
LookPager(LookPager),
|
||||||
|
DelPager(DelPager),
|
||||||
|
DelSelector(DelSelector),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComponentDataModel {
|
impl ComponentDataModel {
|
||||||
@ -109,7 +107,7 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|reminder| reminder.display(&flags, &pager.timezone))
|
.map(|reminder| reminder.display(&flags, &pager.timezone))
|
||||||
.fold(0, |t, r| t + r.len())
|
.fold(0, |t, r| t + r.len())
|
||||||
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH) as u16;
|
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||||
|
|
||||||
let channel_name =
|
let channel_name =
|
||||||
if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {
|
if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {
|
||||||
@ -118,12 +116,7 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let next_page = match pager.action {
|
let next_page = pager.next_page(pages);
|
||||||
PageAction::First => 0,
|
|
||||||
PageAction::Previous => 0.max(pager.page - 1),
|
|
||||||
PageAction::Next => (pages - 1).min(pager.page + 1),
|
|
||||||
PageAction::Last => pages - 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut char_count = 0;
|
let mut char_count = 0;
|
||||||
let mut skip_char_count = 0;
|
let mut skip_char_count = 0;
|
||||||
@ -144,31 +137,6 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
|||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let page_first = ComponentDataModel::LookPager(LookPager {
|
|
||||||
flags: flags.clone(),
|
|
||||||
page: next_page,
|
|
||||||
action: PageAction::First,
|
|
||||||
timezone: pager.timezone,
|
|
||||||
});
|
|
||||||
let page_prev = ComponentDataModel::LookPager(LookPager {
|
|
||||||
flags: flags.clone(),
|
|
||||||
page: next_page,
|
|
||||||
action: PageAction::Previous,
|
|
||||||
timezone: pager.timezone,
|
|
||||||
});
|
|
||||||
let page_next = ComponentDataModel::LookPager(LookPager {
|
|
||||||
flags: flags.clone(),
|
|
||||||
page: next_page,
|
|
||||||
action: PageAction::Next,
|
|
||||||
timezone: pager.timezone,
|
|
||||||
});
|
|
||||||
let page_last = ComponentDataModel::LookPager(LookPager {
|
|
||||||
flags: flags.clone(),
|
|
||||||
page: next_page,
|
|
||||||
action: PageAction::Last,
|
|
||||||
timezone: pager.timezone,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut embed = CreateEmbed::default();
|
let mut embed = CreateEmbed::default();
|
||||||
embed
|
embed
|
||||||
.title(format!(
|
.title(format!(
|
||||||
@ -179,40 +147,223 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
|||||||
.footer(|f| f.text(format!("Page {} of {}", next_page + 1, pages)))
|
.footer(|f| f.text(format!("Page {} of {}", next_page + 1, pages)))
|
||||||
.color(*THEME_COLOR);
|
.color(*THEME_COLOR);
|
||||||
|
|
||||||
let _ =
|
let _ = component
|
||||||
|
.create_interaction_response(&ctx, |r| {
|
||||||
|
r.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
||||||
|
|response| {
|
||||||
|
response.embeds(vec![embed]).components(|comp| {
|
||||||
|
pager.create_button_row(pages, comp);
|
||||||
|
|
||||||
|
comp
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ComponentDataModel::DelPager(pager) => {
|
||||||
|
let reminders =
|
||||||
|
Reminder::from_guild(ctx, component.guild_id, component.user.id).await;
|
||||||
|
|
||||||
|
let pages = reminders
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(count, reminder)| reminder.display_del(count, &pager.timezone))
|
||||||
|
.fold(0, |t, r| t + r.len())
|
||||||
|
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||||
|
|
||||||
|
let next_page = pager.next_page(pages);
|
||||||
|
|
||||||
|
let mut char_count = 0;
|
||||||
|
let mut skip_char_count = 0;
|
||||||
|
let mut first_num = 0;
|
||||||
|
|
||||||
|
let (shown_reminders, display_vec): (Vec<&Reminder>, Vec<String>) = reminders
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(count, reminder)| {
|
||||||
|
(reminder, reminder.display_del(count, &pager.timezone))
|
||||||
|
})
|
||||||
|
.skip_while(|(_, p)| {
|
||||||
|
first_num += 1;
|
||||||
|
skip_char_count += p.len();
|
||||||
|
|
||||||
|
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * next_page
|
||||||
|
})
|
||||||
|
.take_while(|(_, p)| {
|
||||||
|
char_count += p.len();
|
||||||
|
|
||||||
|
char_count < EMBED_DESCRIPTION_MAX_LENGTH
|
||||||
|
})
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
let display = display_vec.join("\n");
|
||||||
|
|
||||||
|
let del_selector = ComponentDataModel::DelSelector(DelSelector {
|
||||||
|
page: next_page,
|
||||||
|
timezone: pager.timezone,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut embed = CreateEmbed::default();
|
||||||
|
embed
|
||||||
|
.title("Delete Reminders")
|
||||||
|
.description(display)
|
||||||
|
.footer(|f| f.text(format!("Page {} of {}", next_page + 1, pages)))
|
||||||
|
.color(*THEME_COLOR);
|
||||||
|
|
||||||
component
|
component
|
||||||
.create_interaction_response(&ctx, |r| {
|
.create_interaction_response(&ctx, |r| {
|
||||||
r.kind(InteractionResponseType::UpdateMessage)
|
r.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
||||||
.interaction_response_data(|response| {
|
|response| {
|
||||||
response.embeds(vec![embed]).components(|comp| {
|
response.embeds(vec![embed]).components(|comp| {
|
||||||
|
pager.create_button_row(pages, comp);
|
||||||
|
|
||||||
comp.create_action_row(|row| {
|
comp.create_action_row(|row| {
|
||||||
row.create_button(|b| {
|
row.create_select_menu(|menu| {
|
||||||
b.label("⏮️")
|
menu.custom_id(del_selector.to_custom_id()).options(
|
||||||
.style(ButtonStyle::Primary)
|
|opt| {
|
||||||
.custom_id(page_first.to_custom_id())
|
for (count, reminder) in
|
||||||
.disabled(next_page == 0)
|
shown_reminders.iter().enumerate()
|
||||||
|
{
|
||||||
|
opt.create_option(|o| {
|
||||||
|
o.label(count + first_num)
|
||||||
|
.value(reminder.id)
|
||||||
|
.description({
|
||||||
|
let c =
|
||||||
|
reminder.display_content();
|
||||||
|
|
||||||
|
if c.len() > 100 {
|
||||||
|
format!(
|
||||||
|
"{}...",
|
||||||
|
reminder
|
||||||
|
.display_content()
|
||||||
|
.chars()
|
||||||
|
.take(97)
|
||||||
|
.collect::<String>(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
c.to_string()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.create_button(|b| {
|
});
|
||||||
b.label("◀️")
|
}
|
||||||
.style(ButtonStyle::Secondary)
|
|
||||||
.custom_id(page_prev.to_custom_id())
|
opt
|
||||||
.disabled(next_page == 0)
|
},
|
||||||
})
|
)
|
||||||
.create_button(|b| {
|
|
||||||
b.label("▶️")
|
|
||||||
.style(ButtonStyle::Secondary)
|
|
||||||
.custom_id(page_next.to_custom_id())
|
|
||||||
.disabled(next_page + 1 == pages)
|
|
||||||
})
|
|
||||||
.create_button(|b| {
|
|
||||||
b.label("⏭️")
|
|
||||||
.style(ButtonStyle::Primary)
|
|
||||||
.custom_id(page_last.to_custom_id())
|
|
||||||
.disabled(next_page + 1 == pages)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ComponentDataModel::DelSelector(selector) => {
|
||||||
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
let selected_id = component.data.values.join(",");
|
||||||
|
|
||||||
|
sqlx::query!("DELETE FROM reminders WHERE FIND_IN_SET(id, ?)", selected_id)
|
||||||
|
.execute(&pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let reminders =
|
||||||
|
Reminder::from_guild(ctx, component.guild_id, component.user.id).await;
|
||||||
|
|
||||||
|
let mut char_count = 0;
|
||||||
|
let mut skip_char_count = 0;
|
||||||
|
let mut first_num = 0;
|
||||||
|
|
||||||
|
let (shown_reminders, display_vec): (Vec<&Reminder>, Vec<String>) = reminders
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(count, reminder)| {
|
||||||
|
(reminder, reminder.display_del(count, &selector.timezone))
|
||||||
|
})
|
||||||
|
.skip_while(|(_, p)| {
|
||||||
|
first_num += 1;
|
||||||
|
skip_char_count += p.len();
|
||||||
|
|
||||||
|
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * selector.page
|
||||||
|
})
|
||||||
|
.take_while(|(_, p)| {
|
||||||
|
char_count += p.len();
|
||||||
|
|
||||||
|
char_count < EMBED_DESCRIPTION_MAX_LENGTH
|
||||||
|
})
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
let display = display_vec.join("\n");
|
||||||
|
|
||||||
|
let pages = reminders
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(count, reminder)| reminder.display_del(count, &selector.timezone))
|
||||||
|
.fold(0, |t, r| t + r.len())
|
||||||
|
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||||
|
|
||||||
|
let pager = DelPager::new(selector.timezone);
|
||||||
|
|
||||||
|
let del_selector = ComponentDataModel::DelSelector(DelSelector {
|
||||||
|
page: selector.page,
|
||||||
|
timezone: selector.timezone,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut embed = CreateEmbed::default();
|
||||||
|
embed
|
||||||
|
.title("Delete Reminders")
|
||||||
|
.description(display)
|
||||||
|
.footer(|f| f.text(format!("Page {} of {}", selector.page + 1, pages)))
|
||||||
|
.color(*THEME_COLOR);
|
||||||
|
|
||||||
|
component
|
||||||
|
.create_interaction_response(&ctx, |r| {
|
||||||
|
r.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
||||||
|
|response| {
|
||||||
|
response.embeds(vec![embed]).components(|comp| {
|
||||||
|
pager.create_button_row(pages, comp);
|
||||||
|
|
||||||
|
comp.create_action_row(|row| {
|
||||||
|
row.create_select_menu(|menu| {
|
||||||
|
menu.custom_id(del_selector.to_custom_id()).options(
|
||||||
|
|opt| {
|
||||||
|
for (count, reminder) in
|
||||||
|
shown_reminders.iter().enumerate()
|
||||||
|
{
|
||||||
|
opt.create_option(|o| {
|
||||||
|
o.label(count + 1)
|
||||||
|
.value(reminder.id)
|
||||||
|
.description({
|
||||||
|
let c =
|
||||||
|
reminder.display_content();
|
||||||
|
|
||||||
|
if c.len() > 100 {
|
||||||
|
format!(
|
||||||
|
"{}...",
|
||||||
|
reminder
|
||||||
|
.display_content()
|
||||||
|
.chars()
|
||||||
|
.take(97)
|
||||||
|
.collect::<String>(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
c.to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
opt
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -227,19 +378,8 @@ pub struct Restrict {
|
|||||||
pub guild_id: GuildId,
|
pub guild_id: GuildId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[repr(u8)]
|
pub struct DelSelector {
|
||||||
pub enum PageAction {
|
pub page: usize,
|
||||||
First = 0,
|
|
||||||
Previous = 1,
|
|
||||||
Next = 2,
|
|
||||||
Last = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct LookPager {
|
|
||||||
pub flags: LookFlags,
|
|
||||||
pub page: u16,
|
|
||||||
pub action: PageAction,
|
|
||||||
pub timezone: Tz,
|
pub timezone: Tz,
|
||||||
}
|
}
|
||||||
|
183
src/component_models/pager.rs
Normal file
183
src/component_models/pager.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
use chrono_tz::Tz;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_repr::*;
|
||||||
|
use serenity::{builder::CreateComponents, model::interactions::message_component::ButtonStyle};
|
||||||
|
|
||||||
|
use crate::{component_models::ComponentDataModel, models::reminder::look_flags::LookFlags};
|
||||||
|
|
||||||
|
pub trait Pager {
|
||||||
|
fn next_page(&self, max_pages: usize) -> usize;
|
||||||
|
|
||||||
|
fn create_button_row(&self, max_pages: usize, comp: &mut CreateComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum PageAction {
|
||||||
|
First = 0,
|
||||||
|
Previous = 1,
|
||||||
|
Next = 2,
|
||||||
|
Last = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct LookPager {
|
||||||
|
pub flags: LookFlags,
|
||||||
|
pub page: usize,
|
||||||
|
action: PageAction,
|
||||||
|
pub timezone: Tz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pager for LookPager {
|
||||||
|
fn next_page(&self, max_pages: usize) -> usize {
|
||||||
|
match self.action {
|
||||||
|
PageAction::First => 0,
|
||||||
|
PageAction::Previous => 0.max(self.page - 1),
|
||||||
|
PageAction::Next => (max_pages - 1).min(self.page + 1),
|
||||||
|
PageAction::Last => max_pages - 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_button_row(&self, max_pages: usize, comp: &mut CreateComponents) {
|
||||||
|
let next_page = self.next_page(max_pages);
|
||||||
|
|
||||||
|
let (page_first, page_prev, page_next, page_last) =
|
||||||
|
LookPager::buttons(self.flags, next_page, self.timezone);
|
||||||
|
|
||||||
|
comp.create_action_row(|row| {
|
||||||
|
row.create_button(|b| {
|
||||||
|
b.label("⏮️")
|
||||||
|
.style(ButtonStyle::Primary)
|
||||||
|
.custom_id(page_first.to_custom_id())
|
||||||
|
.disabled(next_page == 0)
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.label("◀️")
|
||||||
|
.style(ButtonStyle::Secondary)
|
||||||
|
.custom_id(page_prev.to_custom_id())
|
||||||
|
.disabled(next_page == 0)
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.label("▶️")
|
||||||
|
.style(ButtonStyle::Secondary)
|
||||||
|
.custom_id(page_next.to_custom_id())
|
||||||
|
.disabled(next_page + 1 == max_pages)
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.label("⏭️")
|
||||||
|
.style(ButtonStyle::Primary)
|
||||||
|
.custom_id(page_last.to_custom_id())
|
||||||
|
.disabled(next_page + 1 == max_pages)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LookPager {
|
||||||
|
pub fn new(flags: LookFlags, timezone: Tz) -> Self {
|
||||||
|
Self { flags, page: 0, action: PageAction::First, timezone }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buttons(
|
||||||
|
flags: LookFlags,
|
||||||
|
page: usize,
|
||||||
|
timezone: Tz,
|
||||||
|
) -> (ComponentDataModel, ComponentDataModel, ComponentDataModel, ComponentDataModel) {
|
||||||
|
(
|
||||||
|
ComponentDataModel::LookPager(LookPager {
|
||||||
|
flags,
|
||||||
|
page,
|
||||||
|
action: PageAction::First,
|
||||||
|
timezone,
|
||||||
|
}),
|
||||||
|
ComponentDataModel::LookPager(LookPager {
|
||||||
|
flags,
|
||||||
|
page,
|
||||||
|
action: PageAction::Previous,
|
||||||
|
timezone,
|
||||||
|
}),
|
||||||
|
ComponentDataModel::LookPager(LookPager {
|
||||||
|
flags,
|
||||||
|
page,
|
||||||
|
action: PageAction::Next,
|
||||||
|
timezone,
|
||||||
|
}),
|
||||||
|
ComponentDataModel::LookPager(LookPager {
|
||||||
|
flags,
|
||||||
|
page,
|
||||||
|
action: PageAction::Last,
|
||||||
|
timezone,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct DelPager {
|
||||||
|
pub page: usize,
|
||||||
|
action: PageAction,
|
||||||
|
pub timezone: Tz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pager for DelPager {
|
||||||
|
fn next_page(&self, max_pages: usize) -> usize {
|
||||||
|
match self.action {
|
||||||
|
PageAction::First => 0,
|
||||||
|
PageAction::Previous => 0.max(self.page - 1),
|
||||||
|
PageAction::Next => (max_pages - 1).min(self.page + 1),
|
||||||
|
PageAction::Last => max_pages - 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_button_row(&self, max_pages: usize, comp: &mut CreateComponents) {
|
||||||
|
let next_page = self.next_page(max_pages);
|
||||||
|
|
||||||
|
let (page_first, page_prev, page_next, page_last) =
|
||||||
|
DelPager::buttons(next_page, self.timezone);
|
||||||
|
|
||||||
|
comp.create_action_row(|row| {
|
||||||
|
row.create_button(|b| {
|
||||||
|
b.label("⏮️")
|
||||||
|
.style(ButtonStyle::Primary)
|
||||||
|
.custom_id(page_first.to_custom_id())
|
||||||
|
.disabled(next_page == 0)
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.label("◀️")
|
||||||
|
.style(ButtonStyle::Secondary)
|
||||||
|
.custom_id(page_prev.to_custom_id())
|
||||||
|
.disabled(next_page == 0)
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.label("▶️")
|
||||||
|
.style(ButtonStyle::Secondary)
|
||||||
|
.custom_id(page_next.to_custom_id())
|
||||||
|
.disabled(next_page + 1 == max_pages)
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.label("⏭️")
|
||||||
|
.style(ButtonStyle::Primary)
|
||||||
|
.custom_id(page_last.to_custom_id())
|
||||||
|
.disabled(next_page + 1 == max_pages)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DelPager {
|
||||||
|
pub fn new(timezone: Tz) -> Self {
|
||||||
|
Self { page: 0, action: PageAction::First, timezone }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buttons(
|
||||||
|
page: usize,
|
||||||
|
timezone: Tz,
|
||||||
|
) -> (ComponentDataModel, ComponentDataModel, ComponentDataModel, ComponentDataModel) {
|
||||||
|
(
|
||||||
|
ComponentDataModel::DelPager(DelPager { page, action: PageAction::First, timezone }),
|
||||||
|
ComponentDataModel::DelPager(DelPager { page, action: PageAction::Previous, timezone }),
|
||||||
|
ComponentDataModel::DelPager(DelPager { page, action: PageAction::Next, timezone }),
|
||||||
|
ComponentDataModel::DelPager(DelPager { page, action: PageAction::Last, timezone }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -16,15 +16,13 @@ use serenity::{
|
|||||||
framework::Framework,
|
framework::Framework,
|
||||||
futures::prelude::future::BoxFuture,
|
futures::prelude::future::BoxFuture,
|
||||||
http::Http,
|
http::Http,
|
||||||
json::Value,
|
|
||||||
model::{
|
model::{
|
||||||
channel::{Channel, GuildChannel, Message},
|
channel::{Channel, GuildChannel, Message},
|
||||||
guild::{Guild, Member},
|
guild::{Guild, Member},
|
||||||
id::{ChannelId, GuildId, MessageId, RoleId, UserId},
|
id::{ChannelId, GuildId, MessageId, RoleId, UserId},
|
||||||
interactions::{
|
interactions::{
|
||||||
application_command::{
|
application_command::{
|
||||||
ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOption,
|
ApplicationCommand, ApplicationCommandInteraction, ApplicationCommandOptionType,
|
||||||
ApplicationCommandOptionType,
|
|
||||||
},
|
},
|
||||||
InteractionResponseType,
|
InteractionResponseType,
|
||||||
},
|
},
|
||||||
@ -772,7 +770,8 @@ impl RegexFramework {
|
|||||||
ApplicationCommand::set_global_application_commands(&http, |c| {
|
ApplicationCommand::set_global_application_commands(&http, |c| {
|
||||||
self._populate_commands(c)
|
self._populate_commands(c)
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
Some(debug_guild) => {
|
Some(debug_guild) => {
|
||||||
debug_guild
|
debug_guild
|
||||||
|
@ -304,9 +304,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
.add_command("natural", &reminder_cmds::NATURAL_COMMAND)
|
.add_command("natural", &reminder_cmds::NATURAL_COMMAND)
|
||||||
.add_command("n", &reminder_cmds::NATURAL_COMMAND)
|
.add_command("n", &reminder_cmds::NATURAL_COMMAND)
|
||||||
.add_command("", &reminder_cmds::NATURAL_COMMAND)
|
.add_command("", &reminder_cmds::NATURAL_COMMAND)
|
||||||
// management commands
|
|
||||||
.add_command("del", &reminder_cmds::DELETE_COMMAND)
|
|
||||||
*/
|
*/
|
||||||
|
// management commands
|
||||||
|
.add_command(&reminder_cmds::DELETE_COMMAND)
|
||||||
.add_command(&reminder_cmds::LOOK_COMMAND)
|
.add_command(&reminder_cmds::LOOK_COMMAND)
|
||||||
.add_command(&reminder_cmds::PAUSE_COMMAND)
|
.add_command(&reminder_cmds::PAUSE_COMMAND)
|
||||||
.add_command(&reminder_cmds::OFFSET_COMMAND)
|
.add_command(&reminder_cmds::OFFSET_COMMAND)
|
||||||
|
@ -2,8 +2,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_repr::*;
|
use serde_repr::*;
|
||||||
use serenity::model::id::ChannelId;
|
use serenity::model::id::ChannelId;
|
||||||
|
|
||||||
use crate::consts::REGEX_CHANNEL;
|
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, Debug)]
|
#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, Debug)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum TimeDisplayType {
|
pub enum TimeDisplayType {
|
||||||
|
@ -4,11 +4,6 @@ pub mod errors;
|
|||||||
mod helper;
|
mod helper;
|
||||||
pub mod look_flags;
|
pub mod look_flags;
|
||||||
|
|
||||||
use std::{
|
|
||||||
convert::{TryFrom, TryInto},
|
|
||||||
env,
|
|
||||||
};
|
|
||||||
|
|
||||||
use chrono::{NaiveDateTime, TimeZone};
|
use chrono::{NaiveDateTime, TimeZone};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
@ -19,14 +14,13 @@ use sqlx::MySqlPool;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::reminder::{
|
models::reminder::{
|
||||||
errors::InteractionError,
|
|
||||||
helper::longhand_displacement,
|
helper::longhand_displacement,
|
||||||
look_flags::{LookFlags, TimeDisplayType},
|
look_flags::{LookFlags, TimeDisplayType},
|
||||||
},
|
},
|
||||||
SQLPool,
|
SQLPool,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Reminder {
|
pub struct Reminder {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub uid: String,
|
pub uid: String,
|
||||||
@ -284,6 +278,19 @@ WHERE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display_del(&self, count: usize, timezone: &Tz) -> String {
|
||||||
|
format!(
|
||||||
|
"**{}**: '{}' *<#{}>* at **{}**",
|
||||||
|
count + 1,
|
||||||
|
self.display_content(),
|
||||||
|
self.channel,
|
||||||
|
timezone
|
||||||
|
.timestamp(self.utc_time.timestamp(), 0)
|
||||||
|
.format("%Y-%m-%d %H:%M:%S")
|
||||||
|
.to_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn display(&self, flags: &LookFlags, timezone: &Tz) -> String {
|
pub fn display(&self, flags: &LookFlags, timezone: &Tz) -> String {
|
||||||
let time_display = match flags.time_display {
|
let time_display = match flags.time_display {
|
||||||
TimeDisplayType::Absolute => timezone
|
TimeDisplayType::Absolute => timezone
|
||||||
|
Loading…
Reference in New Issue
Block a user