todo pager and selector
This commit is contained in:
parent
ebabe0e85a
commit
b310e99085
@ -6,7 +6,10 @@ use serenity::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
component_models::pager::TodoPager,
|
component_models::{
|
||||||
|
pager::{Pager, TodoPager},
|
||||||
|
ComponentDataModel, TodoSelector,
|
||||||
|
},
|
||||||
consts::{EMBED_DESCRIPTION_MAX_LENGTH, SELECT_MAX_ENTRIES, THEME_COLOR},
|
consts::{EMBED_DESCRIPTION_MAX_LENGTH, SELECT_MAX_ENTRIES, THEME_COLOR},
|
||||||
framework::{CommandInvoke, CommandOptions, CreateGenericResponse},
|
framework::{CommandInvoke, CommandOptions, CreateGenericResponse},
|
||||||
SQLPool,
|
SQLPool,
|
||||||
@ -67,8 +70,6 @@ async fn todo(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
|||||||
_ => (Some(invoke.author_id().0), None, None),
|
_ => (Some(invoke.author_id().0), None, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("{:?}", keys);
|
|
||||||
|
|
||||||
match args.get("task") {
|
match args.get("task") {
|
||||||
Some(task) => {
|
Some(task) => {
|
||||||
let task = task.to_string();
|
let task = task.to_string();
|
||||||
@ -91,7 +92,7 @@ async fn todo(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
|||||||
None => {
|
None => {
|
||||||
let values = sqlx::query!(
|
let values = sqlx::query!(
|
||||||
// fucking braindead mysql use <=> instead of = for null comparison
|
// fucking braindead mysql use <=> instead of = for null comparison
|
||||||
"SELECT value FROM todos WHERE user_id <=> ? AND channel_id <=> ? AND guild_id <=> ?",
|
"SELECT id, value FROM todos WHERE user_id <=> ? AND channel_id <=> ? AND guild_id <=> ?",
|
||||||
keys.0,
|
keys.0,
|
||||||
keys.1,
|
keys.1,
|
||||||
keys.2,
|
keys.2,
|
||||||
@ -100,8 +101,8 @@ async fn todo(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|row| row.value.clone())
|
.map(|row| (row.id as usize, row.value.clone()))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<(usize, String)>>();
|
||||||
|
|
||||||
let resp = show_todo_page(&values, 0, keys.0, keys.1, keys.2);
|
let resp = show_todo_page(&values, 0, keys.0, keys.1, keys.2);
|
||||||
|
|
||||||
@ -119,11 +120,11 @@ async fn todo(ctx: &Context, invoke: CommandInvoke, args: CommandOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_todo_page(todo_values: &[String]) -> usize {
|
pub fn max_todo_page(todo_values: &[(usize, String)]) -> usize {
|
||||||
let mut rows = 0;
|
let mut rows = 0;
|
||||||
let mut char_count = 0;
|
let mut char_count = 0;
|
||||||
|
|
||||||
todo_values.iter().enumerate().map(|(c, v)| format!("{}: {}", c, v)).fold(
|
todo_values.iter().enumerate().map(|(c, (_, v))| format!("{}: {}", c, v)).fold(
|
||||||
1,
|
1,
|
||||||
|mut pages, text| {
|
|mut pages, text| {
|
||||||
rows += 1;
|
rows += 1;
|
||||||
@ -141,13 +142,13 @@ pub fn max_todo_page(todo_values: &[String]) -> usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_todo_page(
|
pub fn show_todo_page(
|
||||||
todo_values: &[String],
|
todo_values: &[(usize, String)],
|
||||||
page: usize,
|
page: usize,
|
||||||
user_id: Option<u64>,
|
user_id: Option<u64>,
|
||||||
channel_id: Option<u64>,
|
channel_id: Option<u64>,
|
||||||
guild_id: Option<u64>,
|
guild_id: Option<u64>,
|
||||||
) -> CreateInteractionResponse {
|
) -> CreateInteractionResponse {
|
||||||
// let pager = TodoPager::new(page, user_id, channel_id, guild_id);
|
let pager = TodoPager::new(page, user_id, channel_id, guild_id);
|
||||||
|
|
||||||
let pages = max_todo_page(todo_values);
|
let pages = max_todo_page(todo_values);
|
||||||
let mut page = page;
|
let mut page = page;
|
||||||
@ -163,11 +164,11 @@ pub fn show_todo_page(
|
|||||||
|
|
||||||
let mut skipped_pages = 0;
|
let mut skipped_pages = 0;
|
||||||
|
|
||||||
let display_vec: Vec<String> = todo_values
|
let (todo_ids, display_vec): (Vec<usize>, Vec<String>) = todo_values
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(c, v)| format!("`{}`: {}", c + 1, v))
|
.map(|(c, (i, v))| (i, format!("`{}`: {}", c + 1, v)))
|
||||||
.skip_while(|p| {
|
.skip_while(|(_, p)| {
|
||||||
first_num += 1;
|
first_num += 1;
|
||||||
skipped_rows += 1;
|
skipped_rows += 1;
|
||||||
skipped_char_count += p.len();
|
skipped_char_count += p.len();
|
||||||
@ -182,13 +183,13 @@ pub fn show_todo_page(
|
|||||||
|
|
||||||
skipped_pages < page
|
skipped_pages < page
|
||||||
})
|
})
|
||||||
.take_while(|p| {
|
.take_while(|(_, p)| {
|
||||||
rows += 1;
|
rows += 1;
|
||||||
char_count += p.len();
|
char_count += p.len();
|
||||||
|
|
||||||
char_count < EMBED_DESCRIPTION_MAX_LENGTH && rows <= SELECT_MAX_ENTRIES
|
char_count < EMBED_DESCRIPTION_MAX_LENGTH && rows <= SELECT_MAX_ENTRIES
|
||||||
})
|
})
|
||||||
.collect();
|
.unzip();
|
||||||
|
|
||||||
let display = display_vec.join("\n");
|
let display = display_vec.join("\n");
|
||||||
|
|
||||||
@ -200,6 +201,9 @@ pub fn show_todo_page(
|
|||||||
"Server"
|
"Server"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let todo_selector =
|
||||||
|
ComponentDataModel::TodoSelector(TodoSelector { page, user_id, channel_id, guild_id });
|
||||||
|
|
||||||
let mut embed = CreateEmbed::default();
|
let mut embed = CreateEmbed::default();
|
||||||
embed
|
embed
|
||||||
.title(format!("{} Todo List", title))
|
.title(format!("{} Todo List", title))
|
||||||
@ -208,7 +212,27 @@ pub fn show_todo_page(
|
|||||||
.color(*THEME_COLOR);
|
.color(*THEME_COLOR);
|
||||||
|
|
||||||
let mut response = CreateInteractionResponse::default();
|
let mut response = CreateInteractionResponse::default();
|
||||||
response.interaction_response_data(|d| d.embeds(vec![embed]));
|
response.interaction_response_data(|d| {
|
||||||
|
d.embeds(vec![embed]).components(|comp| {
|
||||||
|
pager.create_button_row(pages, comp);
|
||||||
|
|
||||||
|
comp.create_action_row(|row| {
|
||||||
|
row.create_select_menu(|menu| {
|
||||||
|
menu.custom_id(todo_selector.to_custom_id()).options(|opt| {
|
||||||
|
for (count, (id, disp)) in todo_ids.iter().zip(&display_vec).enumerate() {
|
||||||
|
opt.create_option(|o| {
|
||||||
|
o.label(format!("Mark {} complete", count + first_num))
|
||||||
|
.value(id)
|
||||||
|
.description(disp.split_once(" ").unwrap_or(("", "")).1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
opt
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@ use serenity::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::reminder_cmds::{max_delete_page, show_delete_page},
|
commands::{
|
||||||
|
reminder_cmds::{max_delete_page, show_delete_page},
|
||||||
|
todo_cmds::{max_todo_page, show_todo_page},
|
||||||
|
},
|
||||||
component_models::pager::{DelPager, LookPager, Pager, TodoPager},
|
component_models::pager::{DelPager, LookPager, Pager, TodoPager},
|
||||||
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
|
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
|
||||||
models::reminder::Reminder,
|
models::reminder::Reminder,
|
||||||
@ -33,6 +36,7 @@ pub enum ComponentDataModel {
|
|||||||
DelPager(DelPager),
|
DelPager(DelPager),
|
||||||
TodoPager(TodoPager),
|
TodoPager(TodoPager),
|
||||||
DelSelector(DelSelector),
|
DelSelector(DelSelector),
|
||||||
|
TodoSelector(TodoSelector),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComponentDataModel {
|
impl ComponentDataModel {
|
||||||
@ -199,7 +203,80 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ComponentDataModel::TodoPager(pager) => {}
|
ComponentDataModel::TodoPager(pager) => {
|
||||||
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
|
||||||
|
let values = sqlx::query!(
|
||||||
|
// fucking braindead mysql use <=> instead of = for null comparison
|
||||||
|
"SELECT id, value FROM todos WHERE user_id <=> ? AND channel_id <=> ? AND guild_id <=> ?",
|
||||||
|
pager.user_id,
|
||||||
|
pager.channel_id,
|
||||||
|
pager.guild_id,
|
||||||
|
)
|
||||||
|
.fetch_all(&pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|row| (row.id as usize, row.value.clone()))
|
||||||
|
.collect::<Vec<(usize, String)>>();
|
||||||
|
|
||||||
|
let max_pages = max_todo_page(&values);
|
||||||
|
|
||||||
|
let resp = show_todo_page(
|
||||||
|
&values,
|
||||||
|
pager.next_page(max_pages),
|
||||||
|
pager.user_id,
|
||||||
|
pager.channel_id,
|
||||||
|
pager.guild_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = component
|
||||||
|
.create_interaction_response(&ctx, move |r| {
|
||||||
|
*r = resp;
|
||||||
|
r.kind(InteractionResponseType::UpdateMessage)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ComponentDataModel::TodoSelector(selector) => {
|
||||||
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
let selected_id = component.data.values.join(",");
|
||||||
|
|
||||||
|
sqlx::query!("DELETE FROM todos WHERE FIND_IN_SET(id, ?)", selected_id)
|
||||||
|
.execute(&pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let values = sqlx::query!(
|
||||||
|
// fucking braindead mysql use <=> instead of = for null comparison
|
||||||
|
"SELECT id, value FROM todos WHERE user_id <=> ? AND channel_id <=> ? AND guild_id <=> ?",
|
||||||
|
selector.user_id,
|
||||||
|
selector.channel_id,
|
||||||
|
selector.guild_id,
|
||||||
|
)
|
||||||
|
.fetch_all(&pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|row| (row.id as usize, row.value.clone()))
|
||||||
|
.collect::<Vec<(usize, String)>>();
|
||||||
|
|
||||||
|
let max_pages = max_todo_page(&values);
|
||||||
|
|
||||||
|
let resp = show_todo_page(
|
||||||
|
&values,
|
||||||
|
selector.page,
|
||||||
|
selector.user_id,
|
||||||
|
selector.channel_id,
|
||||||
|
selector.guild_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = component
|
||||||
|
.create_interaction_response(&ctx, move |r| {
|
||||||
|
*r = resp;
|
||||||
|
r.kind(InteractionResponseType::UpdateMessage)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,3 +293,11 @@ pub struct DelSelector {
|
|||||||
pub page: usize,
|
pub page: usize,
|
||||||
pub timezone: Tz,
|
pub timezone: Tz,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct TodoSelector {
|
||||||
|
pub page: usize,
|
||||||
|
pub user_id: Option<u64>,
|
||||||
|
pub channel_id: Option<u64>,
|
||||||
|
pub guild_id: Option<u64>,
|
||||||
|
}
|
||||||
|
@ -221,9 +221,9 @@ impl DelPager {
|
|||||||
pub struct TodoPager {
|
pub struct TodoPager {
|
||||||
pub page: usize,
|
pub page: usize,
|
||||||
action: PageAction,
|
action: PageAction,
|
||||||
pub user_id: Option<UserId>,
|
pub user_id: Option<u64>,
|
||||||
pub channel_id: Option<ChannelId>,
|
pub channel_id: Option<u64>,
|
||||||
pub guild_id: Option<GuildId>,
|
pub guild_id: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pager for TodoPager {
|
impl Pager for TodoPager {
|
||||||
@ -278,18 +278,18 @@ impl Pager for TodoPager {
|
|||||||
impl TodoPager {
|
impl TodoPager {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
page: usize,
|
page: usize,
|
||||||
user_id: Option<UserId>,
|
user_id: Option<u64>,
|
||||||
channel_id: Option<ChannelId>,
|
channel_id: Option<u64>,
|
||||||
guild_id: Option<GuildId>,
|
guild_id: Option<u64>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { page, action: PageAction::Refresh, user_id, channel_id, guild_id }
|
Self { page, action: PageAction::Refresh, user_id, channel_id, guild_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn buttons(
|
pub fn buttons(
|
||||||
page: usize,
|
page: usize,
|
||||||
user_id: Option<UserId>,
|
user_id: Option<u64>,
|
||||||
channel_id: Option<ChannelId>,
|
channel_id: Option<u64>,
|
||||||
guild_id: Option<GuildId>,
|
guild_id: Option<u64>,
|
||||||
) -> (
|
) -> (
|
||||||
ComponentDataModel,
|
ComponentDataModel,
|
||||||
ComponentDataModel,
|
ComponentDataModel,
|
||||||
|
@ -70,43 +70,6 @@ WHERE
|
|||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_id(ctx: &Context, id: u32) -> Option<Self> {
|
|
||||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
|
||||||
|
|
||||||
sqlx::query_as_unchecked!(
|
|
||||||
Self,
|
|
||||||
"
|
|
||||||
SELECT
|
|
||||||
reminders.id,
|
|
||||||
reminders.uid,
|
|
||||||
channels.channel,
|
|
||||||
reminders.utc_time,
|
|
||||||
reminders.interval,
|
|
||||||
reminders.expires,
|
|
||||||
reminders.enabled,
|
|
||||||
reminders.content,
|
|
||||||
reminders.embed_description,
|
|
||||||
users.user AS set_by
|
|
||||||
FROM
|
|
||||||
reminders
|
|
||||||
INNER JOIN
|
|
||||||
channels
|
|
||||||
ON
|
|
||||||
reminders.channel_id = channels.id
|
|
||||||
LEFT JOIN
|
|
||||||
users
|
|
||||||
ON
|
|
||||||
reminders.set_by = users.id
|
|
||||||
WHERE
|
|
||||||
reminders.id = ?
|
|
||||||
",
|
|
||||||
id
|
|
||||||
)
|
|
||||||
.fetch_one(&pool)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn from_channel<C: Into<ChannelId>>(
|
pub async fn from_channel<C: Into<ChannelId>>(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
channel_id: C,
|
channel_id: C,
|
||||||
|
Loading…
Reference in New Issue
Block a user