look command pager

This commit is contained in:
jellywx 2021-09-16 14:48:29 +01:00
parent 395a8481f1
commit 3c1eeed92f
7 changed files with 284 additions and 84 deletions

92
Cargo.lock generated
View File

@ -171,12 +171,13 @@ checksum = "2554a3155fec064362507487171dcc4edc3df60cb10f3a1fb10ed8094822b120"
dependencies = [
"chrono",
"parse-zoneinfo",
"serde",
]
[[package]]
name = "command_attr"
version = "0.3.7"
source = "git+https://github.com/serenity-rs/serenity?branch=next#5348e9c56909d7c534717825be20763acb33336d"
source = "git+https://github.com/serenity-rs/serenity?branch=next#723749c43182838925dd89ac90b93dd2a837261d"
dependencies = [
"proc-macro2",
"quote",
@ -318,9 +319,9 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.21"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80edafed416a46fb378521624fab1cfa2eb514784fd8921adbe8a8d8321da811"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
@ -694,6 +695,15 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
@ -702,9 +712,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "js-sys"
version = "0.3.54"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254"
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
dependencies = [
"wasm-bindgen",
]
@ -726,9 +736,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
[[package]]
name = "libc"
version = "0.2.101"
version = "0.2.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
[[package]]
name = "libm"
@ -754,12 +764,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "matches"
version = "0.1.9"
@ -1433,9 +1437,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.67"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [
"itoa",
"ryu",
@ -1457,7 +1461,7 @@ dependencies = [
[[package]]
name = "serenity"
version = "0.10.9"
source = "git+https://github.com/serenity-rs/serenity?branch=next#5348e9c56909d7c534717825be20763acb33336d"
source = "git+https://github.com/serenity-rs/serenity?branch=next#723749c43182838925dd89ac90b93dd2a837261d"
dependencies = [
"async-trait",
"async-tungstenite",
@ -1545,9 +1549,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "socket2"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad"
checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
dependencies = [
"libc",
"winapi",
@ -1561,14 +1565,12 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "sqlformat"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684001e7985ec1a9a66963b77ed151ef22a7876b3fdd7e37a57ec774f54b7d96"
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
dependencies = [
"lazy_static",
"maplit",
"itertools",
"nom",
"regex",
"unicode_categories",
]
@ -1765,9 +1767,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.3.1"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
checksum = "5241dd6f21443a3606b432718b166d3cedc962fd4b8bea54a8bc7f514ebda986"
dependencies = [
"tinyvec_macros",
]
@ -1863,9 +1865,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
version = "0.1.26"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
checksum = "c2ba9ab62b7d6497a8638dfda5e5c4fb3b2d5a7fca4118f2b96151c8ef1a437e"
dependencies = [
"cfg-if",
"log",
@ -1876,9 +1878,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.15"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77"
dependencies = [
"proc-macro2",
"quote",
@ -1887,9 +1889,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.19"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8"
checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf"
dependencies = [
"lazy_static",
]
@ -2044,9 +2046,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.77"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if",
"serde",
@ -2056,9 +2058,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.77"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
dependencies = [
"bumpalo",
"lazy_static",
@ -2071,9 +2073,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.27"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a87d738d4abc4cf22f6eb142f5b9a81301331ee3c767f2fef2fda4e325492060"
checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
dependencies = [
"cfg-if",
"js-sys",
@ -2083,9 +2085,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.77"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -2093,9 +2095,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.77"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
dependencies = [
"proc-macro2",
"quote",
@ -2106,15 +2108,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.77"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
[[package]]
name = "web-sys"
version = "0.3.54"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a84d70d1ec7d2da2d26a5bd78f4bca1b8c3254805363ce743b7a05bc30d195a"
checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
dependencies = [
"js-sys",
"wasm-bindgen",

View File

@ -14,7 +14,7 @@ regex = "1.4"
log = "0.4"
env_logger = "0.8"
chrono = "0.4"
chrono-tz = "0.5"
chrono-tz = { version = "0.5", features = ["serde"] }
lazy_static = "1.4"
num-integer = "0.1"
serde = "1.0"

View File

@ -14,12 +14,14 @@ use serenity::{
model::{
channel::{Channel, Message},
id::ChannelId,
interactions::message_component::ButtonStyle,
misc::Mentionable,
},
};
use crate::{
check_subscription_on_message,
component_models::{ComponentDataModel, LookPager, PageAction},
consts::{
EMBED_DESCRIPTION_MAX_LENGTH, REGEX_CHANNEL_USER, REGEX_NATURAL_COMMAND_1,
REGEX_NATURAL_COMMAND_2, REGEX_REMIND_COMMAND, THEME_COLOR,
@ -296,6 +298,12 @@ async fn look(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: C
invoke.channel_id()
};
let channel_name = if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {
Some(channel.name)
} else {
None
};
let reminders = Reminder::from_channel(ctx, channel_id, &flags).await;
if reminders.is_empty() {
@ -325,35 +333,84 @@ async fn look(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync), args: C
.fold(0, |t, r| t + r.len())
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
let _ = invoke
let page_first = ComponentDataModel::LookPager(LookPager {
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
.respond(
ctx.http.clone(),
CreateGenericResponse::new()
.embed(|e| {
e.title(format!("Reminders on {}", channel_id.mention()))
.description(display)
.footer(|f| f.text(format!("Page {} of {}", 1, pages)))
e.title(format!(
"Reminders{}",
channel_name.map_or(String::new(), |n| format!(" on #{}", n))
))
.description(display)
.footer(|f| f.text(format!("Page {} of {}", 1, pages)))
.color(*THEME_COLOR)
})
.components(|comp| {
comp.create_action_row(|row| {
row.create_button(|b| b.label("⏮️").custom_id(".1"))
.create_button(|b| b.label("◀️").custom_id(".2"))
.create_button(|b| b.label("▶️").custom_id(".3"))
.create_button(|b| b.label("⏭️").custom_id(".4"))
row.create_button(|b| {
b.label("⏮️")
.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
.unwrap();
}
}
/*
#[command("del")]
#[description("Delete reminders")]
#[permission_level(Managed)]
async fn delete(ctx: &Context, msg: &Message, _args: String) {
let (pool, lm) = get_ctx_data(&ctx).await;
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
async fn delete(ctx: &Context, invoke: &(dyn CommandInvoke + Send + Sync)) {
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
let _ = msg.channel_id.say(&ctx, lm.get(&user_data.language, "del/listing")).await;
@ -417,19 +474,6 @@ DELETE FROM reminders WHERE FIND_IN_SET(id, ?)
.await
.unwrap();
if let Some(guild_id) = msg.guild_id {
let _ = sqlx::query!(
"
INSERT INTO events (event_name, bulk_count, guild_id, user_id) VALUES ('delete', ?, ?, ?)
",
count_row.count,
guild_id.as_u64(),
user_data.id
)
.execute(&pool)
.await;
}
let content = lm.get(&user_data.language, "del/count").replacen(
"{}",
&count_row.count.to_string(),

View File

@ -1,13 +1,28 @@
use std::io::Cursor;
use chrono_tz::Tz;
use rmp_serde::Serializer;
use serde::{Deserialize, Serialize};
use serenity::model::{
id::{ChannelId, RoleId},
interactions::message_component::MessageComponentInteraction,
use serenity::{
builder::CreateEmbed,
client::Context,
model::{
channel::Channel,
id::{ChannelId, RoleId},
interactions::{
message_component::{ButtonStyle, MessageComponentInteraction},
InteractionResponseType,
},
},
};
use crate::models::reminder::look_flags::LookFlags;
use crate::{
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
models::{
reminder::{look_flags::LookFlags, Reminder},
user_data::UserData,
},
};
#[derive(Deserialize, Serialize)]
#[serde(tag = "type")]
@ -29,23 +44,160 @@ impl ComponentDataModel {
rmp_serde::from_read(cur).unwrap()
}
pub async fn act(&self, component: MessageComponentInteraction) {
pub async fn act(&self, ctx: &Context, component: MessageComponentInteraction) {
match self {
ComponentDataModel::Restrict(restrict) => {
println!("{:?}", component.data.values);
}
ComponentDataModel::LookPager(pager) => {}
ComponentDataModel::LookPager(pager) => {
let flags = pager.flags;
let channel_opt = component.channel_id.to_channel_cached(&ctx);
let channel_id = if let Some(Channel::Guild(channel)) = channel_opt {
if Some(channel.guild_id) == component.guild_id {
flags.channel_id.unwrap_or(component.channel_id)
} else {
component.channel_id
}
} else {
component.channel_id
};
let reminders = Reminder::from_channel(ctx, channel_id, &flags).await;
let pages = reminders
.iter()
.map(|reminder| reminder.display(&flags, &pager.timezone))
.fold(0, |t, r| t + r.len())
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH) as u16;
let channel_name =
if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {
Some(channel.name)
} else {
None
};
let next_page = match pager.action {
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 skip_char_count = 0;
let display = reminders
.iter()
.map(|reminder| reminder.display(&flags, &pager.timezone))
.skip_while(|p| {
skip_char_count += p.len();
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * next_page as usize
})
.take_while(|p| {
char_count += p.len();
char_count < EMBED_DESCRIPTION_MAX_LENGTH
})
.collect::<Vec<String>>()
.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();
embed
.title(format!(
"Reminders{}",
channel_name.map_or(String::new(), |n| format!(" on #{}", n))
))
.description(display)
.footer(|f| f.text(format!("Page {} of {}", next_page + 1, pages)))
.color(*THEME_COLOR);
let _ =
component
.create_interaction_response(&ctx, |r| {
r.kind(InteractionResponseType::UpdateMessage)
.interaction_response_data(|response| {
response.embeds(vec![embed]).components(|comp| {
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 == pages)
})
.create_button(|b| {
b.label("⏭️")
.style(ButtonStyle::Primary)
.custom_id(page_last.to_custom_id())
.disabled(next_page + 1 == pages)
})
})
})
})
})
.await;
}
}
}
}
#[derive(Deserialize, Serialize)]
#[derive(Serialize, Deserialize)]
pub struct Restrict {
pub role_id: RoleId,
}
#[derive(Deserialize, Serialize)]
#[derive(Serialize, Deserialize, Debug)]
pub enum PageAction {
First = 0,
Previous = 1,
Next = 2,
Last = 3,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LookPager {
pub flags: LookFlags,
pub page_request: u16,
pub page: u16,
pub action: PageAction,
pub timezone: Tz,
}

View File

@ -1,3 +1,5 @@
// todo move framework to its own module, split out permission checks
use std::{
collections::{HashMap, HashSet},
hash::{Hash, Hasher},

View File

@ -258,7 +258,7 @@ DELETE FROM guilds WHERE guild = ?
}
Interaction::MessageComponent(component) => {
let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id);
component_model.act(component).await;
component_model.act(&ctx, component).await;
}
_ => {}
}

View File

@ -3,13 +3,13 @@ use serenity::model::id::ChannelId;
use crate::consts::REGEX_CHANNEL;
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub enum TimeDisplayType {
Absolute = 0,
Relative = 1,
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub struct LookFlags {
pub show_disabled: bool,
pub channel_id: Option<ChannelId>,