Last active
August 16, 2019 03:23
-
-
Save wayeast/59b510f5163ca78273c413c34d9b2b12 to your computer and use it in GitHub Desktop.
Toy seed app to highlight puzzling issues
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Replace *lib.rs* in the seed-quickstart project with this file, | |
// then run `cargo make build && cargo make serve`. | |
#[macro_use] | |
extern crate seed; | |
#[macro_use] | |
extern crate serde; | |
use seed::prelude::*; | |
// Model | |
enum AuthModel { | |
Unknown, | |
Unauthenticated, | |
Authenticated(String), | |
} | |
enum StuffModel { | |
Disconnected, | |
Connected(String), | |
} | |
struct Model { | |
auth_model: AuthModel, | |
stuff_model: StuffModel, | |
} | |
impl Default for Model { | |
fn default() -> Self { | |
Self { | |
auth_model: AuthModel::Unknown, | |
stuff_model: StuffModel::Disconnected, | |
} | |
} | |
} | |
// Update | |
#[derive(Clone, Serialize, Deserialize, Debug)] | |
enum Msg { | |
WhoAmI, | |
Auth(AuthMsg), | |
Stuff(StuffMsg), | |
} | |
#[derive(Clone, Serialize, Deserialize, Debug)] | |
enum AuthMsg { | |
Login(Option<String>), | |
} | |
#[derive(Clone, Serialize, Deserialize, Debug)] | |
enum StuffMsg { | |
Connect(String), | |
} | |
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) { | |
log!("update fn handling msg:", msg); | |
match msg { | |
Msg::Auth(msg) => { | |
update_auth(msg, &mut model.auth_model, &mut orders.proxy(Msg::Auth)); | |
} | |
Msg::Stuff(msg) => { | |
update_stuff(msg, &mut model.stuff_model, &mut orders.proxy(Msg::Stuff)); | |
} | |
Msg::WhoAmI => { | |
if let AuthModel::Authenticated(user) = &model.auth_model { | |
update_stuff( | |
StuffMsg::Connect(user.clone()), | |
&mut model.stuff_model, | |
&mut orders.proxy(Msg::Stuff), | |
); | |
} | |
} | |
} | |
} | |
fn update_auth(msg: AuthMsg, model: &mut AuthModel, _orders: &mut impl Orders<AuthMsg>) { | |
log!("update auth fn handling msg:", msg); | |
match msg { | |
AuthMsg::Login(res) => { | |
log!("updating login with res:", res); | |
match res { | |
Some(user) => *model = AuthModel::Authenticated(user), | |
None => *model = AuthModel::Unauthenticated, | |
} | |
} | |
} | |
} | |
/* | |
* 1) Here, I am using a pattern taken from the *server_integration* example (example_e) | |
* and which I use in *update_auth* above -- replacing *model with a new StuffModel value. | |
* Whereas following this same update in *update_auth* the dom is correctly rendered, here | |
* rendering does not appear to be triggered. | |
* **********************************************************************************/ | |
fn update_stuff(msg: StuffMsg, model: &mut StuffModel, _orders: &mut impl Orders<StuffMsg>) { | |
log!("update stuff fn handling msg:", msg); | |
match msg { | |
StuffMsg::Connect(user) => *model = StuffModel::Connected(user), // this change is not rendered (?)! | |
} | |
} | |
// View | |
fn view(model: &Model) -> impl View<Msg> { | |
match &model.auth_model { | |
AuthModel::Unknown | AuthModel::Unauthenticated => { | |
view_auth(&model.auth_model).els().map_message(Msg::Auth) | |
} | |
AuthModel::Authenticated(_) => view_stuff(&model.stuff_model).els().map_message(Msg::Stuff), | |
} | |
} | |
fn view_auth(model: &AuthModel) -> impl View<AuthMsg> { | |
match model { | |
AuthModel::Unknown => div![ | |
"auth state is unknown", | |
// check browser cookie cache | |
did_mount(|_| { | |
log!("did_mount sending Login(None)"); | |
seed::update(Msg::Auth(AuthMsg::Login(None))); | |
}), | |
], | |
AuthModel::Unauthenticated => div![ | |
// login form goes here | |
"log in as Alice", | |
button![ | |
"I am Alice", | |
simple_ev(Ev::Click, AuthMsg::Login(Some("Alice".to_string()))), | |
], | |
], | |
AuthModel::Authenticated(_) => { | |
error!("Uh oh, authenticated model should never be handled by auth view fn"); | |
div!["If you're seeing this, there is a big problem!!!"] | |
} | |
} | |
} | |
/* | |
* 2) Here, I am re-using a pattern from *view_auth* above -- when I find the model in an | |
* ambiguous state, I want to notify the app to perform some action and update state. | |
* Here, though, I have to add an additional inner div to the element -- if I use | |
* _exactly_ the same pattern as in *view_auth*, the *did_mount* closure will not fire! | |
* **********************************************************************************/ | |
fn view_stuff(model: &StuffModel) -> impl View<StuffMsg> { | |
match model { | |
StuffModel::Disconnected => div![ | |
// why does this extra div need to be here for did_mount to fire? | |
div![ | |
"not yet connected", | |
did_mount(|_| { | |
log!("did_mount sendinng whoami msg"); | |
seed::update(Msg::WhoAmI); | |
}), | |
], | |
], | |
StuffModel::Connected(user) => div![format!("Connected as {}", user),], | |
} | |
} | |
#[wasm_bindgen(start)] | |
pub fn render() { | |
seed::App::build(|_, _| Model::default(), update, view) | |
.window_events(|_| vec![trigger_update_handler()]) | |
.finish() | |
.run(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment