Skip to content

Commit a21dae6

Browse files
committed
login-flow demo for blog post
1 parent 522d3bd commit a21dae6

File tree

6 files changed

+149
-2
lines changed

6 files changed

+149
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"examples/web-simple-examples/",
1212
"examples/web-todomvc/",
1313
"examples/big-list/",
14+
"examples/login-flow/",
1415
"examples/x-bow-playground/",
1516
"examples/guide-project/",
1617
"examples/async_ui_web_draggable/",

Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ demo_x_bow_playground:
3232
rm gh-pages/demos/x-bow-playground/pkg/.gitignore
3333
cp examples/x-bow-playground/index.html gh-pages/demos/x-bow-playground/
3434

35+
.PHONY: demo_login_flow
36+
demo_login_flow:
37+
@echo "====> building demo login_flow"
38+
# before running make, run `git worktree add gh-pages/ gh-pages
39+
rm -rf gh-pages/demos/login-flow
40+
wasm-pack build --release --target web --out-dir ../../gh-pages/demos/login-flow/pkg examples/login-flow
41+
rm gh-pages/demos/login-flow/pkg/.gitignore
42+
cp examples/login-flow/index.html gh-pages/demos/login-flow/
3543

3644
.PHONY: demos
37-
demos: demo_todomvc demo_simple demo_x_bow_playground
45+
demos: demo_todomvc demo_simple demo_x_bow_playground demo_login_flow

examples/login-flow/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "login-flow"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[lib]
9+
crate-type = ["cdylib"]
10+
11+
[dependencies]
12+
async_ui_web = { path = "../../async_ui_web/" }
13+
wasm-bindgen = "0.2.87"
14+
futures-lite = "1.13.0"
15+
console_error_panic_hook = "0.1.6"
16+
gloo-timers = { version = "0.2", features = ["futures"] }
17+

examples/login-flow/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
5+
</head>
6+
<body style="display: flex; flex-direction: column;">
7+
<script type="module">
8+
import init from './pkg/login_flow.js';
9+
init();
10+
</script>
11+
</body>
12+
</html>

examples/login-flow/src/lib.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use async_ui_web::{
2+
event_traits::EmitElementEvent,
3+
html::{Button, Input},
4+
join, mount, race,
5+
shortcut_traits::{ShortcutRenderStr, UiFutureExt},
6+
};
7+
8+
#[wasm_bindgen::prelude::wasm_bindgen(start)]
9+
pub fn run() {
10+
mount(app());
11+
}
12+
13+
async fn app() {
14+
match connector().await {
15+
Ok(data) => format!("the connector returned: {data}").render().await,
16+
Err(_) => "the connector errored :(".render().await,
17+
}
18+
}
19+
20+
async fn connector() -> Result<String, ()> {
21+
// loop until successful login
22+
let token = loop {
23+
let (username, password) = show_login_form().await;
24+
if let Ok(token) = login(&username, &password).await {
25+
break token;
26+
}
27+
};
28+
let list = fetch_list(&token).await?; // 👈 try operator
29+
let chosen_data_id = show_list(&list).await;
30+
Ok(get_data(&token, chosen_data_id).await?) // 👈 try operator
31+
}
32+
33+
async fn show_login_form() -> (String, String) {
34+
let (username, password) = (Input::new(), Input::new_password());
35+
username.set_placeholder("Username");
36+
password.set_placeholder("Password");
37+
let button = Button::new();
38+
39+
button
40+
.until_click()
41+
.meanwhile(join((
42+
"username is 'user' and password is 'asdf'".render(),
43+
username.render(),
44+
password.render(),
45+
button.render("Login".render()),
46+
)))
47+
.await;
48+
49+
(username.value(), password.value())
50+
}
51+
52+
async fn login(username: &str, password: &str) -> Result<String, ()> {
53+
// take some time
54+
gloo_timers::future::TimeoutFuture::new(1500)
55+
.meanwhile("logging in".render())
56+
.await;
57+
58+
match (username, password) {
59+
("user", "asdf") => Ok("secrettoken".to_string()),
60+
_ => {
61+
let retry_btn = Button::new();
62+
retry_btn
63+
.until_click()
64+
.meanwhile(join((
65+
"incorrect username/password".render(),
66+
retry_btn.render("Retry".render()),
67+
)))
68+
.await;
69+
Err(())
70+
}
71+
}
72+
}
73+
74+
type DataId = i32;
75+
async fn fetch_list(_token: &str) -> Result<Vec<DataId>, ()> {
76+
// take some time
77+
gloo_timers::future::TimeoutFuture::new(1500)
78+
.meanwhile("fetching list of available data".render())
79+
.await;
80+
81+
Ok(vec![1, 2, 3, 4])
82+
}
83+
84+
async fn show_list(list: &[DataId]) -> &DataId {
85+
race((
86+
"which one do you want?".render().pend_after(),
87+
race(
88+
list.iter()
89+
.map(|id| async move {
90+
let btn = Button::new();
91+
btn.until_click()
92+
.meanwhile(btn.render(format!("data with id {id}").render()))
93+
.await;
94+
id
95+
})
96+
.collect::<Vec<_>>(),
97+
),
98+
))
99+
.await
100+
}
101+
102+
async fn get_data(_token: &str, id: &DataId) -> Result<String, ()> {
103+
// take some time
104+
gloo_timers::future::TimeoutFuture::new(1500)
105+
.meanwhile("fetching data".render())
106+
.await;
107+
108+
Ok(format!("data id={id}: blah blah blah"))
109+
}

x-bow/src/path_ext.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ pub trait PathExt: Path {
426426
/// This is useful when you are handling the type that implements `Path`
427427
/// directly. Most of the time, though, you will already be working with
428428
/// `PathBuilder`s.
429-
///
429+
///
430430
/// ```
431431
/// # use x_bow::{Path, PathExt, PathExtGuaranteed, Store, Trackable, IntoPath};
432432
/// fn modify_string(path: impl Path<Out = String>) {

0 commit comments

Comments
 (0)