24_async_data/
24_async_data.rs1use crossterm::event::KeyCode;
9use std::thread;
10use std::time::Duration;
11use telex::prelude::*;
12use telex::Color;
13
14telex::require_api!(0, 2);
15
16fn main() {
17 telex::run_with_theme(App, telex::theme::Theme::nord()).unwrap();
18}
19
20struct App;
21
22#[derive(Clone)]
24struct UserProfile {
25 name: String,
26 email: String,
27 member_since: String,
28}
29
30#[derive(Clone)]
31struct Stats {
32 posts: u32,
33 followers: u32,
34 following: u32,
35}
36
37impl Component for App {
38 fn render(&self, cx: Scope) -> View {
39 let show_help = state!(cx, || false);
40
41 cx.use_command(
43 KeyBinding::key(KeyCode::F(1)),
44 with!(show_help => move || show_help.update(|v| *v = !*v)),
45 );
46
47 let profile = async_data!(cx, || {
49 thread::sleep(Duration::from_secs(2));
50 Ok(UserProfile {
51 name: "Alice Johnson".to_string(),
52 email: "alice@example.com".to_string(),
53 member_since: "January 2024".to_string(),
54 })
55 });
56
57 let stats = async_data!(cx, || {
59 thread::sleep(Duration::from_secs(1));
60 Ok(Stats {
61 posts: 142,
62 followers: 1234,
63 following: 567,
64 })
65 });
66
67 let failing_data = async_data!(cx, || {
69 thread::sleep(Duration::from_millis(500));
70 Err::<String, _>("Network error: Connection refused".to_string())
71 });
72
73 View::vstack()
74 .spacing(1)
75 .child(
76 View::boxed()
78 .border(true)
79 .padding(1)
80 .child(
81 View::vstack()
82 .child(View::styled_text("Async Data Loading Demo").bold().build())
83 .child(
84 View::styled_text(
85 "Demonstrates use_async for loading data with loading/error states",
86 )
87 .dim()
88 .build(),
89 )
90 .build(),
91 )
92 .build(),
93 )
94 .child(
95 View::hstack()
97 .spacing(1)
98 .child(
100 View::boxed()
101 .flex(1)
102 .border(true)
103 .padding(1)
104 .child(render_profile_section(&profile))
105 .build(),
106 )
107 .child(
109 View::boxed()
110 .flex(1)
111 .border(true)
112 .padding(1)
113 .child(render_stats_section(&stats))
114 .build(),
115 )
116 .child(
118 View::boxed()
119 .flex(1)
120 .border(true)
121 .padding(1)
122 .child(render_error_section(&failing_data))
123 .build(),
124 )
125 .build(),
126 )
127 .child(
128 View::boxed()
130 .border(true)
131 .padding(1)
132 .child(
133 View::vstack()
134 .child(View::text(format!(
135 "Overall status: {}",
136 if profile.is_loading() || stats.is_loading() {
137 "Loading..."
138 } else if profile.is_error() || stats.is_error() || failing_data.is_error() {
139 "Some requests failed"
140 } else {
141 "All data loaded"
142 }
143 )))
144 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
145 .build(),
146 )
147 .build(),
148 )
149 .child(
150 View::modal()
151 .visible(show_help.get())
152 .title("Example 24: Async Data")
153 .on_dismiss(with!(show_help => move || show_help.set(false)))
154 .child(
155 View::vstack()
156 .child(View::styled_text("What you're seeing").bold().build())
157 .child(View::text("• Three async data loads in parallel"))
158 .child(View::text("• Loading, success, and error states"))
159 .child(View::text("• Different load times for each"))
160 .child(View::gap(1))
161 .child(View::styled_text("Key concepts").bold().build())
162 .child(View::text("• async_data!() macro runs in background"))
163 .child(View::text("• Returns Async<T> enum"))
164 .child(View::text("• .is_loading() / .is_error() helpers"))
165 .child(View::text("• Pattern match for state handling"))
166 .child(View::gap(1))
167 .child(View::styled_text("Try this").bold().build())
168 .child(View::text("• Watch data load progressively"))
169 .child(View::text("• Notice the failing request"))
170 .child(View::gap(1))
171 .child(View::styled_text("Next up").bold().build())
172 .child(View::text("→ 25_context: context API"))
173 .child(View::gap(1))
174 .child(View::styled_text("Press Escape to close").dim().build())
175 .build()
176 )
177 .build()
178 )
179 .build()
180 }
181}
182
183fn render_profile_section(profile: &Async<UserProfile>) -> View {
184 View::vstack()
185 .spacing(1)
186 .child(
187 View::styled_text("User Profile")
188 .bold()
189 .color(Color::Cyan)
190 .build(),
191 )
192 .child(View::styled_text("(loads in 2s)").dim().build())
193 .child(View::gap(1))
194 .child(match profile {
195 Async::Loading => View::vstack()
196 .child(View::styled_text("Loading...").dim().build())
197 .child(View::text("[=========> ]"))
198 .build(),
199 Async::Ready(p) => View::vstack()
200 .spacing(0)
201 .child(View::text(format!("Name: {}", p.name)))
202 .child(View::text(format!("Email: {}", p.email)))
203 .child(View::text(format!("Member since: {}", p.member_since)))
204 .build(),
205 Async::Error(e) => View::styled_text(format!("Error: {}", e))
206 .color(Color::Red)
207 .build(),
208 })
209 .build()
210}
211
212fn render_stats_section(stats: &Async<Stats>) -> View {
213 View::vstack()
214 .spacing(1)
215 .child(
216 View::styled_text("User Stats")
217 .bold()
218 .color(Color::Green)
219 .build(),
220 )
221 .child(View::styled_text("(loads in 1s)").dim().build())
222 .child(View::gap(1))
223 .child(match stats {
224 Async::Loading => View::vstack()
225 .child(View::styled_text("Loading...").dim().build())
226 .child(View::text("[==================> ]"))
227 .build(),
228 Async::Ready(s) => View::vstack()
229 .spacing(0)
230 .child(View::text(format!("Posts: {}", s.posts)))
231 .child(View::text(format!("Followers: {}", s.followers)))
232 .child(View::text(format!("Following: {}", s.following)))
233 .build(),
234 Async::Error(e) => View::styled_text(format!("Error: {}", e))
235 .color(Color::Red)
236 .build(),
237 })
238 .build()
239}
240
241fn render_error_section(data: &Async<String>) -> View {
242 View::vstack()
243 .spacing(1)
244 .child(
245 View::styled_text("Failing Request")
246 .bold()
247 .color(Color::Red)
248 .build(),
249 )
250 .child(View::styled_text("(fails after 0.5s)").dim().build())
251 .child(View::gap(1))
252 .child(match data {
253 Async::Loading => View::vstack()
254 .child(View::styled_text("Loading...").dim().build())
255 .child(View::text("[=====================]"))
256 .build(),
257 Async::Ready(d) => View::text(format!("Data: {}", d)),
258 Async::Error(e) => View::vstack()
259 .child(
260 View::styled_text("Request failed!")
261 .color(Color::Red)
262 .build(),
263 )
264 .child(View::styled_text(e.clone()).dim().build())
265 .build(),
266 })
267 .build()
268}