208 lines
5.2 KiB
Rust
208 lines
5.2 KiB
Rust
use yew::prelude::*;
|
|
|
|
use crate::api;
|
|
use crate::models;
|
|
use crate::ui::main::{Main, MainPath};
|
|
use crate::uitools::routing::{self, Path as _};
|
|
use std::rc::Rc;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct BaseData {
|
|
almanac_data: Rc<Result<api::Almanac, String>>,
|
|
global_data: Rc<Result<api::Global, String>>,
|
|
observers_data: Rc<Result<api::ObserverList, String>>,
|
|
}
|
|
|
|
impl BaseData {
|
|
pub fn new(base: &models::Base) -> Result<Self, Html<MainApp>> {
|
|
use crate::uitools::loading::{loading, load_error};
|
|
|
|
let almanac = base.almanac().get();
|
|
let global = base.global().get();
|
|
let observers = base.observers().get();
|
|
|
|
let almanac_data = if let Some(almanac_data) = almanac {
|
|
if let Err(e) = &*almanac_data {
|
|
return Err(load_error("almanac", || Msg::Refresh, e.as_str()));
|
|
}
|
|
almanac_data
|
|
} else {
|
|
return Err(loading("almanac"));
|
|
};
|
|
let global_data = if let Some(global_data) = global {
|
|
if let Err(e) = &*global_data {
|
|
return Err(load_error("global", || Msg::Refresh, e.as_str()));
|
|
}
|
|
global_data
|
|
} else {
|
|
return Err(loading("global"));
|
|
};
|
|
let observers_data = if let Some(observers_data) = observers {
|
|
if let Err(e) = &*observers_data {
|
|
return Err(load_error("observers", || Msg::Refresh, e.as_str()));
|
|
}
|
|
observers_data
|
|
} else {
|
|
return Err(loading("observers"));
|
|
};
|
|
Ok(Self {
|
|
almanac_data,
|
|
global_data,
|
|
observers_data,
|
|
})
|
|
}
|
|
|
|
pub fn almanac(&self) -> &api::Almanac {
|
|
(*self.almanac_data).as_ref().unwrap()
|
|
}
|
|
|
|
pub fn global(&self) -> &api::Global {
|
|
(*self.global_data).as_ref().unwrap()
|
|
}
|
|
|
|
pub fn observers(&self) -> &api::ObserverList {
|
|
(*self.observers_data).as_ref().unwrap()
|
|
}
|
|
}
|
|
|
|
pub enum Msg {
|
|
Goto(MainPath),
|
|
OnPopState(String),
|
|
Refresh,
|
|
Notify(()),
|
|
}
|
|
|
|
pub struct MainApp {
|
|
base: models::Base,
|
|
_base_cbr: models::CallbackRegistration,
|
|
world_geo: models::WorldGeo,
|
|
_world_geo_cbr: models::CallbackRegistration,
|
|
_redraw_1sec: yew::services::interval::IntervalTask,
|
|
path: MainPath,
|
|
crumbs: Vec<MainPath>,
|
|
}
|
|
|
|
impl Component for MainApp {
|
|
type Message = Msg;
|
|
type Properties = ();
|
|
|
|
fn create(_properties: Self::Properties, mut link: ComponentLink<Self>) -> Self {
|
|
let mut interval_service = yew::services::IntervalService::new();
|
|
|
|
let onpopstate = |v: stdweb::Value| -> Msg {
|
|
Msg::OnPopState(v.into_string().unwrap())
|
|
};
|
|
let onpopstate = link.send_back(onpopstate);
|
|
let onpopstate = move |v| { onpopstate.emit(v) };
|
|
|
|
let pathstr = stdweb::js!{
|
|
window.onpopstate = function(event) {
|
|
@{onpopstate}(window.location.hash.substr(1));
|
|
};
|
|
|
|
return window.location.hash.substr(1);
|
|
}.into_string().unwrap();
|
|
|
|
let path = MainPath::parse(&pathstr);
|
|
let crumbs = routing::crumbs(&path);
|
|
|
|
crate::log("starting app");
|
|
|
|
let config = std::rc::Rc::new(crate::config::ConfigData {
|
|
base_url: String::from("https://galmon.eu"),
|
|
});
|
|
let base = models::Base::new(config.clone());
|
|
|
|
let world_geo = models::WorldGeo::new(config.clone());
|
|
|
|
Self {
|
|
_base_cbr: base.register(link.send_back(Msg::Notify)),
|
|
_world_geo_cbr: world_geo.register(link.send_back(Msg::Notify)),
|
|
_redraw_1sec: interval_service.spawn(std::time::Duration::from_secs(/* TODO */ 100), link.send_back(|_| Msg::Notify(()))),
|
|
base,
|
|
world_geo,
|
|
path,
|
|
crumbs,
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
|
match msg {
|
|
Msg::Goto(path) => {
|
|
if self.path == path {
|
|
false
|
|
} else {
|
|
self.path = path;
|
|
self.crumbs = routing::crumbs(&self.path);
|
|
let pathstr = routing::make_path(&self.path).unwrap();
|
|
stdweb::js!{ @(no_return)
|
|
window.location = "#" + @{pathstr};
|
|
};
|
|
true
|
|
}
|
|
},
|
|
Msg::OnPopState(path) => {
|
|
let path = MainPath::parse(&path);
|
|
self.update(Msg::Goto(path))
|
|
},
|
|
Msg::Refresh => {
|
|
self.base.refresh();
|
|
false
|
|
},
|
|
Msg::Notify(()) => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MainApp {
|
|
fn view_crumb(&self, crumb: &MainPath) -> Html<Self> {
|
|
let crumb = crumb.clone();
|
|
let title = format!("{}", crumb);
|
|
if self.path == crumb {
|
|
html!{
|
|
<li class="nav-item active">
|
|
<button class="nav-link btn btn-light" onclick=move |_| Msg::Goto(crumb.clone())>{ title }</button>
|
|
// <a class="nav-link" onclick=move |_| Msg::Goto(crumb.clone())>{ title }</a>
|
|
</li>
|
|
}
|
|
} else {
|
|
html!{
|
|
<li class="nav-item">
|
|
<button class="nav-link btn btn-light" onclick=move |_| Msg::Goto(crumb.clone())>{ title }</button>
|
|
// <a class="nav-link" onclick=move |_| Msg::Goto(crumb.clone())>{ title }</a>
|
|
</li>
|
|
}
|
|
}
|
|
}
|
|
|
|
fn view_wait(&self) -> Html<Self> {
|
|
match BaseData::new(&self.base) {
|
|
Err(html) => html,
|
|
Ok(base) => html! {
|
|
<Main ongoto=Msg::Goto path=self.path.clone() base=base world_geo=self.world_geo.clone()/>
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Renderable<MainApp> for MainApp {
|
|
fn view(&self) -> Html<Self> {
|
|
html!{
|
|
<div class="container-fluid">
|
|
<nav class="mb-3 navbar navbar-expand-lg navbar-light bg-light select-none">
|
|
<a class="navbar-brand" href="">{"galmon"}</a>
|
|
<ul class="navbar-nav">
|
|
<li class="nav-item">
|
|
<button class="nav-link btn btn-light" onclick=|_| Msg::Refresh>
|
|
{ crate::uitools::icons::icon_refresh() }
|
|
</button>
|
|
</li>
|
|
{ for self.crumbs.iter().map(|c| self.view_crumb(c)) }
|
|
</ul>
|
|
</nav>
|
|
{ self.view_wait() }
|
|
</div>
|
|
}
|
|
}
|
|
}
|