use crate::builder::{
Builder as _,
CreateActionRow,
CreateInputText,
CreateInteractionResponse,
CreateModal,
};
use crate::client::Context;
use crate::collector::ModalInteractionCollector;
use crate::model::prelude::*;
#[cfg(feature = "collector")]
pub struct QuickModalResponse {
pub interaction: ModalInteraction,
pub inputs: Vec<String>,
}
#[cfg(feature = "collector")]
#[must_use]
pub struct CreateQuickModal {
title: String,
timeout: Option<std::time::Duration>,
input_texts: Vec<CreateInputText>,
}
#[cfg(feature = "collector")]
impl CreateQuickModal {
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
timeout: None,
input_texts: Vec::new(),
}
}
pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn field(mut self, input_text: CreateInputText) -> Self {
self.input_texts.push(input_text);
self
}
pub fn short_field(self, label: impl Into<String>) -> Self {
self.field(CreateInputText::new(InputTextStyle::Short, label, ""))
}
pub fn paragraph_field(self, label: impl Into<String>) -> Self {
self.field(CreateInputText::new(InputTextStyle::Paragraph, label, ""))
}
pub async fn execute(
self,
ctx: &Context,
interaction_id: InteractionId,
token: &str,
) -> Result<Option<QuickModalResponse>, crate::Error> {
let modal_custom_id = interaction_id.get().to_string();
let builder = CreateInteractionResponse::Modal(
CreateModal::new(&modal_custom_id, self.title).components(
self.input_texts
.into_iter()
.enumerate()
.map(|(i, input_text)| {
CreateActionRow::InputText(input_text.custom_id(i.to_string()))
})
.collect(),
),
);
builder.execute(ctx, (interaction_id, token)).await?;
let collector =
ModalInteractionCollector::new(&ctx.shard).custom_ids(vec![modal_custom_id]);
let collector = match self.timeout {
Some(timeout) => collector.timeout(timeout),
None => collector,
};
let modal_interaction = collector.next().await;
let Some(modal_interaction) = modal_interaction else { return Ok(None) };
let inputs = modal_interaction
.data
.components
.iter()
.filter_map(|row| match row.components.first() {
Some(ActionRowComponent::InputText(text)) => {
if let Some(value) = &text.value {
Some(value.clone())
} else {
tracing::warn!("input text value was empty in modal response");
None
}
},
Some(other) => {
tracing::warn!("expected input text in modal response, got {:?}", other);
None
},
None => {
tracing::warn!("empty action row");
None
},
})
.collect();
Ok(Some(QuickModalResponse {
inputs,
interaction: modal_interaction,
}))
}
}