Skip to content
This repository was archived by the owner on Jul 19, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions crates/yew_router_macro/src/switch/enum_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ pub fn generate_enum_impl(
let token_stream = quote! {
#impl_line
{
fn from_route_part<__T: ::yew_router::route::RouteState>(route: ::yew_router::route::Route<__T>) -> (::std::option::Option<Self>, ::std::option::Option<__T>) {
let mut state = route.state;
let route_string = route.route;
fn from_route_part<__T: ::yew_router::route::RouteState>(route: String, mut state: Option<__T>) -> (::std::option::Option<Self>, ::std::option::Option<__T>) {
let route_string = route;
#(#variant_matchers)*

return (::std::option::Option::None, state)
Expand Down Expand Up @@ -73,10 +72,8 @@ fn build_variant_from_captures(
let (v, s) = match captures.remove(#key) {
::std::option::Option::Some(value) => {
<#field_ty as ::yew_router::Switch>::from_route_part(
::yew_router::route::Route {
route: value,
state,
}
value,
state,
)
}
::std::option::Option::None => {
Expand Down Expand Up @@ -129,10 +126,8 @@ fn build_variant_from_captures(
let (v, s) = match drain.next() {
::std::option::Option::Some(value) => {
<#field_ty as ::yew_router::Switch>::from_route_part(
::yew_router::route::Route {
route: value,
state,
}
value,
state,
)
},
::std::option::Option::None => {
Expand Down
17 changes: 6 additions & 11 deletions crates/yew_router_macro/src/switch/struct_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ pub fn generate_struct_impl(item: SwitchItem, generics: Generics) -> TokenStream
let token_stream = quote! {
#impl_line
{
fn from_route_part<__T: ::yew_router::route::RouteState>(route: ::yew_router::route::Route<__T>) -> (::std::option::Option<Self>, ::std::option::Option<__T>) {
fn from_route_part<__T: ::yew_router::route::RouteState>(route: String, mut state: Option<__T>) -> (::std::option::Option<Self>, ::std::option::Option<__T>) {

#matcher
let mut state = route.state;
let route_string = route.route;
let route_string = route;

#build_from_captures

Expand Down Expand Up @@ -62,10 +61,8 @@ fn build_struct_from_captures(ident: &Ident, fields: &Fields) -> TokenStream2 {
let (v, s) = match captures.remove(#key) {
::std::option::Option::Some(value) => {
<#field_ty as ::yew_router::Switch>::from_route_part(
::yew_router::route::Route {
route: value,
state,
}
value,
state,
)
}
::std::option::Option::None => {
Expand Down Expand Up @@ -108,10 +105,8 @@ fn build_struct_from_captures(ident: &Ident, fields: &Fields) -> TokenStream2 {
let (v, s) = match drain.next() {
::std::option::Option::Some(value) => {
<#field_ty as ::yew_router::Switch>::from_route_part(
::yew_router::route::Route {
route: value,
state,
}
value,
state,
)
},
::std::option::Option::None => {
Expand Down
11 changes: 3 additions & 8 deletions examples/minimal/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,7 @@ impl Component for Model {
let mut route_service: RouteService<()> = RouteService::new();
let route = route_service.get_route();
let route = Route::from(route);
let callback = link.callback(|(route, state)| -> Msg {
Msg::RouteChanged(Route {
route,
state: Some(state),
})
});
let callback = link.callback(Msg::RouteChanged);
route_service.register_callback(callback);

Model {
Expand All @@ -78,9 +73,9 @@ impl Component for Model {
AppRoute::C => format!("/c"),
};
self.route_service.set_route(&route_string, ());
self.route = Route {
self.route = Route{
route: route_string,
state: None,
state: (),
};
}
}
Expand Down
22 changes: 10 additions & 12 deletions src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub use dispatcher::RouteAgentDispatcher;
#[derive(Debug)]
pub enum Msg<T> {
/// Message for when the route is changed.
BrowserNavigationRouteChanged((String, T)),
BrowserNavigationRouteChanged(Route<T>), // TODO make this a route?
}

/// Input message type for interacting with the `RouteAgent'.
Expand Down Expand Up @@ -105,10 +105,8 @@ where

fn update(&mut self, msg: Self::Message) {
match msg {
Msg::BrowserNavigationRouteChanged((_route_string, state)) => {
Msg::BrowserNavigationRouteChanged(route) => {
trace!("Browser navigated");
let mut route = Route::current_route(&self.route_service);
route.state = Some(state);
for sub in &self.subscribers {
self.link.respond(*sub, route.clone());
}
Expand All @@ -125,24 +123,24 @@ where
RouteRequest::ReplaceRoute(route) => {
let route_string: String = route.to_string();
self.route_service
.replace_route(&route_string, route.state.unwrap_or_default());
let route = Route::current_route(&self.route_service);
.replace_route(&route_string, route.state);
let route = self.route_service.get_route();
for sub in &self.subscribers {
self.link.respond(*sub, route.clone());
}
}
RouteRequest::ReplaceRouteNoBroadcast(route) => {
let route_string: String = route.to_string();
self.route_service
.replace_route(&route_string, route.state.unwrap_or_default());
.replace_route(&route_string, route.state);
}
RouteRequest::ChangeRoute(route) => {
let route_string: String = route.to_string();
// set the route
self.route_service
.set_route(&route_string, route.state.unwrap_or_default());
// get the new route. This will contain a default state object
let route = Route::current_route(&self.route_service);
.set_route(&route_string, route.state);
// get the new route.
let route = self.route_service.get_route();
// broadcast it to all listening components
for sub in &self.subscribers {
self.link.respond(*sub, route.clone());
Expand All @@ -151,10 +149,10 @@ where
RouteRequest::ChangeRouteNoBroadcast(route) => {
let route_string: String = route.to_string();
self.route_service
.set_route(&route_string, route.state.unwrap_or_default());
.set_route(&route_string, route.state);
}
RouteRequest::GetCurrentRoute => {
let route = Route::current_route(&self.route_service);
let route = self.route_service.get_route();
self.link.respond(who, route.clone());
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ pub use self::{router_button::RouterButton, router_link::RouterAnchor, router_li
use crate::RouterState;

// TODO This should also be PartialEq and Clone. Its blocked on Children not supporting that.
// TODO This should no longer take link & String, and instead take a route: T implementing Switch
/// Properties for `RouterButton` and `RouterLink`.
#[derive(Properties, Default, Debug)]
pub struct Props<T: RouterState> {
/// The route that will be set when the component is clicked.
pub link: String,
/// The state to set when changing the route.
pub state: Option<T>,
pub state: T,
#[deprecated(note = "Use children field instead (nested html)")]
/// The text to display.
pub text: String,
Expand Down
24 changes: 4 additions & 20 deletions src/route.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Wrapper around route url string, and associated history state.
use crate::service::RouteService;
use serde::{Deserialize, Serialize};
use std::{fmt, ops::Deref};
use stdweb::{unstable::TryFrom, Value};
Expand All @@ -16,7 +15,7 @@ pub struct Route<T = ()> {
/// The route string
pub route: String,
/// The state stored in the history api
pub state: Option<T>,
pub state: T,
}

/// Formats a path, query, and fragment into a string.
Expand All @@ -32,33 +31,18 @@ pub(crate) fn format_route_string(path: &str, query: &str, fragment: &str) -> St
)
}

impl<T> Route<T> {
/// Gets the current route from the route service.
///
/// # Note
/// It does not get the current state.
/// That is only provided via events.
/// See [RouteService.register_callback](struct.RouteService.html#method.register_callback) to
/// acquire state.
pub fn current_route(route_service: &RouteService<T>) -> Self {
let route = route_service.get_route();
// TODO, should try to get the state using the history api once that is exposed through
// stdweb. https://github.com/koute/stdweb/issues/371
Route { route, state: None }
}
}

impl<T> fmt::Display for Route<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
std::fmt::Display::fmt(&self.route, f)
}
}

impl<T> From<&str> for Route<T> {
// This is getting removed anyway
impl<T: Default> From<&str> for Route<T> {
fn from(string: &str) -> Route<T> {
Route {
route: string.to_string(),
state: None,
state: T::default(),
}
}
}
Expand Down
54 changes: 43 additions & 11 deletions src/service.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
//! Service that interfaces with the browser to handle routing.

use stdweb::{
web::{event::PopStateEvent, window, EventListenerHandle, History, IEventTarget, Location},
Value,
};
use stdweb::{web::{event::PopStateEvent, window, EventListenerHandle, History, IEventTarget, Location}, Value};
use yew::callback::Callback;

use crate::route::RouteState;
use crate::route::{RouteState, Route};
use std::marker::PhantomData;
use stdweb::unstable::TryFrom;
use stdweb::unstable::TryInto;
use stdweb::js;

/// A service that facilitates manipulation of the browser's URL bar and responding to browser events
/// when users press 'forward' or 'back'.
Expand Down Expand Up @@ -53,10 +52,7 @@ impl<T> RouteService<T> {
crate::route::format_route_string(&path, &query, &fragment)
}

/// Gets the concatenated path, query, and fragment.
pub fn get_route(&self) -> String {
Self::get_route_from_location(&self.location)
}


/// Gets the path name of the current url.
pub fn get_path(&self) -> String {
Expand All @@ -81,7 +77,7 @@ where
/// Registers a callback to the route service.
/// Callbacks will be called when the History API experiences a change such as
/// popping a state off of its stack when the forward or back buttons are pressed.
pub fn register_callback(&mut self, callback: Callback<(String, T)>) {
pub fn register_callback(&mut self, callback: Callback<Route<T>>) {
self.event_listener = Some(window().add_event_listener(move |event: PopStateEvent| {
let state_value: Value = event.state();
let state_string: String = String::try_from(state_value).unwrap_or_default();
Expand All @@ -96,7 +92,8 @@ where
let location: Location = window().location().unwrap();
let route: String = Self::get_route_from_location(&location);

callback.emit((route.clone(), state))

callback.emit(Route{route, state})
}));
}

Expand All @@ -121,4 +118,39 @@ where
});
let _ = self.history.replace_state(state_string, "", Some(route));
}

/// Gets the concatenated path, query, and fragment.
pub fn get_route(&self) -> Route<T> {
let route_string = Self::get_route_from_location(&self.location);
let state: T = get_state_string(&self.history)
.or_else(|| {
log::trace!("History state is empty");
None
})
.and_then(|state_string| -> Option<Option<T>>{
serde_json::from_str(&state_string)
.ok()
.or_else(|| {
log::error!("Could not deserialize state string");
None
})
})
.and_then(std::convert::identity) // flatten
.unwrap_or_default();
Route {
route: route_string,
state
}
}
}

fn get_state(history: &History) -> Value {
js!(
return @{history}.state;
)
}

fn get_state_string(history: &History) -> Option<String> {
get_state(history).try_into().ok()
}

Loading