use std::{any::TypeId, sync::Arc};
use topcoat::{
context::Cx,
router::{
Body, RouteFuture,
error::{forbidden, not_found},
path_param_segment,
},
view::{BoxView, ViewExt},
};
use super::actions::{input_options, run_header};
use crate::{Page, page::MountedPage, topcoat_compat::async_page};
pub(crate) fn mounted_page<P: Page>(cx: &Cx) -> Option<&Arc<MountedPage>> {
super::state::current(cx)?
.mounted_pages
.get(&TypeId::of::<P>())
}
pub fn header_action_buttons<P: Page>(cx: &Cx) -> BoxView<'_> {
let Some(page) = mounted_page::<P>(cx).filter(|page| page.admits(cx)) else {
return ().boxed();
};
let Some(url) = super::url::page::<P>(cx) else {
return ().boxed();
};
super::header_bar(cx, &url, &page.header_actions, |_| true).render(cx)
}
fn admitted<P: Page>(cx: &Cx) -> topcoat::Result<&Arc<MountedPage>> {
crate::auth::guard(cx)?;
let page = mounted_page::<P>(cx).ok_or_else(not_found)?;
if !page.admits(cx) {
return Err(forbidden().into());
}
Ok(page)
}
pub(super) fn page_handler<P: Page>(cx: &Cx, _body: Body) -> BoxView<'_> {
async_page(async move {
admitted::<P>(cx)?;
P::render(cx).await
})
}
pub(super) fn page_action<P: Page>(cx: &Cx, body: Body) -> BoxView<'_> {
async_page(async move {
let page = admitted::<P>(cx)?;
let Some(action) = page
.header_actions
.find(path_param_segment(cx, "action"))
.copied()
else {
return Err(not_found().into());
};
if !(action.can_run)(cx) {
return Err(forbidden().into());
}
let Some(home) = crate::panel::url::page::<P>(cx) else {
return Err(not_found().into());
};
run_header(cx, body, action, &home, None).await
})
}
pub(super) fn page_action_options<P: Page>(cx: &Cx, _body: Body) -> RouteFuture<'_> {
Box::pin(async move {
let page = admitted::<P>(cx)?;
let Some(action) = page.header_actions.find(path_param_segment(cx, "action")) else {
return Err(not_found().into());
};
if !(action.can_run)(cx) {
return Err(forbidden().into());
}
input_options(cx, action.input).await
})
}
#[cfg(test)]
mod tests;