Web UI with a functional async-await boundary!
This commit is contained in:
parent
6154bf2c0b
commit
6eca450346
|
@ -14,3 +14,4 @@ bigboy
|
||||||
.tmp*
|
.tmp*
|
||||||
node_modules
|
node_modules
|
||||||
dist/
|
dist/
|
||||||
|
yarn-error.log
|
||||||
|
|
|
@ -3341,7 +3341,10 @@ name = "tenebrous-web-ui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"grpc-web-client",
|
"grpc-web-client",
|
||||||
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
"yew",
|
"yew",
|
||||||
"yew-router",
|
"yew-router",
|
||||||
"yewdux",
|
"yewdux",
|
||||||
|
|
|
@ -15,7 +15,10 @@ yewtil = {version = "0.3" }
|
||||||
yew-router = {version = "0.14" }
|
yew-router = {version = "0.14" }
|
||||||
yewdux = {version = "^0.6" }
|
yewdux = {version = "^0.6" }
|
||||||
wasm-bindgen = { version = "0.2" }
|
wasm-bindgen = { version = "0.2" }
|
||||||
|
wasm-bindgen-futures = "0.4"
|
||||||
|
js-sys = "0.3"
|
||||||
grpc-web-client = "0.1"
|
grpc-web-client = "0.1"
|
||||||
|
web-sys = "0.3"
|
||||||
|
|
||||||
# hopefully we can add grpc-web later instead of graphql.
|
# hopefully we can add grpc-web later instead of graphql.
|
||||||
# prost = { version = "0.7.0", default-features = false }
|
# prost = { version = "0.7.0", default-features = false }
|
||||||
|
|
|
@ -1,98 +1,84 @@
|
||||||
use oaths::OathsList;
|
use rooms::RoomList;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_router::{components::RouterAnchor, prelude::*};
|
use yew_router::{components::RouterAnchor, prelude::*};
|
||||||
|
|
||||||
pub mod grpc;
|
pub mod grpc;
|
||||||
pub mod oaths;
|
pub mod rooms;
|
||||||
|
|
||||||
#[derive(Switch, Clone, Debug)]
|
#[derive(Switch, Clone, Debug)]
|
||||||
pub enum AppRoute {
|
pub enum AppRoute {
|
||||||
#[to = "/oaths"]
|
#[to = "/rooms"]
|
||||||
Oaths,
|
Rooms,
|
||||||
#[to = "/commitments"]
|
#[to = "/rooms/{room_id}"]
|
||||||
Commitments,
|
Room(String),
|
||||||
#[to = "/studies"]
|
|
||||||
Studies,
|
|
||||||
#[to = "/divination"]
|
|
||||||
RunicDivination,
|
|
||||||
#[to = "/"]
|
#[to = "/"]
|
||||||
Index,
|
Index,
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppRouter = Router<AppRoute>;
|
type AppRouter = Router<AppRoute>;
|
||||||
type AppAnchor = RouterAnchor<AppRoute>;
|
type AppAnchor = RouterAnchor<AppRoute>; //For rendering clickable links.
|
||||||
|
|
||||||
fn render_route(switch: AppRoute) -> Html {
|
fn render_route(switch: AppRoute) -> Html {
|
||||||
match switch {
|
match switch {
|
||||||
AppRoute::Oaths => {
|
AppRoute::Rooms => {
|
||||||
html! {
|
html! {
|
||||||
<OathsList />
|
<RoomList />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AppRoute::Commitments => {
|
AppRoute::Room(room_id) => {
|
||||||
html! {
|
html! {
|
||||||
<div>{"This is the commitments page."}</div>
|
<div>{"This is the specifi roompage."}</div>
|
||||||
}
|
|
||||||
}
|
|
||||||
AppRoute::Studies => {
|
|
||||||
html! {
|
|
||||||
<div>{"This is the studies page."}</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppRoute::RunicDivination => {
|
|
||||||
html! {
|
|
||||||
<div>{"This is the runic divination page."}</div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AppRoute::Index => {
|
AppRoute::Index => {
|
||||||
html! {
|
html! {
|
||||||
<div>{"This is the index."}</div>
|
<RoomList />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AppMenu;
|
// struct AppMenu;
|
||||||
|
|
||||||
impl Component for AppMenu {
|
// impl Component for AppMenu {
|
||||||
type Message = ();
|
// type Message = ();
|
||||||
type Properties = ();
|
// type Properties = ();
|
||||||
|
|
||||||
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
// fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
||||||
Self
|
// Self
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
|
// fn update(&mut self, _msg: Self::Message) -> ShouldRender {
|
||||||
false
|
// false
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
// fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
||||||
false
|
// false
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn view(&self) -> Html {
|
// fn view(&self) -> Html {
|
||||||
html! {
|
// html! {
|
||||||
<ul>
|
// <ul>
|
||||||
<li>
|
// <li>
|
||||||
<AppAnchor route=AppRoute::Index>{"Home"}</AppAnchor>
|
// <AppAnchor route=AppRoute::Index>{"Home"}</AppAnchor>
|
||||||
</li>
|
// </li>
|
||||||
<li>
|
// <li>
|
||||||
<AppAnchor route=AppRoute::Oaths>{"Oaths"}</AppAnchor>
|
// <AppAnchor route=AppRoute::Oaths>{"Oaths"}</AppAnchor>
|
||||||
</li>
|
// </li>
|
||||||
<li>
|
// <li>
|
||||||
<AppAnchor route=AppRoute::Commitments>{"Commitments"}</AppAnchor>
|
// <AppAnchor route=AppRoute::Commitments>{"Commitments"}</AppAnchor>
|
||||||
</li>
|
// </li>
|
||||||
<li>
|
// <li>
|
||||||
<AppAnchor route=AppRoute::Studies>{"Studies"}</AppAnchor>
|
// <AppAnchor route=AppRoute::Studies>{"Studies"}</AppAnchor>
|
||||||
</li>
|
// </li>
|
||||||
<li>
|
// <li>
|
||||||
<AppAnchor route=AppRoute::RunicDivination>{"Runic Divination"}</AppAnchor>
|
// <AppAnchor route=AppRoute::RunicDivination>{"Runic Divination"}</AppAnchor>
|
||||||
</li>
|
// </li>
|
||||||
</ul>
|
// </ul>
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
struct App;
|
struct App;
|
||||||
|
|
||||||
|
@ -116,7 +102,6 @@ impl Component for App {
|
||||||
html! {
|
html! {
|
||||||
<div>
|
<div>
|
||||||
{"Hello World"}
|
{"Hello World"}
|
||||||
<AppMenu />
|
|
||||||
<AppRouter render=AppRouter::render(render_route) />
|
<AppRouter render=AppRouter::render(render_route) />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
Loading…
Reference in New Issue