dusk_ui/
window.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::sync::Arc;

use tokio::sync::oneshot;
use twilight_http::client::InteractionClient;
use twilight_model::application::interaction::{Interaction, InteractionData};
use twilight_model::channel::message::{Component, MessageFlags};
use twilight_model::channel::{message, Message};
use twilight_model::http::interaction::{
    InteractionResponse, InteractionResponseData, InteractionResponseType,
};

use crate::component::CompWindow;
use crate::context::{BuildContext, BuildContextPrefix};
use crate::dusk::Dusk;

fn draw_text<D, FT, FTR>(data: &D, render_text: &FT) -> Option<String>
where
    FT: Fn(&D) -> FTR + 'static,
    FTR: Into<Option<String>> + 'static,
{
    render_text(data).into()
}

fn draw_components<D, FC>(
    build_ctx: &BuildContext<D>,
    data: &D,
    render_cmp: &FC,
) -> Option<Vec<message::Component>>
where
    FC: Fn(&D) -> CompWindow<D>,
{
    let comps = render_cmp(data);
    let vec = comps
        .children
        .into_iter()
        .enumerate()
        .map(|(i, x)| {
            x.build(BuildContextPrefix {
                parent: build_ctx,
                prefix: i.to_string(),
            })
        })
        .collect();
    Some(vec)
}

pub async fn create<'a, D, FT, FTR, FC>(
    dusk: &Dusk,
    interaction: &'a Interaction,
    client: &'a InteractionClient<'a>,
    ephemeral: bool,
    no_defer: bool,
    mut data: D,
    render_text: FT,
    render_cmp: FC,
) -> crate::errors::Result<()>
where
    FT: Fn(&D) -> FTR + 'static,
    FTR: Into<Option<String>> + 'static,
    FC: Fn(&D) -> CompWindow<D>,
{
    let last_text = "".to_string();

    let build_ctx = BuildContext::<D>::new();
    let ctx = Arc::clone(&build_ctx.ctx);

    if !no_defer {
        client
            .create_response(
                interaction.id,
                &interaction.token,
                &InteractionResponse {
                    kind: InteractionResponseType::DeferredChannelMessageWithSource,
                    data: if ephemeral {
                        Some(InteractionResponseData {
                            flags: Some(MessageFlags::EPHEMERAL),
                            ..Default::default()
                        })
                    } else {
                        None
                    },
                },
            )
            .await?;
    }

    let mut msg: Option<Message> = None;
    let token: String = interaction.token.clone();
    while !*ctx.should_exit.lock().unwrap() {
        if let Some(msg) = msg.as_ref() {
            dusk.messages.remove(&msg.id);
        }

        let mut update = client.update_response(&token);

        let new_text = draw_text(&data, &render_text);
        if let Some(new_text) = &new_text {
            if *new_text != last_text {
                update = update.content(Some(new_text.as_str()))?;
            }
        }

        let mut components: Option<Vec<Component>> = None;

        if !*ctx.dont_update.lock().unwrap() {
            build_ctx.binding.clear();
            components = draw_components(&build_ctx, &data, &render_cmp);
            update = update.components(components.as_deref())?;
        } else {
            *ctx.dont_update.lock().unwrap() = false;
        }

        let result = update.await?.model().await?;
        msg = Some(result);

        let (tx, rx) = oneshot::channel();
        dusk.messages.insert(msg.as_ref().unwrap().id, tx);

        let interaction = rx.await?;
        if let Some(InteractionData::MessageComponent(interaction_data)) = &interaction.data {
            let custom_id = &interaction_data.custom_id;
            if let Some(callback) = build_ctx.binding.get(custom_id) {
                let callback = callback.value();
                data = callback(&interaction, &ctx, data).await;
            }
        }
    }

    Ok(())
}