use crate::{ admin::Admin, error::Error, extensions::ProfileExt, 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 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: true, }) }) } } 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 nav_button(&self, loader: &ActixLoader) -> Button { Button::link(&fl!(loader, "nav-text")) .href(&self.href) .class("nav-link") } pub(crate) fn browse_button(&self, loader: &ActixLoader) -> Button { Button::link(&fl!(loader, "nav-browse-button")).href("/browse") } fn profile_button(&self, loader: &ActixLoader) -> Button { if let Some(view) = self.profile.as_ref() { Button::link(&fl!(loader, "nav-profile-button")).href(&view.profile.view_path()) } else { Button::link(&fl!(loader, "nav-switch-profile-button")).href("/profiles/change") } } pub(crate) fn notifications_path(&self) -> &'static str { "/notifications" } fn notifications_button(&self, loader: &ActixLoader) -> Button { Button::link(&fl!(loader, "nav-notifications-button")).href(self.notifications_path()) } fn admin_button(&self, loader: &ActixLoader) -> Button { Button::link(&fl!(loader, "nav-admin-button")).href("/admin") } fn account_button(&self, loader: &ActixLoader) -> Button { Button::link(&fl!(loader, "nav-account-button")).href("/session/account") } fn login_button(&self, loader: &ActixLoader) -> Button { Button::primary_link(&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 logout_button(&self, logout_state: &LogoutState, loader: &ActixLoader) -> Button { Button::primary_link(&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