pib-viewer 0.8.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::{
    hooks::{use_resource, use_signal},
    prelude::{Props, component, dioxus_elements, rsx},
    signals::ReadableExt as _,
};
use dioxus_core::Element;
use dioxus_i18n::t;
use dioxus_router::Link;
use oparl_types::MeetingUrl;

use crate::{
    client::{get_body, get_meeting, get_organization},
    components::{AgendaItemList, OrganizationList},
    route::{Route, UrlEncoded},
};

#[derive(Debug, Clone, PartialEq, Props)]
pub struct MeetingProps {
    meeting_url: UrlEncoded<MeetingUrl>,
}

struct MeetingData {
    meeting: oparl_types::Meeting,
    body: oparl_types::Body,
    organizations: Vec<oparl_types::Organization>,
}

impl MeetingData {
    async fn get(meeting_url: &MeetingUrl) -> Result<Self, String> {
        let meeting = get_meeting(meeting_url).await?;

        let mut organizations = Vec::new();

        for organization in &meeting.organization {
            let organization = get_organization(organization).await?;
            organizations.push(organization);
        }

        let Some(organization) = organizations.first() else {
            return Err("Couldn't load body from list of organizations".to_string());
        };
        let body = get_body(organization.body.as_ref().unwrap()).await?;

        Ok(Self {
            meeting,
            organizations,
            body,
        })
    }
}

#[component]
pub fn Meeting(props: MeetingProps) -> Element {
    let meeting_url = use_signal(|| props.meeting_url.get_inner().unwrap().clone());
    let data = { use_resource(move || async move { MeetingData::get(&meeting_url()).await }) };
    rsx! {
        match &*data.read_unchecked() {
            Some(Ok(data)) => {
                let meeting_label =
                    if let Some(name) = &data.meeting.name {
                        t!("meeting-label", name: name.as_str())
                    } else {
                        t!("meeting-unknown-label")
                    };
                rsx! {
                    dioxus::document::Title { "{meeting_label.as_str()}" }
                    h1 { "{meeting_label.as_str()}" }
                    Link {
                        to: Route::Body{body_url: UrlEncoded::from(data.body.id.clone())},
                        { t!("body-label", name: data.body.name.as_str()) }
                    }
                    AgendaItemList {
                        agenda_items: data.meeting.agenda_item.clone()
                    }
                    OrganizationList {
                        organizations: data.organizations.clone()
                    }
                }
            },
            Some(Err(e)) => rsx! {
                dioxus::document::Title { { t!("loading-failed") } },
                p { { t!("loading-meeting-failed", error: e.to_string()) } }
            },
            None =>  rsx! {
                dioxus::document::Title { { t!("loading") } },
                p { { t!("loading") } }
            }
        }
    }
}