Web UI with a functional async-await boundary!

This commit is contained in:
projectmoon 2021-06-05 21:42:13 +00:00
parent 6154bf2c0b
commit 6eca450346
6 changed files with 167 additions and 139 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ bigboy
.tmp*
node_modules
dist/
yarn-error.log

3
Cargo.lock generated
View File

@ -3341,7 +3341,10 @@ name = "tenebrous-web-ui"
version = "0.1.0"
dependencies = [
"grpc-web-client",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"yew",
"yew-router",
"yewdux",

View File

@ -15,7 +15,10 @@ yewtil = {version = "0.3" }
yew-router = {version = "0.14" }
yewdux = {version = "^0.6" }
wasm-bindgen = { version = "0.2" }
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
grpc-web-client = "0.1"
web-sys = "0.3"
# hopefully we can add grpc-web later instead of graphql.
# prost = { version = "0.7.0", default-features = false }

View File

@ -1,98 +1,84 @@
use oaths::OathsList;
use rooms::RoomList;
use wasm_bindgen::prelude::*;
use yew::prelude::*;
use yew_router::{components::RouterAnchor, prelude::*};
pub mod grpc;
pub mod oaths;
pub mod rooms;
#[derive(Switch, Clone, Debug)]
pub enum AppRoute {
#[to = "/oaths"]
Oaths,
#[to = "/commitments"]
Commitments,
#[to = "/studies"]
Studies,
#[to = "/divination"]
RunicDivination,
#[to = "/rooms"]
Rooms,
#[to = "/rooms/{room_id}"]
Room(String),
#[to = "/"]
Index,
}
type AppRouter = Router<AppRoute>;
type AppAnchor = RouterAnchor<AppRoute>;
type AppAnchor = RouterAnchor<AppRoute>; //For rendering clickable links.
fn render_route(switch: AppRoute) -> Html {
match switch {
AppRoute::Oaths => {
AppRoute::Rooms => {
html! {
<OathsList />
<RoomList />
}
}
AppRoute::Commitments => {
AppRoute::Room(room_id) => {
html! {
<div>{"This is the commitments page."}</div>
}
}
AppRoute::Studies => {
html! {
<div>{"This is the studies page."}</div>
}
}
AppRoute::RunicDivination => {
html! {
<div>{"This is the runic divination page."}</div>
<div>{"This is the specifi roompage."}</div>
}
}
AppRoute::Index => {
html! {
<div>{"This is the index."}</div>
<RoomList />
}
}
}
}
struct AppMenu;
// struct AppMenu;
impl Component for AppMenu {
type Message = ();
type Properties = ();
// impl Component for AppMenu {
// type Message = ();
// type Properties = ();
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self
}
// fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
// Self
// }
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
false
}
// fn update(&mut self, _msg: Self::Message) -> ShouldRender {
// false
// }
fn change(&mut self, _: Self::Properties) -> ShouldRender {
false
}
// fn change(&mut self, _: Self::Properties) -> ShouldRender {
// false
// }
fn view(&self) -> Html {
html! {
<ul>
<li>
<AppAnchor route=AppRoute::Index>{"Home"}</AppAnchor>
</li>
<li>
<AppAnchor route=AppRoute::Oaths>{"Oaths"}</AppAnchor>
</li>
<li>
<AppAnchor route=AppRoute::Commitments>{"Commitments"}</AppAnchor>
</li>
<li>
<AppAnchor route=AppRoute::Studies>{"Studies"}</AppAnchor>
</li>
<li>
<AppAnchor route=AppRoute::RunicDivination>{"Runic Divination"}</AppAnchor>
</li>
</ul>
}
}
}
// fn view(&self) -> Html {
// html! {
// <ul>
// <li>
// <AppAnchor route=AppRoute::Index>{"Home"}</AppAnchor>
// </li>
// <li>
// <AppAnchor route=AppRoute::Oaths>{"Oaths"}</AppAnchor>
// </li>
// <li>
// <AppAnchor route=AppRoute::Commitments>{"Commitments"}</AppAnchor>
// </li>
// <li>
// <AppAnchor route=AppRoute::Studies>{"Studies"}</AppAnchor>
// </li>
// <li>
// <AppAnchor route=AppRoute::RunicDivination>{"Runic Divination"}</AppAnchor>
// </li>
// </ul>
// }
// }
// }
struct App;
@ -116,7 +102,6 @@ impl Component for App {
html! {
<div>
{"Hello World"}
<AppMenu />
<AppRouter render=AppRouter::render(render_route) />
</div>
}

View File

@ -1,77 +0,0 @@
use yew::prelude::*;
use yewdux::prelude::*;
use yewtil::NeqAssign;
struct Oaths;
#[derive(Clone)]
struct Oath {
title: String,
content: String,
}
#[derive(Default, Clone)]
pub(crate) struct OathState {
oaths: Vec<Oath>,
}
type OathDispatch = DispatchProps<BasicStore<OathState>>;
//Oaths list
pub(crate) struct StatefulOathsList {
dispatch: OathDispatch,
}
pub(crate) type OathsList = WithDispatch<StatefulOathsList>;
fn view_oath(oath: &Oath) -> Html {
html! {
<div>
<div>{oath.title.clone()}</div>
<div>{oath.content.clone()}</div>
</div>
}
}
impl Component for StatefulOathsList {
type Message = ();
type Properties = OathDispatch;
fn create(dispatch: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { dispatch }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
false
}
fn change(&mut self, dispatch: Self::Properties) -> ShouldRender {
self.dispatch.neq_assign(dispatch)
}
fn view(&self) -> Html {
let add_oath = self.dispatch.reduce_callback(|s| {
s.oaths.push(Oath {
title: "yolo".to_string(),
content: "nolo".to_string(),
})
});
html! {
<div>
<button onclick=add_oath>{ "Add Oath" }</button>
<ul>
{
for self.dispatch.state().oaths.iter().map(|oath| {
view_oath(oath)
})
}
</ul>
</div>
}
}
}
//New oath form
//Edit oath

113
web-ui/crate/src/rooms.rs Normal file
View File

@ -0,0 +1,113 @@
use std::sync::Arc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::{future_to_promise, spawn_local};
use web_sys::console;
use yew::prelude::*;
use yewdux::prelude::*;
use yewtil::NeqAssign;
#[derive(Clone)]
pub(crate) struct Room {
room_id: String,
display_name: String,
}
#[derive(Default, Clone)]
pub(crate) struct RoomListState {
rooms: Vec<Room>,
}
pub(crate) enum Action {
AddRoom(Room),
}
impl Reducer for RoomListState {
type Action = Action;
fn new() -> Self {
Self { rooms: vec![] }
}
fn reduce(&mut self, action: Self::Action) -> bool {
match action {
Action::AddRoom(room) => {
self.rooms.push(room.clone());
true
}
}
}
}
type RoomListDispatch = DispatchProps<ReducerStore<RoomListState>>;
//Oaths list
#[doc(hidden)]
pub(crate) struct YewduxRoomList {
dispatch: RoomListDispatch,
link: ComponentLink<YewduxRoomList>,
}
pub(crate) type RoomList = WithDispatch<YewduxRoomList>;
fn view_room(room: &Room) -> Html {
html! {
<div>
<div>{room.room_id.clone()}</div>
<div>{room.display_name.clone()}</div>
</div>
}
}
async fn do_things(dispatch: &RoomListDispatch) {
dispatch.send(Action::AddRoom(Room {
room_id: "asdf".into(),
display_name: "adslkjg".into(),
}));
}
impl Component for YewduxRoomList {
type Message = ();
type Properties = RoomListDispatch;
fn create(dispatch: Self::Properties, link: ComponentLink<Self>) -> Self {
Self { dispatch, link }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
false
}
fn change(&mut self, dispatch: Self::Properties) -> ShouldRender {
self.dispatch.neq_assign(dispatch)
}
fn view(&self) -> Html {
let dispatch = Arc::new(self.dispatch.clone());
let the_future = self.link.callback(move |_| {
let dispatch = dispatch.clone();
spawn_local(async move {
do_things(&*dispatch).await;
});
});
html! {
<div>
<button onclick=the_future>{ "Add Room" }</button>
<ul>
{
for self.dispatch.state().rooms.iter().map(|oath| {
view_room(oath)
})
}
</ul>
</div>
}
}
}
//New oath form
//Edit oath