pib-viewer 0.5.0

A viewer for public governmental data served over OParl
// SPDX-FileCopyrightText: Politik im Blick developers
// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
//
// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2

use dioxus::{
    document,
    hooks::use_signal,
    prelude::{component, dioxus_elements, rsx},
};
use dioxus_core::Element;
use dioxus_signals::{ReadableExt as _, WritableExt as _};
use manganis::{Asset, asset};
use tracing::{info, warn};
use url::Url;

use crate::{
    client::{Config, Contents},
    components::ConnectDialog,
    route::{Route, UrlEncoded},
};

const WELCOME_PAGE_CSS: Asset = asset!("/assets/styling/components/welcome_page.css");

#[component]
pub fn WelcomePage() -> Element {
    let mut config_state = use_signal(|| ConfigState::Initial);

    #[derive(Debug, Clone, PartialEq, Eq)]
    enum ConfigState {
        Initial,
        Loading,
        Failed { message: String },
        NoConfigPresent,
        ConfigLoaded { config: Config },
    }

    let initialize_config = move |_| async move {
        if !matches!(&*config_state.read(), ConfigState::Initial) {
            return;
        }
        *config_state.write() = ConfigState::Loading;

        let Ok(base_url) = document::eval("return document.URL;").await else {
            warn!("couldn't load base url.");
            *config_state.write() = ConfigState::Failed {
                message: "couldn't load base url.".to_string(),
            };
            return;
        };
        let serde_json::Value::String(base_url) = base_url else {
            warn!("not a string value: {base_url}");
            *config_state.write() = ConfigState::Failed {
                message: format!("not a string value: {base_url}"),
            };
            return;
        };
        info!("base url: {base_url:?}");
        let base_url = match base_url.parse::<Url>() {
            Ok(url) => url,
            Err(e) => {
                warn!("Couldn't parse base url {base_url}, reason: {e}");
                *config_state.write() = ConfigState::Failed {
                    message: format!("Couldn't parse base url {base_url}, reason: {e}"),
                };
                return;
            }
        };
        match crate::client::get_page_config(base_url).await {
            Ok(Some(config)) => {
                *config_state.write() = ConfigState::ConfigLoaded { config };
            }
            Ok(None) => {
                *config_state.write() = ConfigState::NoConfigPresent;
            }
            Err(e) => {
                *config_state.write() = ConfigState::Failed {
                    message: format!("Failed to load config: {e}"),
                };
            }
        }
    };

    rsx! {
        document::Link { rel: "stylesheet", href: WELCOME_PAGE_CSS }

        div {
            onmounted: initialize_config,
            id: "welcome_page",

            h1 { "🗒️ PiB Viewer" }

            p {
                "👋 Hi! "
                b { "PiB Viewer" }
                " allows you to view public administration data from an "
                a {
                    href: "https://oparl.org/",
                    "OParl v1.1 service"
                }
                "."
            }

            match &*config_state.read_unchecked() {
                ConfigState::ConfigLoaded { config } => rsx! {
                    match &config.contents {
                        Contents::SingleSystem { system } => rsx! {
                            div {
                                dioxus_router::Link {
                                    to: Route::OParlSystem {
                                        system_url: UrlEncoded::from(system.url.clone()),
                                    },
                                    "Go to {system.name}…"
                                }
                            }
                        },
                        Contents::SingleBody { body } => rsx! {
                            div {
                                dioxus_router::Link {
                                    to: Route::Body {
                                        body_url: UrlEncoded::from(body.url.clone()),
                                    },
                                    "Go to 🏛️ {body.name}…"
                                }
                            }
                        },
                        Contents::ConnectDialog => rsx! {
                            ConnectDialog {},
                        }
                    }
                },
                ConfigState::NoConfigPresent => rsx! {
                    ConnectDialog {},
                },
                ConfigState::Failed{message} => rsx! {
                    div {
                        p { "Loading config failed, {message}" }
                        button {
                            class: "button",
                            "data-style": "primary",
                            onclick: move |_| async move {
                                *config_state.write() = ConfigState::NoConfigPresent
                            },
                            "Show connect dialog"
                        }
                    }
                },
                ConfigState::Loading | ConfigState::Initial=>  rsx! { p { "Loading config..." } }
            }
        }
    }
}