dialtone_yew 0.1.0

Dialtone Yew Webapp
use std::rc::Rc;

use crate::components::common::bordered_panel::*;
use crate::components::common::horizontal_header_table::*;
use crate::components::common::loading::*;
use crate::components::common::titled_panel::*;
use crate::components::common::unadorned_panel::*;
use crate::components::common::vertical_header_table::*;
use crate::components::common::Datum;
use crate::utils::DATETIME_FORMAT;
use crate::MainRoute;
use crate::{
    app_types::AppSiteConnection,
    components::{common::main_layout::*, pages::error::handle_error},
};
use bounce::{use_atom, UseAtomHandle};
use dialtone_common::rest::users::web_user::LastLoginData;
use dialtone_common::rest::users::web_user::LastSeenData;
use dialtone_common::rest::users::web_user::WebUser;
use dialtone_reqwest::api_v1::users::get_user::get_user;
use yew::prelude::*;
use yew_router::prelude::*;

#[derive(Properties, PartialEq)]
pub struct AccountProps {
    pub acct: String,
}

struct AccountPageState {
    pub sc_atom: UseAtomHandle<AppSiteConnection>,
    pub webuser: UseStateHandle<Option<WebUser>>,
    pub acct: String,
    pub history: AnyHistory,
}

#[function_component(Account)]
pub fn account(AccountProps { acct }: &AccountProps) -> Html {
    let page_state = Rc::new(AccountPageState {
        sc_atom: use_atom::<AppSiteConnection>(),
        webuser: use_state(|| None),
        acct: acct.clone(),
        history: use_history().unwrap(),
    });

    use_effect_with_deps(
        {
            let page_state = Rc::clone(&page_state);
            move |_| {
                let page_state = Rc::clone(&page_state);
                wasm_bindgen_futures::spawn_local(async move {
                    let page_state = Rc::clone(&page_state);
                    let webuser = get_user(&page_state.sc_atom.0, Some(&page_state.acct)).await;
                    match webuser {
                        Ok(webuser) => page_state.webuser.set(Some(webuser)),
                        Err(err) => handle_error(&page_state.history, err),
                    }
                });
                || ()
            }
        },
        (),
    );

    html! {
        <MainLayout>
            if let Some(webuser) = (*page_state.webuser).clone() {
                <div class="asymmetrical_split_page">
                    <div class="asymmetrical_col_left">
                        <TitledPanel title="Actors">
                            <div class="link_nav">
                                <div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Persons"}</Link<MainRoute>></div>
                                <div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Groups"}</Link<MainRoute>></div>
                                <div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Services"}</Link<MainRoute>></div>
                            </div>
                        </TitledPanel>
                        <TitledPanel title="Account">
                            <div class="link_nav">
                                <div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Preferences"}</Link<MainRoute>></div>
                                <div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Password"}</Link<MainRoute>></div>
                            </div>
                        </TitledPanel>
                    </div>
                    <div class="asymmetrical_col_right">
                        <BorderedPanel>
                            <div style="text-align:center">
                                <span class="info_text">{page_state.acct.clone()}</span>
                            </div>
                        </BorderedPanel>
                        <AccountInfoPanel webuser={webuser.clone()}>
                        </AccountInfoPanel>
                        <LoginInfoPanel logins={webuser.last_login_data}>
                        </LoginInfoPanel>
                        <PingsInfoPanel pings={webuser.last_seen_data}>
                        </PingsInfoPanel>
                    </div>
                </div>
            } else {
                <Loading/>
            }
        </MainLayout>
    }
}

#[derive(Properties, Debug, PartialEq)]
struct AccountInfoPanelProps {
    pub webuser: WebUser,
}

#[function_component(AccountInfoPanel)]
fn account_info_panel(AccountInfoPanelProps { webuser }: &AccountInfoPanelProps) -> Html {
    let rows = vec![
        VerticalHeaderTableRow {
            header: "Status".to_string(),
            data: vec![Datum::new(webuser.status.to_string())],
        },
        VerticalHeaderTableRow {
            header: "Created".to_string(),
            data: vec![Datum::new_mono(
                webuser.created_at.format(DATETIME_FORMAT).to_string(),
            )],
        },
        VerticalHeaderTableRow {
            header: "Last Modified".to_string(),
            data: vec![Datum::new_mono(
                webuser.modified_at.format(DATETIME_FORMAT).to_string(),
            )],
        },
    ];
    html! {
        <UnadornedPanel>
            <div class="centered_col_container">
                <VerticalHeaderTable rows={rows}>
                </VerticalHeaderTable>
            </div>
        </UnadornedPanel>
    }
}

#[derive(Properties, PartialEq, Debug)]
struct LoginInfoPanelProps {
    pub logins: Vec<LastLoginData>,
}

#[function_component(LoginInfoPanel)]
fn login_info_panel(LoginInfoPanelProps { logins }: &LoginInfoPanelProps) -> Html {
    let headers = vec!["From".to_string(), "When".to_string(), "Login".to_string()];
    let rows = logins
        .iter()
        .map(|login| {
            vec![
                Datum::new(login.from.to_string()),
                Datum::new_mono(login.at.format(DATETIME_FORMAT).to_string()),
                Datum::new(
                    if login.success {
                        "Succeeded".to_string()
                    } else {
                        "Failed".to_string()
                    }
                ),
            ]
        })
        .collect::<Vec<Vec<Datum>>>();
    html! {
        <TitledPanel title="Logins">
            <div class="centered_col_container">
                <HorizontalHeaderTable headers={headers} rows={rows}>
                </HorizontalHeaderTable>
            </div>
        </TitledPanel>
    }
}

#[derive(Properties, PartialEq, Clone)]
struct PingsInfoPanelProps {
    pub pings: Vec<LastSeenData>,
}

#[function_component(PingsInfoPanel)]
fn pings_info_panel(PingsInfoPanelProps { pings }: &PingsInfoPanelProps) -> Html {
    let headers = vec!["From".to_string(), "When".to_string()];
    let rows = pings
        .iter()
        .map(|ping| {
            vec![
                Datum::new(ping.from.to_string()),
                Datum::new_mono(ping.at.format(DATETIME_FORMAT).to_string()),
            ]
        })
        .collect::<Vec<Vec<Datum>>>();
    html! {
        <TitledPanel title="Pings">
            <div class="centered_col_container">
                <HorizontalHeaderTable headers={headers} rows={rows}>
                </HorizontalHeaderTable>
            </div>
        </TitledPanel>
    }
}