Make mobile nav not require page refresh when JS enabled

Make top bar stick to top of screen
Make wide view show rows of 4
Improve notification page styles, text
Add button js to more pages
This commit is contained in:
asonix 2021-01-15 22:50:15 -06:00
parent 95b3b17c61
commit 5f0682ee22
29 changed files with 296 additions and 79 deletions

View file

@ -3,16 +3,31 @@
font-style: italic;
}
picture {
width: 100%;
}
.toolkit-card.nav {
margin-bottom: 0;
}
.desktop-bar,
.mobile-bar {
position: fixed;
z-index: 1;
top: 0;
left: 0;
right: 0;
overflow-x: auto;
}
.mobile-bar {
display: none;
}
.nav-body {
position: fixed;
z-index: 2;
top: 0;
bottom: 0;
left: 0;
@ -22,6 +37,13 @@
&.nav-open {
display: block;
animation-duration: 0.3s;
animation-name: slideup;
}
&.nav-closing {
display: block;
animation-duration: 0.5s;
animation-name: slidedown;
}
&.nav-closed {
display: none;
@ -44,7 +66,7 @@
}
.home-content {
padding: 32px 0;
padding: 96px 0 32px;
}
.profile-box {
@ -119,7 +141,7 @@
.submission-tiles {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
.submission-icon {
position: relative;
@ -128,7 +150,6 @@
picture {
display: block;
width: 100%;
height: 100%;
}
@ -185,10 +206,38 @@
}
.submission-box {
max-height: 70vh;
max-height: 90vh;
display: flex;
justify-content: center;
background-color: #000;
img {
object-fit: contain;
}
}
@keyframes slideup {
from {
background-color: rgba(0, 0, 0, 0);
bottom: -100vh;
}
to {
background-color: rgba(0, 0, 0, 0.4);
bottom: 0vh;
}
}
@keyframes slidedown {
from {
background-color: rgba(0, 0, 0, 0.4);
bottom: 0vh;
}
to {
background-color: rgba(0, 0, 0, 0);
bottom: -100vh;
}
}
@media (max-width: 700px) {

View file

@ -1,7 +1,7 @@
use crate::{error::Error, State};
use activitystreams::base::AnyBase;
use actix_web::{web, HttpResponse, Scope};
use hyaenidae_profiles::{apub::ApubIds, Spawner};
use hyaenidae_profiles::apub::ApubIds;
use sled::Tree;
use url::Url;
use uuid::Uuid;
@ -54,9 +54,11 @@ async fn shared_inbox(
do_inbox(body.into_inner(), &state).await
}
// TODO: signature validation, Actor check
// TODO: signature validation
async fn do_inbox(any_base: AnyBase, state: &State) -> Result<HttpResponse, Error> {
state.spawn.process(any_base, vec![]);
state
.spawn
.ingest(any_base, "http://temporary.url".parse().unwrap());
Ok(HttpResponse::Created().finish())
}

View file

@ -3,6 +3,7 @@ use ructe::Ructe;
fn main() -> ructe::Result<()> {
let mut ructe = Ructe::from_env()?;
let mut statics = ructe.statics()?;
statics.add_files("static")?;
statics.add_sass_file("scss/layout.scss")?;
ructe.compile_templates("templates")?;

View file

@ -43,6 +43,18 @@ pub(super) fn build(
#[derive(Clone)]
pub(super) struct Spawn(QueueHandle);
impl Spawn {
pub(crate) fn ingest(&self, any_base: AnyBase, key_owner: Url) {
if let Err(e) = self.0.queue(Ingest {
any_base,
key_owner: Some(key_owner),
stack: vec![],
}) {
log::error!("Failed to queue ingest: {}", e);
}
}
}
impl Spawner for Spawn {
fn download_apub(&self, url: Url, stack: Vec<AnyBase>) {
if let Err(e) = self.0.queue(DownloadApub { url, stack }) {
@ -63,7 +75,11 @@ impl Spawner for Spawn {
}
fn process(&self, any_base: AnyBase, stack: Vec<AnyBase>) {
if let Err(e) = self.0.queue(Ingest { any_base, stack }) {
if let Err(e) = self.0.queue(Ingest {
any_base,
key_owner: None,
stack,
}) {
log::error!("Failed to queue process job: {}", e);
}
}
@ -109,6 +125,7 @@ struct DownloadApub {
#[derive(Clone, serde::Deserialize, serde::Serialize)]
struct Ingest {
any_base: AnyBase,
key_owner: Option<Url>,
stack: Vec<AnyBase>,
}
@ -267,7 +284,10 @@ impl ActixJob for Ingest {
if self.stack.len() > MAX_INGEST_DEPTH {
return Err(anyhow::anyhow!("Max recursion depth exceded"));
}
state.profiles.ingest(self.any_base, self.stack).await?;
state
.profiles
.ingest(self.any_base, self.key_owner, self.stack)
.await?;
Ok(())
})
}

View file

@ -58,16 +58,18 @@ async fn main() -> anyhow::Result<()> {
secret_key
};
let domain = config
.base_url
.domain()
.expect("Invalid domain for base url")
.to_owned();
let accounts_config = hyaenidae_accounts::Config {
toolkit_path: format!(
"/toolkit/{}",
hyaenidae_toolkit::templates::statics::toolkit_css.name
),
domain: config
.base_url
.domain()
.expect("Invalid domain for base url")
.to_owned(),
domain: domain.clone(),
key: secret_key,
https: false,
pages: std::sync::Arc::new(accounts::Pages),
@ -81,14 +83,17 @@ async fn main() -> anyhow::Result<()> {
)?;
let accounts_state = hyaenidae_accounts::state(&accounts_config, db.clone())?;
let state = State::new(
spawner.clone(),
config.base_url.clone(),
config.pictrs_upstream.clone(),
&db.clone(),
)?;
state.profiles.create_server_actor(domain).await?;
if let Some(user) = config.make_admin {
let user = accounts_state.by_username(user).await?.req()?;
let state = State::new(
spawner.clone(),
config.base_url,
config.pictrs_upstream,
&db.clone(),
)?;
state.admin.make_admin(user.id())?;
return Ok(());

View file

@ -1,4 +1,7 @@
use crate::{comments::Comment, error::Error, nav::NavState, profiles::Profile, State};
use crate::{
comments::Comment, error::Error, nav::NavState, profiles::Profile, submissions::Submission,
State,
};
use actix_web::{web, HttpResponse, Scope};
use futures::stream::{FuturesUnordered, StreamExt};
use hyaenidae_toolkit::{Button, Link};
@ -206,20 +209,37 @@ async fn remove_comment_notification(
}
pub(crate) struct CommentView<'a> {
submission: &'a Submission,
parent: Option<&'a Comment>,
comment: &'a Comment,
author: &'a Profile,
id: Uuid,
self_id: Uuid,
}
impl<'a> CommentView<'a> {
fn comment_reply(&self) -> Option<Uuid> {
self.parent.and_then(|c| {
if c.profile_id() == self.self_id {
Some(c.id())
} else {
None
}
})
}
fn submission_path(&self) -> Option<String> {
if self.comment.comment_id().is_none() {
if self.comment_reply().is_none() {
Some(format!("/submissions/{}", self.comment.submission_id()))
} else {
None
}
}
pub(crate) fn submission_title(&self) -> String {
self.submission.title()
}
pub(crate) fn submission_link(&self, dark: bool) -> Option<Link> {
self.submission_path().map(|path| {
let mut link = Link::new_tab(&path);
@ -229,7 +249,7 @@ impl<'a> CommentView<'a> {
}
fn reply_to_path(&self) -> Option<String> {
if let Some(reply_to_id) = self.comment.comment_id() {
if let Some(reply_to_id) = self.comment_reply() {
Some(format!("/comments/{}", reply_to_id))
} else {
None
@ -249,7 +269,7 @@ impl<'a> CommentView<'a> {
}
pub(crate) fn view_button(&self, dark: bool) -> Button {
let btn = Button::primary("View");
let btn = Button::secondary("View");
btn.href(&self.comment_path()).new_tab().dark(dark);
btn
}
@ -259,7 +279,7 @@ impl<'a> CommentView<'a> {
}
pub(crate) fn remove_button(&self, dark: bool) -> Button {
let btn = Button::primary_outline("Remove");
let btn = Button::secondary("Remove");
btn.form(&self.remove_path()).dark(dark);
btn
}
@ -282,7 +302,7 @@ pub(crate) struct FollowRequestView<'a> {
impl<'a> FollowRequestView<'a> {
pub(crate) fn accept_button(&self, dark: bool) -> Button {
let btn = Button::primary("Accept");
let btn = Button::secondary("Accept");
btn.form(&format!(
"/notifications/follow-requests/{}/accept",
self.id
@ -292,7 +312,7 @@ impl<'a> FollowRequestView<'a> {
}
pub(crate) fn reject_button(&self, dark: bool) -> Button {
let btn = Button::primary_outline("Reject");
let btn = Button::secondary("Reject");
btn.form(&format!(
"/notifications/follow-requests/{}/reject",
self.id
@ -306,10 +326,12 @@ impl<'a> FollowRequestView<'a> {
pub struct NotificationsView {
comment_hm: HashMap<Uuid, Comment>,
profile_hm: HashMap<Uuid, Profile>,
submission_hm: HashMap<Uuid, Submission>,
fr_profile_hm: HashMap<Uuid, Uuid>,
comments: Vec<Uuid>,
follow_requests: Vec<Uuid>,
count: u64,
self_id: Uuid,
}
impl NotificationsView {
@ -317,11 +339,16 @@ impl NotificationsView {
self.comments.iter().filter_map(move |comment_id| {
let comment = self.comment_hm.get(comment_id)?;
let author = self.profile_hm.get(&comment.profile_id())?;
let submission = self.submission_hm.get(&comment.submission_id())?;
let parent = comment.comment_id().and_then(|id| self.comment_hm.get(&id));
Some(CommentView {
comment,
parent,
author,
submission,
id: *comment_id,
self_id: self.self_id,
})
})
}
@ -397,6 +424,7 @@ impl NotificationsView {
let comment_store = state.profiles.store.comments.clone();
let profile_store = state.profiles.store.profiles.clone();
let submission_store = state.profiles.store.submissions.clone();
let file_store = state.profiles.store.files.clone();
let follow_request_store = state.profiles.store.view.follow_requests.clone();
let comment_notifs = state.profiles.store.view.comments.clone();
@ -406,10 +434,12 @@ impl NotificationsView {
let mut view = NotificationsView {
comment_hm: HashMap::new(),
profile_hm: HashMap::new(),
submission_hm: HashMap::new(),
fr_profile_hm: HashMap::new(),
comments: vec![],
follow_requests: vec![],
count,
self_id: profile_id,
};
for comment_id in comment_notifs.for_profile(profile_id) {
@ -422,6 +452,21 @@ impl NotificationsView {
)?;
view.profile_hm.insert(profile.id(), profile);
}
if !view.submission_hm.contains_key(&comment.submission_id()) {
let submission = Submission::from_stores(
comment.submission_id(),
&submission_store,
&file_store,
)?;
view.submission_hm.insert(submission.id(), submission);
}
if let Some(id) = comment.comment_id() {
if !view.profile_hm.contains_key(&id) {
if let Some(comment) = comment_store.by_id(id)? {
view.comment_hm.insert(comment.id(), comment);
}
}
}
view.comments.push(comment.id());
view.comment_hm.insert(comment.id(), comment);

47
server/static/nav.js Normal file
View file

@ -0,0 +1,47 @@
(function(fn) {
if (document.readyState === "complete" || document.readyState === "interactive") {
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
})(function() {
function onClick(nav) {
return function _listener(event) {
event.preventDefault();
var containsOpen = false;
for (var i = 0; i < nav.classList.length; i++) {
if (nav.classList[i] == "nav-open") {
containsOpen = true;
}
}
if (containsOpen) {
nav.setAttribute("class", "nav-body nav-closing");
setTimeout(function() {
nav.setAttribute("class", "nav-body nav-closed");
}, 500)
} else {
nav.setAttribute("class", "nav-body nav-open");
}
};
}
var navs = document.getElementsByClassName("nav-body");
var nav = navs[0];
if (!nav) {
return;
}
var links = document.getElementsByClassName("nav-link");
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (!link) {
continue;
}
link.addEventListener("click", onClick(nav));
}
})

View file

@ -0,0 +1,4 @@
@use hyaenidae_toolkit::templates::statics::button_js;
@()
<script src="@crate::toolkit_path(button_js.name)"></script>

View file

@ -1,3 +1,4 @@
@use crate::templates::button_js;
@use crate::templates::layouts::home;
@use crate::templates::comments::{nodes, profile_box};
@use crate::comments::CommentView;
@ -11,7 +12,9 @@
@(view: &CommentView, nav_state: &NavState)
@if let Some((comment, author)) = view.comments.item.comment() {
@:home(&author.name(), comment.body(), nav_state, {}, {
@:home(&author.name(), comment.body(), nav_state, {
@:button_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
@:card_title({ Comment })
@:card_body({

View file

@ -1,5 +1,6 @@
@use crate::comments::ReportView;
@use crate::nav::NavState;
@use crate::templates::button_js;
@use crate::templates::layouts::home;
@use crate::templates::comments::profile_box;
@use hyaenidae_toolkit::{templates::button_group, Button};
@ -8,7 +9,9 @@
@(view: &ReportView, nav_state: &NavState)
@:home("Report Comment", &format!("Report comment by {}", view.author.name()), nav_state, {}, {
@:home("Report Comment", &format!("Report comment by {}", view.author.name()), nav_state, {
@:button_js()
}, {
@:card(Card::full_width().dark(nav_state.dark()), {
@:card_title({ Report Comment })
@:card_body({

View file

@ -0,0 +1,5 @@
@use hyaenidae_toolkit::templates::statics::file_input_js;
@()
<script src="@crate::toolkit_path(file_input_js.name)"></script>

View file

@ -1,4 +1,5 @@
@use crate::{templates::{layouts::root, nav}, nav::NavState};
@use crate::templates::statics::nav_js;
@use hyaenidae_toolkit::templates::{bar, centered};
@use hyaenidae_toolkit::{templates::{card, card_body}, Card};
@use hyaenidae_toolkit::{templates::link, Link};
@ -6,7 +7,10 @@
@(title: &str, description: &str, nav_state: &NavState, head: Content, body: Content)
@:root(title, description, nav_state.dark(), { @:head() }, {
@:root(title, description, nav_state.dark(), {
<script src="@crate::statics_path(nav_js.name)"></script>
@:head()
}, {
@:bar(nav_state.dark(), "desktop-bar", {
<div>
@:link(&Link::current_tab("/").plain(true).dark(nav_state.dark()), {
@ -21,7 +25,7 @@
@:link(&Link::current_tab("/").plain(true).dark(nav_state.dark()), {
<h2>Hyaenidae</h2>
})
<h3>@:link(&Link::current_tab(nav_state.href()).plain(true).dark(nav_state.dark()), { Nav })</h3>
<h3 class="nav-link">@:link(&Link::current_tab(nav_state.href()).plain(true).dark(nav_state.dark()), { Nav })</h3>
})
<div class="home-content">
@:centered(false, {
@ -29,10 +33,12 @@
})
</div>
<div class="nav-body @nav_state.class_string()">
@:link(&Link::current_tab(nav_state.href()).plain(true).dark(nav_state.dark()), {
<div class="nav-background">
</div>
})
<div class="nav-link nav-background">
@:link(&Link::current_tab(nav_state.href()).plain(true).dark(nav_state.dark()), {
<div class="nav-background">
</div>
})
</div>
<nav class="nav-links">
@:centered(false, {
@:card(&Card::full_width().classes(&["nav"]).dark(nav_state.dark()), {
@ -40,9 +46,11 @@
@:nav(nav_state)
})
@:card_body({
@:button_group(&[
Button::primary_outline("Close").href(nav_state.href()).dark(nav_state.dark()),
])
<div class="nav-link">
@:button_group(&[
Button::primary_outline("Close").href(nav_state.href()).dark(nav_state.dark()),
])
</div>
})
})
})

View file

@ -1,5 +1,6 @@
@use crate::nav::NavState;
@use crate::notifications::NotificationsView;
@use crate::templates::button_js;
@use crate::templates::layouts::home;
@use crate::templates::submissions::profile_box;
@use hyaenidae_toolkit::templates::button_group;
@ -8,7 +9,9 @@
@(view: &NotificationsView, nav_state: &NavState)
@:home(&format!("Notifications: {}", view.count()), "Notifications on Hyaenidae", nav_state, {}, {
@:home(&format!("Notifications: {}", view.count()), "Notifications on Hyaenidae", nav_state, {
@:button_js()
}, {
@:card(Card::full_width().dark(nav_state.dark()), {
@:card_title({ Clear All })
@:card_body({
@ -25,12 +28,13 @@
@:card_title({ Follow Requests })
@for fr in view.follow_requests() {
@:card_body({
@:profile_box(fr.profile, None, nav_state.dark(), {
@:profile_box(fr.profile, None, nav_state.dark(), {})
<div class="button-section">
@:button_group(&[
&fr.accept_button(nav_state.dark()),
&fr.reject_button(nav_state.dark()),
])
})
</div>
})
}
@:card_body({
@ -50,8 +54,8 @@
@c.author_name()
})
@if let Some(l) = c.submission_link(nav_state.dark()) {
commented on your
@:link(&l, { submission })
commented on your submission:
@:link(&l, { @c.submission_title() })
}
@if let Some(l) = c.reply_to_link(nav_state.dark()) {
replied to your

View file

@ -1,11 +1,12 @@
@use crate::{templates::{layouts::home, profiles::view}, nav::NavState, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input, statics::{button_js, file_input_js}}, Button, Card, FileInput};
@use crate::templates::{button_js, file_js};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input}, Button, Card, FileInput};
@(banner_input: &FileInput, error: Option<String>, profile: &Profile, nav_state: &NavState)
@:home("Create Profile", "Create a new profile on Hyaenidae", nav_state, {
<script src="/toolkit/@file_input_js.name"></script>
<script src="/toolkit/@button_js.name"></script>
@:button_js()
@:file_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
<form method="POST" action="/profiles/create/banner" enctype="multipart/form-data">

View file

@ -1,10 +1,11 @@
@use crate::{templates::{layouts::home, profiles::view}, nav::NavState, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, text_input, statics::button_js}, Button, Card, TextInput};
@use crate::templates::button_js;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, text_input}, Button, Card, TextInput};
@(display_name_input: &TextInput, description_input: &TextInput, profile: &Profile, nav_state: &NavState)
@:home("Create Profile", "Create a new profile on Hyaenidae", nav_state, {
<script src="/toolkit/@button_js.name"></script>
@:button_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
<form method="POST" action="/profiles/create/bio">

View file

@ -1,10 +1,11 @@
@use crate::{templates::layouts::home, nav::NavState};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, text_input, statics::button_js}, Button, Card, TextInput};
@use crate::templates::button_js;
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, text_input}, Button, Card, TextInput};
@(handle_input: &TextInput, nav_state: &NavState)
@:home("Create Profile", "Create a new profile on Hyaenidae", nav_state, {
<script src="/toolkit/@button_js.name"></script>
@:button_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
<form method="POST" action="/profiles/create/handle">

View file

@ -1,11 +1,12 @@
@use crate::templates::{button_js, file_js};
@use crate::{templates::{layouts::home, profiles::view}, nav::NavState, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input, statics::{button_js, file_input_js}}, Button, Card, FileInput};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input}, Button, Card, FileInput};
@(icon_input: &FileInput, error: Option<String>, profile: &Profile, nav_state: &NavState)
@:home("Create Profile", "Create a new profile on Hyaenidae", nav_state, {
<script src="/toolkit/@file_input_js.name"></script>
<script src="/toolkit/@button_js.name"></script>
@:button_js()
@:file_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
<form method="POST" action="/profiles/create/icon" enctype="multipart/form-data">

View file

@ -1,10 +1,11 @@
@use crate::templates::button_js;
@use crate::{templates::{layouts::home, profiles::view}, nav::NavState, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, statics::button_js}, Button, Card};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title}, Button, Card};
@(require_login: bool, error: Option<String>, profile: &Profile, nav_state: &NavState)
@:home("Create Profile", "Create a new profile on Hyaenidae", nav_state, {
<script src="/toolkit/@button_js.name"></script>
@:button_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
<form method="POST" action="/profiles/create/require-login">

View file

@ -1,11 +1,12 @@
@use crate::templates::{button_js, file_js};
@use crate::{templates::{layouts::home, profiles::{banner, icon, view}}, nav::NavState, profiles::ProfileState};
@use hyaenidae_toolkit::{templates::{button, button_group, card, card_body, card_title, file_input, text_input, statics::{button_js, file_input_js}}, Button, Card};
@use hyaenidae_toolkit::{templates::{button, button_group, card, card_body, card_title, file_input, text_input}, Button, Card};
@(state: &ProfileState, nav_state: &NavState)
@:home("Profile Settings", &format!("{}'s profile", state.profile.name()), nav_state, {
<script src="/toolkit/@file_input_js.name"></script>
<script src="/toolkit/@button_js.name"></script>
@:button_js()
@:file_js()
}, {
@:card(Card::full_width().dark(nav_state.dark()), { @:view(&state.profile, nav_state.dark()) })
@:card(Card::full_width().dark(nav_state.dark()), {

View file

@ -1,10 +1,11 @@
@use crate::templates::button_js;
@use crate::{templates::{layouts::home, profiles::view}, nav::NavState, profiles::Profile};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, statics::button_js}, Button, Card};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title}, Button, Card};
@(profile: &Profile, nav_state: &NavState)
@:home("Delete Profile", &format!("Delete {}", profile.name()), nav_state, {
<script src="/toolkit/@button_js.name"></script>
@:button_js()
}, {
@:card(Card::full_width().dark(nav_state.dark()), { @:view(profile, nav_state.dark()) })
@:card(Card::full_width().dark(nav_state.dark()), {

View file

@ -1,3 +1,4 @@
@use crate::templates::button_js;
@use crate::{templates::{layouts::home, profiles::view}, nav::NavState, profiles::ReportView};
@use hyaenidae_toolkit::{templates::button_group, Button};
@use hyaenidae_toolkit::{templates::{card, card_body, card_title}, Card};
@ -5,7 +6,9 @@
@(rview: &ReportView, nav_state: &NavState)
@:home("Report Profile", rview.profile.description().unwrap_or(&format!("Report {}'s profile on Hyaenidae", rview.profile.name())), nav_state, {}, {
@:home("Report Profile", rview.profile.description().unwrap_or(&format!("Report {}'s profile on Hyaenidae", rview.profile.name())), nav_state, {
@:button_js()
}, {
@:card(Card::full_width().dark(nav_state.dark()), { @:view(&rview.profile, nav_state.dark()) })
@:card(Card::full_width().dark(nav_state.dark()), {
<form method="POST" action="@rview.profile.report_path()">

View file

@ -1,11 +1,12 @@
@use crate::templates::button_js;
@use crate::{templates::layouts::home, nav::NavState};
@use hyaenidae_accounts::{templates::{update_password, update_username}, UpdatePasswordState, UpdateUsernameState, User};
@use hyaenidae_toolkit::{templates::{button, card, card_body, card_title, statics::button_js}, Button, Card};
@use hyaenidae_toolkit::{templates::{button, card, card_body, card_title}, Button, Card};
@(user: &User, uname_state: &UpdateUsernameState, pass_state: &UpdatePasswordState, nav_state: &NavState)
@:home("Account Settings", "Update account information", nav_state, {
<script src="/toolkit/@button_js.name"></script>
@:button_js()
}, {
@:card(Card::full_width().dark(nav_state.dark()), {
@:card_title({ Current Username: @user.username() })

View file

@ -1,9 +1,12 @@
@use crate::templates::button_js;
@use crate::{templates::layouts::home, nav::NavState};
@use hyaenidae_accounts::{templates::delete_user, DeleteUserState};
@use hyaenidae_toolkit::Card;
@(state: &DeleteUserState, nav_state: &NavState)
@:home("Delete Account", "Are you sure you want to delete your account?", nav_state, {}, {
@:home("Delete Account", "Are you sure you want to delete your account?", nav_state, {
@:button_js()
}, {
@:delete_user(&Card::full_width().dark(nav_state.dark()), state)
})

View file

@ -1,11 +1,12 @@
@use crate::templates::button_js;
@use crate::{templates::layouts::home, nav::NavState};
@use hyaenidae_accounts::{templates::login, LoginState};
@use hyaenidae_toolkit::{templates::statics::button_js, Card};
@use hyaenidae_toolkit::Card;
@(login_state: &LoginState, nav_state: &NavState)
@:home("Login", "Log into Hyaenidae", nav_state, {
<script src="/toolkit/@button_js.name"></script>
@:button_js()
}, {
@:login(&Card::full_width().dark(nav_state.dark()), login_state)
})

View file

@ -1,11 +1,10 @@
@use crate::templates::button_js;
@use crate::{templates::layouts::home, nav::NavState};
@use hyaenidae_accounts::{templates::register, RegisterState};
@use hyaenidae_toolkit::{templates::statics::button_js, Card};
@use hyaenidae_toolkit::Card;
@(register_state: &RegisterState, nav_state: &NavState)
@:home("Register", "Register for Hyaenidae", nav_state, {
<script src="/toolkit/@button_js.name"></script>
}, {
@:home("Register", "Register for Hyaenidae", nav_state, { @:button_js() }, {
@:register(&Card::full_width().dark(nav_state.dark()), register_state)
})

View file

@ -1,11 +1,12 @@
@use crate::templates::{button_js, file_js};
@use crate::{templates::layouts::home, nav::NavState};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input, statics::{button_js, file_input_js}}, Button, Card, FileInput};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input}, Button, Card, FileInput};
@(file: &FileInput, error: Option<String>, nav_state: &NavState)
@:home("Create Submission", "Upload a file", nav_state, {
<script src="/toolkit/@button_js.name"></script>
<script src="/toolkit/@file_input_js.name"></script>
@:button_js()
@:file_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
<form method="POST" action="/submissions/create" enctype="multipart/form-data">

View file

@ -1,3 +1,4 @@
@use crate::templates::button_js;
@use crate::templates::layouts::home;
@use crate::templates::comments::nodes;
@use crate::templates::submissions::{image, profile_box};
@ -9,7 +10,9 @@
@(view: &SubmissionView, nav_state: &NavState)
@:home(&view.submission.title(), view.submission.description().unwrap_or(&format!("{} hosted on Hyaenidae", view.submission.title())), nav_state, {}, {
@:home(&view.submission.title(), view.submission.description().unwrap_or(&format!("{} hosted on Hyaenidae", view.submission.title())), nav_state, {
@:button_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
@:card_title({
@view.submission.title()

View file

@ -1,3 +1,4 @@
@use crate::templates::button_js;
@use crate::templates::admin::submission_box;
@use crate::templates::layouts::home;
@use crate::{nav::NavState, submissions::ReportView};
@ -7,8 +8,9 @@
@(view: &ReportView, nav_state: &NavState)
@:home("Report Submission", view.submission.description().unwrap_or(&format!("Report {}'s
submission on Hyaenidae", view.profile.name())), nav_state, {}, {
@:home("Report Submission", view.submission.description().unwrap_or(&format!("Report {}'s submission on Hyaenidae", view.profile.name())), nav_state, {
@:button_js()
}, {
@:card(&Card::full_width().dark(nav_state.dark()), {
@:card_title({
Report @view.submission.title()

View file

@ -1,11 +1,12 @@
@use crate::templates::{button_js, file_js};
@use crate::{templates::{layouts::home, submissions::image}, nav::NavState, submissions::SubmissionState};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input, text_input, statics::{button_js, file_input_js}}, Button, Card};
@use hyaenidae_toolkit::{templates::{button_group, card, card_body, card_title, file_input, text_input}, Button, Card};
@(state: &SubmissionState, nav_state: &NavState)
@:home("Update Submission", "Update information or images", nav_state, {
<script src="/toolkit/@button_js.name"></script>
<script src="/toolkit/@file_input_js.name"></script>
@:button_js()
@:file_js()
}, {
@if !state.is_published() {
@:card(&Card::full_width().dark(nav_state.dark()), {