fn account_access(state: &AppState, current_route: &str) -> RouteDecision {
if state.session.is_some() {
RouteDecision::Allow
} else {
RouteRedirect::replace("/sign-in")
.return_to(current_route)
.into()
}
}
let decision = account_access(view.state(), &view.env().current_route);
Router::<AppState>::new()
.with_path(view.state().current_path.clone())
.route_component(
"/account",
ProtectedRoute::new(decision, AccountRoute)
.pending(SessionLoadingRoute)
.denied(SessionUnavailableRoute),
)
.route_component("/sign-in", SignInRoute)
use fission::prelude::*;
use fission::store::{StoreErrorKind, StoreKey};
struct AppState {
current_path: String,
session: Option<Session>,
account_access: RouteDecision,
}
impl Default for AppState {
fn default() -> Self {
Self {
current_path: "/".into(),
session: None,
account_access: RouteDecision::Pending,
}
}
}
impl GlobalState for AppState {}
#[fission_reducer(LoadSession)]
fn load_session(_state: &mut AppState, ctx: &mut ReducerContext<AppState>) {
ctx.effects
.store()
.get(StoreKey::<Session>::new("auth", "session"))
.on_ok(ActionEnvelope::from(SessionLoaded))
.on_err(ActionEnvelope::from(SessionLoadFailed));
}
#[fission_reducer(SessionLoaded)]
fn session_loaded(state: &mut AppState, ctx: &mut ReducerContext<AppState>) {
match ctx.input.store_value::<Session>() {
Some(Ok(session)) => {
state.session = Some(session);
state.account_access = RouteDecision::Allow;
}
Some(Err(error)) if error.kind == StoreErrorKind::InvalidRequest => {
state.session = None;
state.account_access = RouteRedirect::replace("/sign-in")
.return_to("/account")
.into();
}
Some(Err(error)) => {
state.account_access = RouteDecision::Deny;
// Store `error.message` separately when the denied page should
// explain why session restoration failed.
}
None => state.account_access = RouteDecision::Deny,
}
}
#[fission_reducer(SessionLoadFailed)]
fn session_load_failed(state: &mut AppState) {
state.account_access = RouteDecision::Deny;
}
let decision = view.state().account_access.clone();
Router::<AppState>::new()
.with_path(view.state().current_path.clone())
.route_component(
"/account",
ProtectedRoute::new(decision, AccountRoute)
.pending(SessionLoadingRoute)
.denied(SessionUnavailableRoute),
)
.route_component("/sign-in", SignInRoute)
WebApp::<AppState, _>::new(App)
.with_startup_action(LoadSession)
.run()
Decision | Built branch | Shell behavior |
|---|---|---|
Pending | pending | Waits for application state to change. |
Allow | allowed | Builds the protected component. |
Deny | denied | Shows denial; SSR responds with 403. |
Redirect | pending | Declares navigation after the build; SSR responds with 302. |