use crate::{ admin::Admin, extensions::ProfileExt, middleware::UserProfile, notifications::total_for_profile, ActixLoader, State, }; use actix_web::{ dev::Payload, web::{Data, Query}, FromRequest, HttpRequest, }; use futures::future::LocalBoxFuture; use hyaenidae_accounts::LogoutState; use hyaenidae_profiles::store::Profile; use hyaenidae_toolkit::Button; use i18n_embed_fl::fl; impl FromRequest for NavState { type Config = (); type Error = actix_web::Error; type Future = LocalBoxFuture<'static, Result>; fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { let profile = Option::::extract(req); let logout = Option::::extract(req); let query = Option::>>::extract(req); let admin = Option::::extract(req); let path = req.uri().path().to_owned(); let state = Data::::extract(req); Box::pin(async move { let profile = profile.await?.map(|p| p.0); let logout_state = logout.await?; let query = query.await?; let admin = admin.await?; let state = state.await?; let mut is_open = false; let href = if let Some(query) = query { let query = query .into_inner() .into_iter() .filter_map(|(key, value)| { if key == "show_nav" { is_open = true; None } else { Some(format!("{}={}", key, value)) } }) .collect::>() .join("&"); let query = if is_open { query } else { format!("{}&show_nav=true", query) }; format!("{}?{}", path, query) } else { format!("{}?show_nav=true", path) }; let notification_count = if let Some(profile) = &profile { total_for_profile(profile.id(), &state).await.ok() } else { None }; Ok(NavState { profile, notification_count, logout_state, admin, href, is_open, dark: false, }) }) } } pub struct NavState { profile: Option, notification_count: Option, logout_state: Option, admin: Option, href: String, is_open: bool, dark: bool, } impl NavState { pub(crate) fn href(&self) -> &str { &self.href } fn submission_button(&self, loader: &ActixLoader) -> Button { Button::primary(&fl!(loader, "nav-submission-button")).href("/submissions/create") } fn nav_button(&self, loader: &ActixLoader) -> Button { Button::secondary(&fl!(loader, "nav-text")) .href(&self.href) .class("nav-link") } fn browse_button(&self, loader: &ActixLoader) -> Button { Button::secondary(&fl!(loader, "nav-browse-button")).href("/browse") } fn profile_button(&self, loader: &ActixLoader) -> Button { if let Some(profile) = self.profile.as_ref() { Button::secondary(&fl!(loader, "nav-profile-button")).href(&profile.view_path()) } else { Button::secondary(&fl!(loader, "nav-switch-profile-button")).href("/profiles/change") } } fn notifications_button(&self, loader: &ActixLoader) -> Button { Button::secondary(&fl!(loader, "nav-notifications-button")).href("/notifications") } fn admin_button(&self, loader: &ActixLoader) -> Button { Button::secondary(&fl!(loader, "nav-admin-button")).href("/admin") } fn account_button(&self, loader: &ActixLoader) -> Button { Button::secondary(&fl!(loader, "nav-account-button")).href("/session/account") } fn login_button(&self, loader: &ActixLoader) -> Button { Button::primary_outline(&fl!(loader, "nav-login-button")).href("/session/auth/login") } fn register_button(&self, loader: &ActixLoader) -> Button { Button::primary_outline(&fl!(loader, "nav-register-button")).href("/session/auth/register") } fn logout_button(&self, logout_state: &LogoutState, loader: &ActixLoader) -> Button { Button::primary_outline(&fl!(loader, "nav-logout-button")).form(&logout_state.logout_path()) } pub(crate) fn buttons(&self, loader: &ActixLoader) -> Vec