Properly delay the navigation until state has loaded

This commit is contained in:
Jeremy Wall 2023-01-02 20:48:31 -06:00
parent 6d21626521
commit dd9ce0fca2
5 changed files with 53 additions and 14 deletions

View File

@ -11,7 +11,10 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::{BTreeMap, BTreeSet};
use std::{
collections::{BTreeMap, BTreeSet},
fmt::Debug,
};
use client_api::UserData;
use recipes::{parse, IngredientKey, Recipe, RecipeEntry};
@ -50,7 +53,6 @@ impl AppState {
}
}
#[derive(Debug)]
pub enum Message {
ResetRecipeCounts,
UpdateRecipeCount(String, usize),
@ -64,8 +66,46 @@ pub enum Message {
AddFilteredIngredient(IngredientKey),
UpdateAmt(IngredientKey, String),
SetUserData(UserData),
SaveState,
LoadState,
SaveState(Option<Box<dyn FnOnce()>>),
LoadState(Option<Box<dyn FnOnce()>>),
}
impl Debug for Message {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ResetRecipeCounts => write!(f, "ResetRecipeCounts"),
Self::UpdateRecipeCount(arg0, arg1) => f
.debug_tuple("UpdateRecipeCount")
.field(arg0)
.field(arg1)
.finish(),
Self::AddExtra(arg0, arg1) => {
f.debug_tuple("AddExtra").field(arg0).field(arg1).finish()
}
Self::RemoveExtra(arg0) => f.debug_tuple("RemoveExtra").field(arg0).finish(),
Self::UpdateExtra(arg0, arg1, arg2) => f
.debug_tuple("UpdateExtra")
.field(arg0)
.field(arg1)
.field(arg2)
.finish(),
Self::SaveRecipe(arg0) => f.debug_tuple("SaveRecipe").field(arg0).finish(),
Self::SetRecipe(arg0, arg1) => {
f.debug_tuple("SetRecipe").field(arg0).field(arg1).finish()
}
Self::SetCategoryMap(arg0) => f.debug_tuple("SetCategoryMap").field(arg0).finish(),
Self::ResetInventory => write!(f, "ResetInventory"),
Self::AddFilteredIngredient(arg0) => {
f.debug_tuple("AddFilteredIngredient").field(arg0).finish()
}
Self::UpdateAmt(arg0, arg1) => {
f.debug_tuple("UpdateAmt").field(arg0).field(arg1).finish()
}
Self::SetUserData(arg0) => f.debug_tuple("SetUserData").field(arg0).finish(),
Self::SaveState(_) => write!(f, "SaveState"),
Self::LoadState(_) => write!(f, "LoadState"),
}
}
}
pub struct StateMachine {
@ -284,16 +324,17 @@ impl MessageMapper<Message, AppState> for StateMachine {
Message::SetUserData(user_data) => {
original_copy.auth = Some(user_data);
}
Message::SaveState => {
Message::SaveState(f) => {
let original_copy = original_copy.clone();
let store = self.store.clone();
spawn_local_scoped(cx, async move {
if let Err(e) = store.save_app_state(original_copy).await {
error!(err=?e, "Error saving app state")
};
f.map(|f| f());
});
}
Message::LoadState => {
Message::LoadState(f) => {
let store = self.store.clone();
let local_store = self.local_store.clone();
spawn_local_scoped(cx, async move {
@ -305,6 +346,7 @@ impl MessageMapper<Message, AppState> for StateMachine {
&original.get().modified_amts,
&original.get().extras,
));
f.map(|f| f());
});
return;
}

View File

@ -55,14 +55,14 @@ pub fn RecipePlan<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> Vie
))
}
input(type="button", value="Reset", on:click=move |_| {
sh.dispatch(cx, Message::LoadState);
sh.dispatch(cx, Message::LoadState(None));
})
input(type="button", value="Clear All", on:click=move |_| {
sh.dispatch(cx, Message::ResetRecipeCounts);
})
input(type="button", value="Save Plan", on:click=move |_| {
// Poor man's click event signaling.
sh.dispatch(cx, Message::SaveState);
sh.dispatch(cx, Message::SaveState(None));
})
}
}

View File

@ -202,7 +202,7 @@ pub fn ShoppingList<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> V
})
input(type="button", value="Save", class="no-print", on:click=move |_| {
info!("Registering save request for inventory");
sh.dispatch(cx, Message::SaveState);
sh.dispatch(cx, Message::SaveState(None));
})
}
}

View File

@ -36,8 +36,7 @@ pub fn LoginForm<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View
debug!("authenticating against ui");
if let Some(user_data) = store.authenticate(username, password).await {
sh.dispatch(cx, Message::SetUserData(user_data));
sh.dispatch(cx, Message::LoadState);
sycamore_router::navigate("/ui/planning/plan");
sh.dispatch(cx, Message::LoadState(Some(Box::new(|| sycamore_router::navigate("/ui/planning/plan")))));
}
});
}

View File

@ -20,7 +20,6 @@ use crate::{api, routing::Handler as RouteHandler};
#[instrument]
#[component]
pub fn UI<G: Html>(cx: Scope) -> View<G> {
// FIXME(jwall): We shouldn't need to get the store from a context anymore.
api::HttpStore::provide_context(cx, "/api".to_owned());
let store = api::HttpStore::get_from_context(cx).as_ref().clone();
info!("Starting UI");
@ -29,8 +28,7 @@ pub fn UI<G: Html>(cx: Scope) -> View<G> {
let view = create_signal(cx, View::empty());
spawn_local_scoped(cx, {
async move {
sh.dispatch(cx, Message::LoadState);
// TODO(jwall): This needs to be moved into the RouteHandler
sh.dispatch(cx, Message::LoadState(None));
view.set(view! { cx,
RouteHandler(sh=sh)
});