Skip to content

Commit ae5679b

Browse files
committed
feat(components-v2): add reply helper module
1 parent 125490d commit ae5679b

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

src/abstraction/components_v2.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use poise::CreateReply;
2+
use serenity::all::{
3+
Colour, CreateActionRow, CreateButton, CreateComponent, CreateContainer,
4+
CreateContainerComponent, CreateMessage, CreateSeparator, CreateTextDisplay, MessageFlags,
5+
};
6+
use std::borrow::Cow;
7+
8+
#[derive(Clone, Copy)]
9+
pub enum Status {
10+
Success,
11+
Error,
12+
Info,
13+
Warning,
14+
}
15+
16+
fn accent(status: Status) -> Colour {
17+
match status {
18+
Status::Success => Colour(0x2ECC71),
19+
Status::Error => Colour::RED,
20+
Status::Info => Colour::BLUE,
21+
Status::Warning => Colour::ORANGE,
22+
}
23+
}
24+
25+
fn is_ephemeral(status: Status) -> bool {
26+
matches!(status, Status::Error | Status::Warning)
27+
}
28+
29+
fn container_reply<'a>(container: CreateContainer<'a>) -> CreateReply<'a> {
30+
CreateReply::new()
31+
.flags(MessageFlags::IS_COMPONENTS_V2)
32+
.components(vec![CreateComponent::Container(container)])
33+
}
34+
35+
fn container_message<'a>(container: CreateContainer<'a>) -> CreateMessage<'a> {
36+
CreateMessage::new()
37+
.flags(MessageFlags::IS_COMPONENTS_V2)
38+
.components(vec![CreateComponent::Container(container)])
39+
}
40+
41+
/// One-line Container reply with a TextDisplay, accent-coloured by `status`.
42+
/// Errors and warnings are auto-ephemeral.
43+
pub fn status_reply<'a, S: Into<Cow<'a, str>>>(status: Status, text: S) -> CreateReply<'a> {
44+
let container = CreateContainer::new(vec![CreateContainerComponent::TextDisplay(
45+
CreateTextDisplay::new(text),
46+
)])
47+
.accent_colour(accent(status));
48+
49+
let reply = container_reply(container);
50+
if is_ephemeral(status) {
51+
reply.ephemeral(true)
52+
} else {
53+
reply
54+
}
55+
}
56+
57+
/// Wrap an owned Container as a V2-flagged reply (for commands that need custom layout).
58+
pub fn reply_from_container<'a>(container: CreateContainer<'a>) -> CreateReply<'a> {
59+
container_reply(container)
60+
}
61+
62+
/// Wrap an owned Container as a V2-flagged CreateMessage (for ChannelId::send_message /
63+
/// User::dm / interaction responses).
64+
pub fn message_from_container<'a>(container: CreateContainer<'a>) -> CreateMessage<'a> {
65+
container_message(container)
66+
}
67+
68+
/// Card reply: heading + separator + one labelled line per (label, value) row.
69+
/// Values are rendered with `**label:** value` markdown.
70+
pub fn card_reply<'a>(
71+
status: Status,
72+
heading: impl Into<Cow<'a, str>>,
73+
rows: Vec<(String, String)>,
74+
) -> CreateReply<'a> {
75+
let heading_cow: Cow<'a, str> = heading.into();
76+
let heading_text = format!("## {heading_cow}");
77+
78+
let mut components: Vec<CreateContainerComponent<'a>> = Vec::with_capacity(rows.len() + 2);
79+
80+
components.push(CreateContainerComponent::TextDisplay(
81+
CreateTextDisplay::new(heading_text),
82+
));
83+
components.push(CreateContainerComponent::Separator(CreateSeparator::new()));
84+
85+
for (label, value) in rows {
86+
components.push(CreateContainerComponent::TextDisplay(
87+
CreateTextDisplay::new(format!("**{label}:** {value}")),
88+
));
89+
}
90+
91+
let container = CreateContainer::new(components).accent_colour(accent(status));
92+
container_reply(container)
93+
}
94+
95+
/// Pagination container: body TextDisplay, separator, page indicator, navigation buttons row.
96+
pub fn paginate_container<'a>(
97+
body: String,
98+
page: usize,
99+
total_pages: usize,
100+
buttons: Vec<CreateButton<'a>>,
101+
) -> CreateContainer<'a> {
102+
CreateContainer::new(vec![
103+
CreateContainerComponent::TextDisplay(CreateTextDisplay::new(body)),
104+
CreateContainerComponent::Separator(CreateSeparator::new()),
105+
CreateContainerComponent::TextDisplay(CreateTextDisplay::new(format!(
106+
"Page {} / {}",
107+
page + 1,
108+
total_pages
109+
))),
110+
CreateContainerComponent::ActionRow(CreateActionRow::buttons(buttons)),
111+
])
112+
.accent_colour(Colour::BLURPLE)
113+
}

src/abstraction/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod activity_data;
22
pub mod command;
3+
pub mod components_v2;
34
pub mod playmatch;

0 commit comments

Comments
 (0)