rust-galmon-web/src/ui/app.rs

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>
}
}
}