Server: Start work on better nav bar

This commit is contained in:
asonix 2021-01-31 17:22:15 -06:00
parent 0387e09959
commit 1e62b11a01
9 changed files with 159 additions and 65 deletions

View file

@ -25,6 +25,26 @@ picture {
display: none; display: none;
} }
.profile-nav {
display: flex;
align-items: center;
.bar-icon {
margin-right: 0;
margin-left: 16px;
}
.toolkit-icon--link .toolkit-icon {
border-width: 1px;
}
.notification-bell {
.toolkit-link,
i {
display: block;
}
}
}
.narrow-nav { .narrow-nav {
display: none; display: none;
} }

View file

@ -1,15 +1,19 @@
use crate::{ use crate::{
admin::Admin, extensions::ProfileExt, middleware::UserProfile, admin::Admin,
notifications::total_for_profile, ActixLoader, State, error::Error,
extensions::ProfileExt,
middleware::UserProfile,
notifications::total_for_profile,
views::{OwnedProfileView, ProfileView},
ActixLoader, State,
}; };
use actix_web::{ use actix_web::{
dev::Payload, dev::Payload,
web::{Data, Query}, web::{self, Data, Query},
FromRequest, HttpRequest, FromRequest, HttpRequest,
}; };
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use hyaenidae_accounts::LogoutState; use hyaenidae_accounts::LogoutState;
use hyaenidae_profiles::store::Profile;
use hyaenidae_toolkit::Button; use hyaenidae_toolkit::Button;
use i18n_embed_fl::fl; use i18n_embed_fl::fl;
@ -66,6 +70,29 @@ impl FromRequest for NavState {
None 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 { Ok(NavState {
profile, profile,
notification_count, notification_count,
@ -73,14 +100,14 @@ impl FromRequest for NavState {
admin, admin,
href, href,
is_open, is_open,
dark: false, dark: true,
}) })
}) })
} }
} }
pub struct NavState { pub struct NavState {
profile: Option<Profile>, profile: Option<OwnedProfileView>,
notification_count: Option<u64>, notification_count: Option<u64>,
logout_state: Option<LogoutState>, logout_state: Option<LogoutState>,
admin: Option<Admin>, admin: Option<Admin>,
@ -94,50 +121,70 @@ impl NavState {
&self.href &self.href
} }
fn submission_button(&self, loader: &ActixLoader) -> Button { pub(crate) fn profile<'a>(&'a self) -> Option<ProfileView<'a>> {
Button::primary(&fl!(loader, "nav-submission-button")).href("/submissions/create") 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 { fn nav_button(&self, loader: &ActixLoader) -> Button {
Button::secondary(&fl!(loader, "nav-text")) Button::link(&fl!(loader, "nav-text"))
.href(&self.href) .href(&self.href)
.class("nav-link") .class("nav-link")
} }
fn browse_button(&self, loader: &ActixLoader) -> Button { pub(crate) fn browse_button(&self, loader: &ActixLoader) -> Button {
Button::secondary(&fl!(loader, "nav-browse-button")).href("/browse") Button::link(&fl!(loader, "nav-browse-button")).href("/browse")
} }
fn profile_button(&self, loader: &ActixLoader) -> Button { fn profile_button(&self, loader: &ActixLoader) -> Button {
if let Some(profile) = self.profile.as_ref() { if let Some(view) = self.profile.as_ref() {
Button::secondary(&fl!(loader, "nav-profile-button")).href(&profile.view_path()) Button::link(&fl!(loader, "nav-profile-button")).href(&view.profile.view_path())
} else { } else {
Button::secondary(&fl!(loader, "nav-switch-profile-button")).href("/profiles/change") 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 { fn notifications_button(&self, loader: &ActixLoader) -> Button {
Button::secondary(&fl!(loader, "nav-notifications-button")).href("/notifications") Button::link(&fl!(loader, "nav-notifications-button")).href(self.notifications_path())
} }
fn admin_button(&self, loader: &ActixLoader) -> Button { fn admin_button(&self, loader: &ActixLoader) -> Button {
Button::secondary(&fl!(loader, "nav-admin-button")).href("/admin") Button::link(&fl!(loader, "nav-admin-button")).href("/admin")
} }
fn account_button(&self, loader: &ActixLoader) -> Button { fn account_button(&self, loader: &ActixLoader) -> Button {
Button::secondary(&fl!(loader, "nav-account-button")).href("/session/account") Button::link(&fl!(loader, "nav-account-button")).href("/session/account")
} }
fn login_button(&self, loader: &ActixLoader) -> Button { fn login_button(&self, loader: &ActixLoader) -> Button {
Button::primary_outline(&fl!(loader, "nav-login-button")).href("/session/auth/login") Button::primary_link(&fl!(loader, "nav-login-button")).href("/session/auth/login")
} }
fn register_button(&self, loader: &ActixLoader) -> Button { fn register_button(&self, loader: &ActixLoader) -> Button {
Button::primary_outline(&fl!(loader, "nav-register-button")).href("/session/auth/register") Button::primary_link(&fl!(loader, "nav-register-button")).href("/session/auth/register")
} }
fn logout_button(&self, logout_state: &LogoutState, loader: &ActixLoader) -> Button { fn logout_button(&self, logout_state: &LogoutState, loader: &ActixLoader) -> Button {
Button::primary_outline(&fl!(loader, "nav-logout-button")).form(&logout_state.logout_path()) 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<Button> { pub(crate) fn buttons(&self, loader: &ActixLoader) -> Vec<Button> {
@ -148,10 +195,8 @@ impl NavState {
nav.push(self.browse_button(loader)); nav.push(self.browse_button(loader));
nav.push(self.profile_button(loader)); nav.push(self.profile_button(loader));
if let Some(count) = self.notification_count { if self.has_notifications() {
if count > 0 { nav.push(self.notifications_button(loader));
nav.push(self.notifications_button(loader));
}
} }
nav.push(self.account_button(loader)); nav.push(self.account_button(loader));
@ -189,10 +234,8 @@ impl NavState {
site_nav.push(self.browse_button(loader)); site_nav.push(self.browse_button(loader));
site_nav.push(self.profile_button(loader)); site_nav.push(self.profile_button(loader));
if let Some(count) = self.notification_count { if self.has_notifications() {
if count > 0 { site_nav.push(self.notifications_button(loader));
site_nav.push(self.notifications_button(loader));
}
} }
if self.admin.is_some() { if self.admin.is_some() {

View file

@ -5,14 +5,14 @@
@use hyaenidae_toolkit::templates::bbcode; @use hyaenidae_toolkit::templates::bbcode;
@use hyaenidae_toolkit::templates::{card_body, card_section}; @use hyaenidae_toolkit::templates::{card_body, card_section};
@use hyaenidae_toolkit::{templates::link, Link}; @use hyaenidae_toolkit::{templates::link, Link};
@use hyaenidae_toolkit::templates::icon; @use hyaenidae_toolkit::{templates::icon, Size};
@use hyaenidae_toolkit::{templates::{tile, tiles}, Tiles}; @use hyaenidae_toolkit::{templates::{tile, tiles}, Tiles};
@(loader: &ActixLoader, sub_view: &OwnedSubmissionView, pro_view: &OwnedProfileView, dark: bool) @(loader: &ActixLoader, sub_view: &OwnedSubmissionView, pro_view: &OwnedProfileView, dark: bool)
@:card_body({ @:card_body({
<div class="profile-box"> <div class="profile-box">
@:icon(&pro_view.icon(loader).small(true).dark(dark)) @:icon(&pro_view.icon(loader).size(Size::Small).dark(dark))
<div class="profile-box--content"> <div class="profile-box--content">
<div class="profile-box--all-meta"> <div class="profile-box--all-meta">
<div> <div>

53
templates/bar.rs.html Normal file
View file

@ -0,0 +1,53 @@
@use crate::ActixLoader;
@use crate::nav::NavState;
@use hyaenidae_toolkit::templates::bar;
@use hyaenidae_toolkit::templates::{button, button_group};
@use hyaenidae_toolkit::{templates::icon, Size};
@use hyaenidae_toolkit::{templates::link, Link};
@use i18n_embed_fl::fl;
@(loader: &ActixLoader, nav_state: &NavState)
@:bar(nav_state.dark(), "desktop-bar", {
<div>
@:link(&Link::current_tab("/").plain(true), {
<h2>@fl!(loader, "site-name")</h2>
})
</div>
<nav>
@if let Some(profile) = nav_state.profile() {
<div class="profile-nav">
<div class="toolkit-button-group">
@:button(&nav_state.submission_button(loader))
@:button(&nav_state.browse_button(loader))
@if nav_state.has_notifications() {
<div class="toolkit-button toolkit-button__link">
<span><i class="fa fa-bell"></i></span>
@:link(&Link::current_tab(nav_state.notifications_path()).plain(true).class("toolkit-button--action"), {
<i class="fa fa-bell"></i>
})
</div>
}
</div>
@:icon(&profile.icon(loader).size(Size::Tiny).class("bar-icon").dark(true))
</div>
} else {
<div class="narrow-nav">
@:button_group(&nav_state.narrow_buttons(loader))
</div>
<div class="wide-nav">
@:button_group(&nav_state.buttons(loader))
</div>
}
</nav>
})
@:bar(nav_state.dark(), "mobile-bar", {
@:link(&Link::current_tab("/").plain(true), {
<h2>@fl!(loader, "site-name")</h2>
})
<h3 class="nav-link">
@:link(&Link::current_tab(nav_state.href()).plain(true), {
@fl!(loader, "nav-text")
})
</h3>
})

View file

@ -5,13 +5,13 @@
@use crate::views::OwnedProfileView; @use crate::views::OwnedProfileView;
@use chrono::{DateTime, Utc}; @use chrono::{DateTime, Utc};
@use hyaenidae_toolkit::{templates::link, Link}; @use hyaenidae_toolkit::{templates::link, Link};
@use hyaenidae_toolkit::templates::icon; @use hyaenidae_toolkit::{templates::icon, Size};
@use i18n_embed_fl::fl; @use i18n_embed_fl::fl;
@(loader: &ActixLoader, view: &OwnedProfileView, published: DateTime<Utc>, parent: &CommentNode, cache: &Cache, dark: bool, meta: Content, body: Content) @(loader: &ActixLoader, view: &OwnedProfileView, published: DateTime<Utc>, parent: &CommentNode, cache: &Cache, dark: bool, meta: Content, body: Content)
<div class="profile-box"> <div class="profile-box">
@:icon(&view.icon(loader).small(true).dark(dark)) @:icon(&view.icon(loader).size(Size::Small).dark(dark))
<div class="profile-box--content"> <div class="profile-box--content">
<div class="profile-box--all-meta"> <div class="profile-box--all-meta">
<div> <div>

View file

@ -1,7 +1,9 @@
@use crate::ActixLoader; @use crate::ActixLoader;
@use crate::{templates::layouts::root, nav::NavState}; @use crate::nav::NavState;
@use crate::templates::bar;
@use crate::templates::layouts::root;
@use crate::templates::statics::nav_js; @use crate::templates::statics::nav_js;
@use hyaenidae_toolkit::templates::{bar, centered}; @use hyaenidae_toolkit::templates::centered;
@use hyaenidae_toolkit::{templates::{card, card_body}, Card}; @use hyaenidae_toolkit::{templates::{card, card_body}, Card};
@use hyaenidae_toolkit::{templates::link, Link}; @use hyaenidae_toolkit::{templates::link, Link};
@use hyaenidae_toolkit::{templates::button_group, Button}; @use hyaenidae_toolkit::{templates::button_group, Button};
@ -13,31 +15,7 @@
<script src="@crate::statics_path(nav_js.name)"></script> <script src="@crate::statics_path(nav_js.name)"></script>
@:head() @:head()
}, { }, {
@:bar(nav_state.dark(), "desktop-bar", { @:bar(loader, nav_state)
<div>
@:link(&Link::current_tab("/").plain(true), {
<h2>@fl!(loader, "site-name")</h2>
})
</div>
<nav>
<div class="narrow-nav">
@:button_group(&nav_state.narrow_buttons(loader))
</div>
<div class="wide-nav">
@:button_group(&nav_state.buttons(loader))
</div>
</nav>
})
@:bar(nav_state.dark(), "mobile-bar", {
@:link(&Link::current_tab("/").plain(true), {
<h2>@fl!(loader, "site-name")</h2>
})
<h3 class="nav-link">
@:link(&Link::current_tab(nav_state.href()).plain(true), {
@fl!(loader, "nav-text")
})
</h3>
})
<div class="home-content"> <div class="home-content">
@:body() @:body()
</div> </div>

View file

@ -6,7 +6,7 @@
@use hyaenidae_toolkit::{templates::button_group, Button}; @use hyaenidae_toolkit::{templates::button_group, Button};
@use hyaenidae_toolkit::{templates::{card, card_body, card_section}, Card}; @use hyaenidae_toolkit::{templates::{card, card_body, card_section}, Card};
@use hyaenidae_toolkit::templates::centered; @use hyaenidae_toolkit::templates::centered;
@use hyaenidae_toolkit::templates::{icon, image}; @use hyaenidae_toolkit::{templates::{icon, image}, Size};
@use hyaenidae_toolkit::{templates::link, Link}; @use hyaenidae_toolkit::{templates::link, Link};
@use hyaenidae_toolkit::templates::search; @use hyaenidae_toolkit::templates::search;
@use hyaenidae_toolkit::{templates::{tab, tab_group}, Tab}; @use hyaenidae_toolkit::{templates::{tab, tab_group}, Tab};
@ -42,7 +42,7 @@
} }
<div class="profile-result--left"> <div class="profile-result--left">
<div class="profile-result--icon"> <div class="profile-result--icon">
@:icon(&pview.icon(loader).small(true).dark(nav_state.dark())) @:icon(&pview.icon(loader).size(Size::Small).dark(nav_state.dark()))
</div> </div>
<div class="profile-result--meta"> <div class="profile-result--meta">
<div class="profile-result--display-name"> <div class="profile-result--display-name">

View file

@ -5,7 +5,7 @@
@use crate::views::OwnedProfileView; @use crate::views::OwnedProfileView;
@use hyaenidae_toolkit::{templates::button_group, Button}; @use hyaenidae_toolkit::{templates::button_group, Button};
@use hyaenidae_toolkit::{templates::{card, card_body, card_section, card_title}, Card}; @use hyaenidae_toolkit::{templates::{card, card_body, card_section, card_title}, Card};
@use hyaenidae_toolkit::templates::{icon, image}; @use hyaenidae_toolkit::{templates::{icon, image}, Size};
@use hyaenidae_toolkit::{templates::link, Link}; @use hyaenidae_toolkit::{templates::link, Link};
@use i18n_embed_fl::fl; @use i18n_embed_fl::fl;
@ -24,7 +24,7 @@
} }
<div class="profile-result--left"> <div class="profile-result--left">
<div class="profile-result--icon"> <div class="profile-result--icon">
@:icon(&pview.icon(loader).small(true).dark(nav_state.dark())) @:icon(&pview.icon(loader).size(Size::Small).dark(nav_state.dark()))
</div> </div>
<div class="profile-result--meta"> <div class="profile-result--meta">
<div class="profile-result--display-name"> <div class="profile-result--display-name">

View file

@ -4,12 +4,12 @@
@use crate::views::OwnedProfileView; @use crate::views::OwnedProfileView;
@use chrono::{DateTime, Utc}; @use chrono::{DateTime, Utc};
@use hyaenidae_toolkit::{templates::link, Link}; @use hyaenidae_toolkit::{templates::link, Link};
@use hyaenidae_toolkit::templates::icon; @use hyaenidae_toolkit::{templates::icon, Size};
@(loader: &ActixLoader, view: &OwnedProfileView, published: Option<DateTime<Utc>>, dark: bool, body: Content) @(loader: &ActixLoader, view: &OwnedProfileView, published: Option<DateTime<Utc>>, dark: bool, body: Content)
<div class="profile-box"> <div class="profile-box">
@:icon(&view.icon(loader).small(true).dark(dark)) @:icon(&view.icon(loader).size(Size::Small).dark(dark))
<div class="profile-box--content"> <div class="profile-box--content">
<div class="profile-box--all-meta"> <div class="profile-box--all-meta">
<div class="profile-box--meta"> <div class="profile-box--meta">