1use std::sync::Arc;
2
3use dioxus_core::use_hook;
4use dioxus_hooks::{
5 use_context,
6 use_context_provider,
7 use_signal,
8};
9use dioxus_signals::{
10 CopyValue,
11 Readable,
12 ReadableRef,
13 Signal,
14 Writable,
15};
16use tokio::sync::Notify;
17
18pub struct UsePopup<Data: 'static, Answer: 'static> {
20 open: Signal<bool>,
21 data: Signal<Option<Data>>,
22 answer: Signal<Option<Answer>>,
23 waker: CopyValue<Arc<Notify>>,
24}
25
26impl<Data, Answer> Copy for UsePopup<Data, Answer> {}
27
28impl<Data, Answer> Clone for UsePopup<Data, Answer> {
29 fn clone(&self) -> Self {
30 *self
31 }
32}
33
34impl<Data, Answer> UsePopup<Data, Answer> {
35 pub fn is_open(&self) -> bool {
37 *self.open.read()
38 }
39
40 pub fn read(&self) -> ReadableRef<Signal<Option<Answer>>> {
42 self.answer.read_unchecked()
43 }
44
45 pub async fn open(
47 &mut self,
48 data: impl Into<Option<Data>>,
49 ) -> ReadableRef<Signal<Option<Answer>>> {
50 self.open.set(true);
51 self.data.set(data.into());
52 let waker = self.waker.read().clone();
53 waker.notified().await;
54 self.open.set(false);
55 self.answer.read_unchecked()
56 }
57}
58
59pub fn use_popup<Data, Answer>() -> UsePopup<Data, Answer> {
61 let data = use_signal(|| None);
62 let answer = use_signal(|| None);
63 let waker = use_hook(|| CopyValue::new(Arc::new(Notify::new())));
64
65 use_context_provider(move || UsePopupAnswer {
66 data,
67 answer,
68 waker,
69 });
70
71 use_hook(move || UsePopup {
72 open: Signal::new(false),
73 data,
74 answer,
75 waker,
76 })
77}
78
79pub struct UsePopupAnswer<Data: 'static, Answer: 'static> {
80 data: Signal<Option<Data>>,
81 answer: Signal<Option<Answer>>,
82 waker: CopyValue<Arc<Notify>>,
83}
84
85impl<Data, Answer> Clone for UsePopupAnswer<Data, Answer> {
86 fn clone(&self) -> Self {
87 *self
88 }
89}
90
91impl<Data, Answer> Copy for UsePopupAnswer<Data, Answer> {}
92
93impl<Data, Answer> UsePopupAnswer<Data, Answer> {
94 pub fn answer(&mut self, data: impl Into<Option<Answer>>) {
96 self.answer.set(data.into());
97 self.waker.read().notify_waiters();
98 }
99
100 pub fn data(&self) -> ReadableRef<Signal<Option<Data>>> {
102 self.data.read_unchecked()
103 }
104}
105
106pub fn use_popup_answer<Data, Answer>() -> UsePopupAnswer<Data, Answer> {
108 use_context::<UsePopupAnswer<Data, Answer>>()
109}
110
111#[cfg(test)]
112mod test {
113 use dioxus::prelude::component;
114 use freya::prelude::*;
115 use freya_testing::prelude::*;
116
117 #[tokio::test]
118 pub async fn popup() {
119 fn popup_app() -> Element {
120 let mut my_popup = use_popup::<(), String>();
121
122 let onpress = move |_| async move {
123 let _name = my_popup.open(()).await;
124 };
125
126 rsx!(
127 Button {
128 onpress,
129 label {
130 "{my_popup.read():?}"
131 }
132 }
133 if my_popup.is_open() {
134 AskNamePopup {}
135 }
136 )
137 }
138
139 #[component]
140 fn AskNamePopup() -> Element {
141 let mut popup_answer = use_popup_answer::<(), String>();
142
143 rsx!(
144 Button {
145 onpress: move |_| {
146 popup_answer.answer("Marc".to_string())
147 },
148 label {
149 "Answer 'Marc'"
150 }
151 }
152 )
153 }
154
155 let mut utils = launch_test(popup_app);
156 let root = utils.root();
157 let label = root.get(0).get(0);
158
159 assert_eq!(label.get(0).text(), Some("None"));
160
161 utils.click_cursor((15.0, 15.0)).await;
163 utils.wait_for_update().await;
164
165 utils.click_cursor((15.0, 40.0)).await;
167 utils.wait_for_update().await;
168
169 assert_eq!(label.get(0).text(), Some("Some(\"Marc\")"));
170 }
171}