hyaenidae/src/nav.rs
2021-04-02 12:07:19 -05:00

255 lines
7.7 KiB
Rust

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<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let profile = Option::<UserProfile>::extract(req);
let logout = Option::<LogoutState>::extract(req);
let query = Option::<Query<Vec<(String, String)>>>::extract(req);
let admin = Option::<Admin>::extract(req);
let path = req.uri().path().to_owned();
let state = Data::<State>::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::<Vec<String>>()
.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<OwnedProfileView>,
notification_count: Option<u64>,
logout_state: Option<LogoutState>,
admin: Option<Admin>,
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<ProfileView<'a>> {
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<Button> {
let mut nav = vec![];
if let Some(logout_state) = &self.logout_state {
if self.profile.is_some() {
nav.push(self.submission_button(loader));
nav.push(self.browse_button(loader));
}
if self.admin.is_some() {
nav.push(self.admin_button(loader));
}
nav.push(self.logout_button(logout_state, loader));
} else {
nav.push(self.login_button(loader));
nav.push(self.register_button(loader));
}
nav
}
pub(crate) fn mobile_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
let mut nav = vec![];
if let Some(logout_state) = &self.logout_state {
if self.profile.is_some() {
nav.push(self.mobile_submission_button(loader));
nav.push(self.mobile_browse_button(loader));
}
if self.admin.is_some() {
nav.push(self.mobile_admin_button(loader));
}
nav.push(self.mobile_logout_button(logout_state, loader));
} else {
nav.push(self.mobile_login_button(loader));
nav.push(self.mobile_register_button(loader));
}
nav
}
pub(crate) fn class_string(&self) -> &str {
if self.is_open {
"nav-open"
} else {
"nav-closed"
}
}
pub(crate) fn dark(&self) -> bool {
self.dark
}
}