use crate::{ admin::Admin, error::Error, middleware::UserProfile, notifications::total_for_profile, views::{OwnedProfileView, ProfileView}, ActixLoader, State, }; use actix_web::{ dev::Payload, web::{self, Data, Query}, FromRequest, HttpRequest, }; use futures::future::LocalBoxFuture; use hyaenidae_accounts::LogoutState; 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 dark = if let Some(profile) = &profile { state.settings.for_profile(profile.id()).await?.dark } else { true }; let notification_count = if let Some(profile) = &profile { total_for_profile(profile.id(), &state).await.ok() } else { None }; let store = state.profiles.clone(); let profile = if let Some(profile) = profile { let profile = web::block(move || { let icon = if let Some(file_id) = profile.icon() { store.store.files.by_id(file_id)? } else { None }; Ok(OwnedProfileView { profile, icon, banner: None, }) as Result<_, Error> }) .await??; Some(profile) } else { None }; Ok(NavState { profile, notification_count, logout_state, admin, href, is_open, dark, }) }) } } 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 } pub(crate) fn profile<'a>(&'a self) -> Option> { self.profile.as_ref().map(|p| ProfileView { profile: &p.profile, icon: p.icon.as_ref(), banner: p.banner.as_ref(), }) } pub(crate) fn submission_button(&self, loader: &ActixLoader) -> Button { Button::primary_link(&fl!(loader, "nav-submission-button")).href("/submissions/create") } fn mobile_submission_button(&self, loader: &ActixLoader) -> Button { Button::primary(&fl!(loader, "nav-submission-button")).href("/submissions/create") } pub(crate) fn browse_button(&self, loader: &ActixLoader) -> Button { Button::link(&fl!(loader, "nav-browse-button")).href("/browse") } fn mobile_browse_button(&self, loader: &ActixLoader) -> Button { Button::secondary(&fl!(loader, "nav-browse-button")).href("/browse") } pub(crate) fn notifications_path(&self) -> &'static str { "/notifications" } pub(crate) fn is_admin(&self) -> bool { self.admin.is_some() } pub(crate) fn admin_button(&self, loader: &ActixLoader) -> Button { Button::link(&fl!(loader, "nav-admin-button")).href("/admin") } fn mobile_admin_button(&self, loader: &ActixLoader) -> Button { Button::secondary(&fl!(loader, "nav-admin-button")).href("/admin") } fn login_button(&self, loader: &ActixLoader) -> Button { Button::primary_link(&fl!(loader, "nav-login-button")).href("/session/auth/login") } fn mobile_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_link(&fl!(loader, "nav-register-button")).href("/session/auth/register") } fn mobile_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_link(&fl!(loader, "nav-logout-button")).form(&logout_state.logout_path()) } fn mobile_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 has_notifications(&self) -> bool { if let Some(count) = self.notification_count { count > 0 } else { false } } pub(crate) fn buttons(&self, loader: &ActixLoader) -> Vec