2021-01-22 05:47:47 +00:00
|
|
|
use crate::{
|
2021-01-31 23:22:15 +00:00
|
|
|
admin::Admin,
|
|
|
|
error::Error,
|
|
|
|
extensions::ProfileExt,
|
|
|
|
middleware::UserProfile,
|
|
|
|
notifications::total_for_profile,
|
|
|
|
views::{OwnedProfileView, ProfileView},
|
|
|
|
ActixLoader, State,
|
2021-01-22 05:47:47 +00:00
|
|
|
};
|
2021-01-15 05:51:17 +00:00
|
|
|
use actix_web::{
|
|
|
|
dev::Payload,
|
2021-01-31 23:22:15 +00:00
|
|
|
web::{self, Data, Query},
|
2021-01-15 05:51:17 +00:00
|
|
|
FromRequest, HttpRequest,
|
|
|
|
};
|
|
|
|
use futures::future::LocalBoxFuture;
|
2021-01-10 01:49:33 +00:00
|
|
|
use hyaenidae_accounts::LogoutState;
|
|
|
|
use hyaenidae_toolkit::Button;
|
2021-01-30 18:48:37 +00:00
|
|
|
use i18n_embed_fl::fl;
|
2021-01-10 01:49:33 +00:00
|
|
|
|
|
|
|
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 {
|
2021-01-22 05:47:47 +00:00
|
|
|
let profile = Option::<UserProfile>::extract(req);
|
2021-01-10 01:49:33 +00:00
|
|
|
let logout = Option::<LogoutState>::extract(req);
|
2021-01-11 04:09:47 +00:00
|
|
|
let query = Option::<Query<Vec<(String, String)>>>::extract(req);
|
2021-01-14 04:46:34 +00:00
|
|
|
let admin = Option::<Admin>::extract(req);
|
2021-01-11 04:09:47 +00:00
|
|
|
let path = req.uri().path().to_owned();
|
2021-01-15 05:51:17 +00:00
|
|
|
let state = Data::<State>::extract(req);
|
2021-01-10 01:49:33 +00:00
|
|
|
|
|
|
|
Box::pin(async move {
|
2021-01-22 05:47:47 +00:00
|
|
|
let profile = profile.await?.map(|p| p.0);
|
2021-01-30 18:48:37 +00:00
|
|
|
let logout_state = logout.await?;
|
2021-01-11 04:09:47 +00:00
|
|
|
let query = query.await?;
|
2021-01-14 04:46:34 +00:00
|
|
|
let admin = admin.await?;
|
2021-01-15 05:51:17 +00:00
|
|
|
let state = state.await?;
|
2021-01-10 01:49:33 +00:00
|
|
|
|
2021-01-25 02:05:45 +00:00
|
|
|
let mut is_open = false;
|
|
|
|
let href = if let Some(query) = query {
|
2021-01-11 04:09:47 +00:00
|
|
|
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)
|
|
|
|
};
|
|
|
|
|
2021-01-25 02:05:45 +00:00
|
|
|
format!("{}?{}", path, query)
|
|
|
|
} else {
|
|
|
|
format!("{}?show_nav=true", path)
|
|
|
|
};
|
|
|
|
|
2021-01-30 18:48:37 +00:00
|
|
|
let notification_count = if let Some(profile) = &profile {
|
|
|
|
total_for_profile(profile.id(), &state).await.ok()
|
2021-01-11 04:09:47 +00:00
|
|
|
} else {
|
2021-01-30 18:48:37 +00:00
|
|
|
None
|
|
|
|
};
|
2021-01-25 02:05:45 +00:00
|
|
|
|
2021-01-31 23:22:15 +00:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2021-01-25 02:05:45 +00:00
|
|
|
Ok(NavState {
|
2021-01-30 18:48:37 +00:00
|
|
|
profile,
|
|
|
|
notification_count,
|
|
|
|
logout_state,
|
|
|
|
admin,
|
2021-01-25 02:05:45 +00:00
|
|
|
href,
|
|
|
|
is_open,
|
2021-01-31 23:22:15 +00:00
|
|
|
dark: true,
|
2021-01-25 02:05:45 +00:00
|
|
|
})
|
2021-01-10 01:49:33 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct NavState {
|
2021-01-31 23:22:15 +00:00
|
|
|
profile: Option<OwnedProfileView>,
|
2021-01-30 18:48:37 +00:00
|
|
|
notification_count: Option<u64>,
|
|
|
|
logout_state: Option<LogoutState>,
|
|
|
|
admin: Option<Admin>,
|
2021-01-11 04:09:47 +00:00
|
|
|
href: String,
|
|
|
|
is_open: bool,
|
|
|
|
dark: bool,
|
2021-01-10 01:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl NavState {
|
2021-01-11 04:09:47 +00:00
|
|
|
pub(crate) fn href(&self) -> &str {
|
|
|
|
&self.href
|
|
|
|
}
|
|
|
|
|
2021-01-31 23:22:15 +00:00
|
|
|
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")
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn nav_button(&self, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
Button::link(&fl!(loader, "nav-text"))
|
2021-01-30 18:48:37 +00:00
|
|
|
.href(&self.href)
|
|
|
|
.class("nav-link")
|
|
|
|
}
|
|
|
|
|
2021-01-31 23:22:15 +00:00
|
|
|
pub(crate) fn browse_button(&self, loader: &ActixLoader) -> Button {
|
|
|
|
Button::link(&fl!(loader, "nav-browse-button")).href("/browse")
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn profile_button(&self, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
if let Some(view) = self.profile.as_ref() {
|
|
|
|
Button::link(&fl!(loader, "nav-profile-button")).href(&view.profile.view_path())
|
2021-01-30 18:48:37 +00:00
|
|
|
} else {
|
2021-01-31 23:22:15 +00:00
|
|
|
Button::link(&fl!(loader, "nav-switch-profile-button")).href("/profiles/change")
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-31 23:22:15 +00:00
|
|
|
pub(crate) fn notifications_path(&self) -> &'static str {
|
|
|
|
"/notifications"
|
|
|
|
}
|
|
|
|
|
2021-01-30 18:48:37 +00:00
|
|
|
fn notifications_button(&self, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
Button::link(&fl!(loader, "nav-notifications-button")).href(self.notifications_path())
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn admin_button(&self, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
Button::link(&fl!(loader, "nav-admin-button")).href("/admin")
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn account_button(&self, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
Button::link(&fl!(loader, "nav-account-button")).href("/session/account")
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn login_button(&self, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
Button::primary_link(&fl!(loader, "nav-login-button")).href("/session/auth/login")
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn register_button(&self, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
Button::primary_link(&fl!(loader, "nav-register-button")).href("/session/auth/register")
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn logout_button(&self, logout_state: &LogoutState, loader: &ActixLoader) -> Button {
|
2021-01-31 23:22:15 +00:00
|
|
|
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
|
|
|
|
}
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
|
|
|
let mut nav = vec![];
|
|
|
|
|
|
|
|
if let Some(logout_state) = &self.logout_state {
|
|
|
|
nav.push(self.submission_button(loader));
|
|
|
|
nav.push(self.browse_button(loader));
|
|
|
|
nav.push(self.profile_button(loader));
|
|
|
|
|
2021-01-31 23:22:15 +00:00
|
|
|
if self.has_notifications() {
|
|
|
|
nav.push(self.notifications_button(loader));
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nav.push(self.account_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 narrow_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
|
|
|
let mut narrow_nav = vec![];
|
|
|
|
|
|
|
|
if self.logout_state.is_some() {
|
|
|
|
narrow_nav.push(self.submission_button(loader));
|
|
|
|
narrow_nav.push(self.nav_button(loader));
|
|
|
|
} else {
|
|
|
|
narrow_nav.push(self.nav_button(loader));
|
|
|
|
}
|
|
|
|
|
|
|
|
narrow_nav
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn site_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
|
|
|
let mut site_nav = vec![];
|
|
|
|
|
|
|
|
if self.logout_state.is_some() {
|
|
|
|
site_nav.push(self.submission_button(loader));
|
|
|
|
site_nav.push(self.browse_button(loader));
|
|
|
|
site_nav.push(self.profile_button(loader));
|
|
|
|
|
2021-01-31 23:22:15 +00:00
|
|
|
if self.has_notifications() {
|
|
|
|
site_nav.push(self.notifications_button(loader));
|
2021-01-30 18:48:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if self.admin.is_some() {
|
|
|
|
site_nav.push(self.admin_button(loader));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
site_nav
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn account_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
|
|
|
let mut account_nav = vec![];
|
|
|
|
|
|
|
|
if let Some(logout_state) = &self.logout_state {
|
|
|
|
account_nav.push(self.logout_button(logout_state, loader));
|
|
|
|
} else {
|
|
|
|
account_nav.push(self.login_button(loader));
|
|
|
|
account_nav.push(self.register_button(loader));
|
|
|
|
}
|
|
|
|
|
|
|
|
account_nav
|
|
|
|
}
|
|
|
|
|
2021-01-11 04:09:47 +00:00
|
|
|
pub(crate) fn class_string(&self) -> &str {
|
|
|
|
if self.is_open {
|
|
|
|
"nav-open"
|
|
|
|
} else {
|
|
|
|
"nav-closed"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn dark(&self) -> bool {
|
|
|
|
self.dark
|
|
|
|
}
|
2021-01-10 01:49:33 +00:00
|
|
|
}
|