Add dashboard demo to the showcase

This commit is contained in:
Denis Kolodin 2018-04-10 10:10:13 +03:00
parent 625fcf5231
commit 47ba9a13df
5 changed files with 216 additions and 158 deletions

View File

@ -8,3 +8,4 @@ yew = { path = "../.." }
counter = { path = "sub/counter" }
crm = { path = "sub/crm" }
custom_components = { path = "sub/custom_components" }
dashboard = { path = "sub/dashboard" }

View File

@ -3,19 +3,25 @@ extern crate yew;
extern crate counter;
extern crate crm;
extern crate custom_components;
extern crate dashboard;
use yew::prelude::*;
use yew::services::console::ConsoleService;
use yew::services::dialog::DialogService;
use yew::services::storage::{StorageService, Area};
use yew::services::fetch::FetchService;
use yew::services::websocket::WebSocketService;
use counter::Model as Counter;
use crm::Model as Crm;
use custom_components::Model as CustomComponents;
use dashboard::Model as Dashboard;
struct Context {
console: ConsoleService,
storage: StorageService,
dialog: DialogService,
web: FetchService,
ws: WebSocketService,
}
impl AsMut<ConsoleService> for Context {
@ -36,6 +42,18 @@ impl AsMut<DialogService> for Context {
}
}
impl AsMut<FetchService> for Context {
fn as_mut(&mut self) -> &mut FetchService {
&mut self.web
}
}
impl AsMut<WebSocketService> for Context {
fn as_mut(&mut self) -> &mut WebSocketService {
&mut self.ws
}
}
impl custom_components::Printer for Context {
fn print(&mut self, data: &str) {
self.console.log(data);
@ -47,6 +65,7 @@ enum Scene {
Counter,
Crm,
CustomComponents,
Dashboard,
}
enum Msg {
@ -79,6 +98,7 @@ impl Renderable<Context, Scene> for Scene {
<button onclick=|_| Msg::SwitchTo(Scene::Counter),>{ "Counter" }</button>
<button onclick=|_| Msg::SwitchTo(Scene::Crm),>{ "Crm" }</button>
<button onclick=|_| Msg::SwitchTo(Scene::CustomComponents),>{ "CustomComponents" }</button>
<button onclick=|_| Msg::SwitchTo(Scene::Dashboard),>{ "Dashboard" }</button>
{ self.view_scene() }
}
}
@ -107,6 +127,11 @@ impl Scene {
<CustomComponents: />
}
}
Scene::Dashboard => {
html! {
<Dashboard: />
}
}
}
}
}
@ -117,6 +142,8 @@ fn main() {
console: ConsoleService,
storage: StorageService::new(Area::Local),
dialog: DialogService,
web: FetchService::new(),
ws: WebSocketService::new(),
};
let app: App<_, Scene> = App::new(context);
app.mount_to_body();

View File

@ -7,4 +7,4 @@ authors = ["Denis Kolodin <deniskolodin@gmail.com>"]
failure = "0.1"
serde = "1"
serde_derive = "1"
yew = { path = "../../.." }
yew = { path = "../../../.." }

View File

@ -0,0 +1,176 @@
extern crate failure;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate yew;
use failure::Error;
use yew::prelude::*;
use yew::format::{Nothing, Json};
use yew::services::Task;
use yew::services::fetch::{FetchService, FetchTask, Request, Response};
use yew::services::websocket::{WebSocketService, WebSocketTask, WebSocketStatus};
pub struct Model {
fetching: bool,
data: Option<u32>,
ft: Option<FetchTask>,
ws: Option<WebSocketTask>,
}
pub enum WsAction {
Connect,
SendData,
Disconnect,
Lost,
}
pub enum Msg {
FetchData,
WsAction(WsAction),
FetchReady(Result<DataFromFile, Error>),
WsReady(Result<WsResponse, Error>),
Ignore,
}
impl From<WsAction> for Msg {
fn from(action: WsAction) -> Self {
Msg::WsAction(action)
}
}
/// This type is used to parse data from `./static/data.json` file and
/// have to correspond the data layout from that file.
#[derive(Deserialize, Debug)]
pub struct DataFromFile {
value: u32,
}
/// This type is used as a request which sent to websocket connection.
#[derive(Serialize, Debug)]
struct WsRequest {
value: u32,
}
/// This type is an expected response from a websocket connection.
#[derive(Deserialize, Debug)]
pub struct WsResponse {
value: u32,
}
impl<CTX> Component<CTX> for Model
where
CTX: AsMut<FetchService> + AsMut<WebSocketService> + 'static,
{
type Msg = Msg;
type Properties = ();
fn create(_: Self::Properties, _: &mut Env<CTX, Self>) -> Self {
Model {
fetching: false,
data: None,
ft: None,
ws: None,
}
}
fn update(&mut self, msg: Self::Msg, context: &mut Env<CTX, Self>) -> ShouldRender {
match msg {
Msg::FetchData => {
self.fetching = true;
let callback = context.send_back(|response: Response<Json<Result<DataFromFile, Error>>>| {
let (meta, Json(data)) = response.into_parts();
println!("META: {:?}, {:?}", meta, data);
if meta.status.is_success() {
Msg::FetchReady(data)
} else {
Msg::Ignore // FIXME: Handle this error accordingly.
}
});
let request = Request::get("/data.json").body(Nothing).unwrap();
let fetch_service: &mut FetchService = context.as_mut();
let task = fetch_service.fetch(request, callback);
self.ft = Some(task);
}
Msg::WsAction(action) => {
match action {
WsAction::Connect => {
let callback = context.send_back(|Json(data)| Msg::WsReady(data));
let notification = context.send_back(|status| {
match status {
WebSocketStatus::Opened => Msg::Ignore,
WebSocketStatus::Closed => WsAction::Lost.into(),
}
});
let ws_service: &mut WebSocketService = context.as_mut();
let task = ws_service.connect("ws://localhost:9001/", callback, notification);
self.ws = Some(task);
}
WsAction::SendData => {
let request = WsRequest {
value: 321,
};
self.ws.as_mut().unwrap().send(Json(&request));
}
WsAction::Disconnect => {
self.ws.take().unwrap().cancel();
}
WsAction::Lost => {
self.ws = None;
}
}
}
Msg::FetchReady(response) => {
self.fetching = false;
self.data = response.map(|data| data.value).ok();
}
Msg::WsReady(response) => {
self.data = response.map(|data| data.value).ok();
}
Msg::Ignore => {
return false;
}
}
true
}
}
impl<CTX> Renderable<CTX, Model> for Model
where
CTX: AsMut<FetchService> + AsMut<WebSocketService> + 'static,
{
fn view(&self) -> Html<CTX, Self> {
html! {
<div>
<nav class="menu",>
<button onclick=|_| Msg::FetchData,>{ "Fetch Data" }</button>
{ self.view_data() }
<button disabled=self.ws.is_some(),
onclick=|_| WsAction::Connect.into(),>{ "Connect To WebSocket" }</button>
<button disabled=self.ws.is_none(),
onclick=|_| WsAction::SendData.into(),>{ "Send To WebSocket" }</button>
<button disabled=self.ws.is_none(),
onclick=|_| WsAction::Disconnect.into(),>{ "Close WebSocket connection" }</button>
</nav>
</div>
}
}
}
impl Model {
fn view_data<CTX>(&self) -> Html<CTX, Model>
where
CTX: AsMut<FetchService> + AsMut<WebSocketService> + 'static,
{
if let Some(value) = self.data {
html! {
<p>{ value }</p>
}
} else {
html! {
<p>{ "Data hasn't fetched yet." }</p>
}
}
}
}

View File

@ -1,171 +1,25 @@
extern crate failure;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate yew;
extern crate dashboard;
use failure::Error;
use yew::prelude::*;
use yew::format::{Nothing, Json};
use yew::services::Task;
use yew::services::fetch::{FetchService, FetchTask, Request, Response};
use yew::services::websocket::{WebSocketService, WebSocketTask, WebSocketStatus};
use yew::services::fetch::FetchService;
use yew::services::websocket::WebSocketService;
use dashboard::Model;
struct Context {
web: FetchService,
ws: WebSocketService,
}
struct Model {
fetching: bool,
data: Option<u32>,
ft: Option<FetchTask>,
ws: Option<WebSocketTask>,
}
enum WsAction {
Connect,
SendData,
Disconnect,
Lost,
}
enum Msg {
FetchData,
WsAction(WsAction),
FetchReady(Result<DataFromFile, Error>),
WsReady(Result<WsResponse, Error>),
Ignore,
}
impl From<WsAction> for Msg {
fn from(action: WsAction) -> Self {
Msg::WsAction(action)
impl AsMut<FetchService> for Context {
fn as_mut(&mut self) -> &mut FetchService {
&mut self.web
}
}
/// This type is used to parse data from `./static/data.json` file and
/// have to correspond the data layout from that file.
#[derive(Deserialize, Debug)]
struct DataFromFile {
value: u32,
}
/// This type is used as a request which sent to websocket connection.
#[derive(Serialize, Debug)]
struct WsRequest {
value: u32,
}
/// This type is an expected response from a websocket connection.
#[derive(Deserialize, Debug)]
struct WsResponse {
value: u32,
}
impl Component<Context> for Model {
type Msg = Msg;
type Properties = ();
fn create(_: Self::Properties, _: &mut Env<Context, Self>) -> Self {
Model {
fetching: false,
data: None,
ft: None,
ws: None,
}
}
fn update(&mut self, msg: Self::Msg, context: &mut Env<Context, Self>) -> ShouldRender {
match msg {
Msg::FetchData => {
self.fetching = true;
let callback = context.send_back(|response: Response<Json<Result<DataFromFile, Error>>>| {
let (meta, Json(data)) = response.into_parts();
println!("META: {:?}, {:?}", meta, data);
if meta.status.is_success() {
Msg::FetchReady(data)
} else {
Msg::Ignore // FIXME: Handle this error accordingly.
}
});
let request = Request::get("/data.json").body(Nothing).unwrap();
let task = context.web.fetch(request, callback);
self.ft = Some(task);
}
Msg::WsAction(action) => {
match action {
WsAction::Connect => {
let callback = context.send_back(|Json(data)| Msg::WsReady(data));
let notification = context.send_back(|status| {
match status {
WebSocketStatus::Opened => Msg::Ignore,
WebSocketStatus::Closed => WsAction::Lost.into(),
}
});
let task = context.ws.connect("ws://localhost:9001/", callback, notification);
self.ws = Some(task);
}
WsAction::SendData => {
let request = WsRequest {
value: 321,
};
self.ws.as_mut().unwrap().send(Json(&request));
}
WsAction::Disconnect => {
self.ws.take().unwrap().cancel();
}
WsAction::Lost => {
self.ws = None;
}
}
}
Msg::FetchReady(response) => {
self.fetching = false;
self.data = response.map(|data| data.value).ok();
}
Msg::WsReady(response) => {
self.data = response.map(|data| data.value).ok();
}
Msg::Ignore => {
return false;
}
}
true
}
}
impl Renderable<Context, Model> for Model {
fn view(&self) -> Html<Context, Self> {
html! {
<div>
<nav class="menu",>
<button onclick=|_| Msg::FetchData,>{ "Fetch Data" }</button>
{ self.view_data() }
<button disabled=self.ws.is_some(),
onclick=|_| WsAction::Connect.into(),>{ "Connect To WebSocket" }</button>
<button disabled=self.ws.is_none(),
onclick=|_| WsAction::SendData.into(),>{ "Send To WebSocket" }</button>
<button disabled=self.ws.is_none(),
onclick=|_| WsAction::Disconnect.into(),>{ "Close WebSocket connection" }</button>
</nav>
</div>
}
}
}
impl Model {
fn view_data(&self) -> Html<Context, Model> {
if let Some(value) = self.data {
html! {
<p>{ value }</p>
}
} else {
html! {
<p>{ "Data hasn't fetched yet." }</p>
}
}
impl AsMut<WebSocketService> for Context {
fn as_mut(&mut self) -> &mut WebSocketService {
&mut self.ws
}
}
@ -176,6 +30,6 @@ fn main() {
ws: WebSocketService::new(),
};
let app: App<Context, Model> = App::new(context);
app.mount_to_body();;
app.mount_to_body();
yew::run_loop();
}