From 73d40e2f755c9ab43c060af5eded4687a316f887 Mon Sep 17 00:00:00 2001 From: asonix Date: Tue, 12 Jan 2021 22:39:59 -0600 Subject: [PATCH] Cache associated records when iterating over comments or submissions Rely more on the toolkit for UI Fix a couple hard-coded dark modes --- server/scss/layout.scss | 339 ++---------------- server/src/comments.rs | 163 +++------ server/src/main.rs | 52 --- server/src/nav.rs | 5 +- server/src/profiles/mod.rs | 175 ++++++--- server/src/submissions/mod.rs | 12 +- server/templates/comments/edit.rs.html | 5 +- server/templates/comments/nodes.rs.html | 63 ++-- server/templates/comments/profile_box.rs.html | 47 ++- server/templates/comments/public.rs.html | 13 +- server/templates/layouts/home.rs.html | 18 +- .../templates/profiles/create/banner.rs.html | 2 +- server/templates/profiles/create/bio.rs.html | 2 +- server/templates/profiles/create/done.rs.html | 2 +- server/templates/profiles/create/icon.rs.html | 2 +- .../profiles/create/require_login.rs.html | 2 +- server/templates/profiles/current.rs.html | 16 +- server/templates/profiles/delete.rs.html | 4 +- server/templates/profiles/drafts.rs.html | 2 +- server/templates/profiles/list.rs.html | 2 +- server/templates/profiles/public.rs.html | 32 +- .../profiles/submission_tile.rs.html | 2 +- server/templates/profiles/view.rs.html | 43 +-- .../templates/submissions/profile_box.rs.html | 16 +- server/templates/submissions/public.rs.html | 7 +- server/templates/submissions/update.rs.html | 6 +- 26 files changed, 325 insertions(+), 707 deletions(-) diff --git a/server/scss/layout.scss b/server/scss/layout.scss index dbf0792..e38dd0c 100644 --- a/server/scss/layout.scss +++ b/server/scss/layout.scss @@ -50,6 +50,7 @@ .profile-box { display: flex; flex: 1; + width: 100%; .profile-box--content { flex: 1; @@ -62,7 +63,7 @@ .profile-box--all-meta { display: flex; justify-content: space-between; - flex-wrap: wrap; + align-items: flex-start; width: 100%; } @@ -74,7 +75,7 @@ display: flex; align-items: center; flex-wrap: wrap; - margin-right: 4px; + margin-right: 8px; } .profile-box--meta--display .toolkit-link { @@ -82,21 +83,6 @@ font-size: 16px; } - .profile-box--icon { - position: relative; - height: 64px; - width: 64px; - border-radius: 32px; - border: 3px solid #444; - background-color: #555; - overflow: hidden; - margin-right: 16px; - - img { - width: 100%; - } - } - .profile-box--meta--display { margin-right: 4px; } @@ -106,83 +92,6 @@ } } -.profile-view { - background-color: #444; - color: #f5f5f5; - overflow: hidden; - - &.card-top { - border-radius: 3px 3px 0 0; - } - - &.standalone { - border-radius: 3px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - margin-bottom: 32px; - } - - img { - width: 100%; - } - - .profile-view--banner { - height: 233px; - width: 100%; - } - - .profile-view--banner--placeholder { - height: 100%; - min-height: 100%; - background-color: #555; - } - - .profile-view--content { - padding-bottom: 16px; - } - - .profile-view--content--top { - margin-top: -86px; - display: flex; - justify-content: flex-start; - align-items: flex-start; - flex-wrap: wrap; - padding: 0 16px; - } - - .profile-view--icon { - position: relative; - height: 128px; - width: 128px; - border-radius: 64px; - border: 3px solid #444; - background-color: #555; - overflow: hidden; - margin-right: 16px; - } - - .profile-view--meta { - padding-top: 20px; - text-shadow: 1px 1px 1px #222; - - span { - display: block; - } - } - - .profile-view--meta--display { - font-weight: 600; - font-size: 20px; - } - - .profile-view--meta--handle { - font-weight: 500; - } - - .profile-view--description { - padding: 16px; - } -} - .text-section, .button-section { padding: 8px 0; @@ -208,89 +117,32 @@ justify-content: center; } -.comment { - padding: 16px 0; - - &:first-child { - padding-top: 0; - } - &:last-child { - padding-bottom: 0; - } - - & + .comment { - border-top: 1px solid #555; - } - - .comment-body { - display: flex; - } - - .comment-text { - flex: 1; - } - - .comment-children { - padding-top: 16px; - - .comment:first-child { - border-top: 1px solid #555; - padding-top: 16px; - } - } -} - -.comment-box > .comment, -.comment-box > .comment-children > .comment { - > .comment-children, - > .comment-children > .comment > .comment-children, - > .comment-children > .comment > .comment-children > .comment > .comment-children, - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children, - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children, - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children, - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children, - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children, - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children, - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children { - border-left: 4px solid #555; - padding-left: 16px; - } -} - .submission-tiles { display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); .submission-icon { position: relative; border-radius: 0px; } + picture { + display: block; + width: 100%; + height: 100%; + } + + img { + object-fit: cover; + } + .toolkit-link { display: block; height: 100%; width: 100%; font-style: none; font-weight: 500; + color: #e5e5e5; &:hover, &:focus, @@ -332,15 +184,11 @@ } } -.image-box { - max-width: 100%; - background-color: #f5f5f5; - overflow: hidden; - - img { - display: block; - width: 100%; - } +.submission-box { + max-height: 70vh; + display: flex; + justify-content: center; + background-color: #000; } @media (max-width: 700px) { @@ -360,150 +208,7 @@ } } - .profile-view { - &.standalone { - border-radius: 0; - } - - .profile-view--banner { - height: 33vw; - } - } -} - -@media (max-width: 680px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 660px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 640px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 620px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 600px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children > .comment > .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 580px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children > .comment > .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 560px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - - > .comment-children > .comment > .comment-children > .comment > .comment-children > .comment > - .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 540px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - > .comment-children > .comment > .comment-children > .comment > .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 520px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - > .comment-children > .comment > .comment-children, { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 500px) { - .comment-box > .comment, - .comment-box > .comment-children > .comment { - > .comment-children { - border-left: 0; - padding-left: 0; - } - } -} - -@media (max-width: 350px) { - .profile-view { - .profile-view--content--top { - margin-top: -56px; - display: block; - } - - .profile-view--icon { - width: 96px; - height: 96px; - } - - .profile-view--meta { - padding-bottom: 0; - } + .standalone { + border-radius: 0; } } diff --git a/server/src/comments.rs b/server/src/comments.rs index 6599050..adcd18e 100644 --- a/server/src/comments.rs +++ b/server/src/comments.rs @@ -61,21 +61,42 @@ impl CommentNode { let submission = Submission::from_id(submission_id, state)?; let author = Profile::from_id(submission.profile_id(), state)?; - let items = state + let is_self = viewer.map(|id| id == author.id()).unwrap_or(false); + + let mut profiles: HashMap = HashMap::new(); + + let children = state .profiles .store .comments .for_submission(submission_id) .filter_map(|comment_id| state.profiles.store.comments.by_id(comment_id).ok()?) - .rev(); + .filter_map(|comment| { + if let Some(profile) = profiles.get(&comment.profile_id()) { + CommentNode::from_root(comment, profile.clone(), viewer, &mut profiles, state) + } else { + let profile = Profile::from_id(comment.profile_id(), state).ok()?; + profiles.insert(profile.id(), profile.clone()); + CommentNode::from_root(comment, profile, viewer, &mut profiles, state) + } + }) + .collect(); - Ok(into_nodes(submission, author, viewer, items, state)) + Ok(CommentNode { + item: ItemWithAuthor { + parent: ItemWithAuthorInner::Submission(submission), + author, + }, + is_self, + children, + }) } pub(crate) fn from_root( comment: Comment, author: Profile, self_profile: Option, + profiles: &mut HashMap, state: &State, ) -> Option { if let Some(self_id) = self_profile { @@ -95,11 +116,15 @@ impl CommentNode { .replies_for(comment.id()) .filter_map(|comment_id| state.profiles.store.comments.by_id(comment_id).ok()?) .filter_map(|comment| { - Some((Profile::from_id(comment.profile_id(), state).ok()?, comment)) - }) - .filter_map(|(profile, comment)| { - CommentNode::from_root(comment, profile, self_profile, state) + if let Some(profile) = profiles.get(&comment.profile_id()) { + CommentNode::from_root(comment, profile.clone(), self_profile, profiles, state) + } else { + let profile = Profile::from_id(comment.profile_id(), state).ok()?; + profiles.insert(profile.id(), profile.clone()); + CommentNode::from_root(comment, profile, self_profile, profiles, state) + } }) + .rev() .collect(); let is_self = self_profile.map(|id| id == author.id()).unwrap_or(false); @@ -113,115 +138,6 @@ impl CommentNode { children, }) } - - fn insert( - &mut self, - mut indexes: Vec, - comment: Comment, - author: Profile, - is_self: bool, - ) -> Option> { - let comment_id = comment.id(); - - if let Some(index) = indexes.pop() { - if let Some(node) = self.children.get_mut(index) { - if let Some(mut v) = node.insert(indexes, comment, author, is_self) { - v.push(index); - return Some(v); - } else { - log::warn!("Failed to insert comment {}", comment_id); - } - } else { - match &self.item.parent { - ItemWithAuthorInner::Comment(ref c) => log::error!( - "Error inserting {}: Failed to get child {} of comment {}", - comment_id, - index, - c.id(), - ), - ItemWithAuthorInner::Submission(ref s) => log::error!( - "Error inserting {}: Failed to get child {} of submission {}", - comment_id, - index, - s.id() - ), - } - } - } else { - let index = self.children.len(); - self.children.push(CommentNode { - item: ItemWithAuthor { - parent: ItemWithAuthorInner::Comment(comment), - author, - }, - is_self, - children: vec![], - }); - return Some(vec![index]); - } - - None - } -} - -// Assumes comments are in-order by publish date -fn into_nodes( - submission: Submission, - author: Profile, - viewer: Option, - items: impl IntoIterator, - state: &State, -) -> CommentNode { - let is_self = viewer.map(|id| id == author.id()).unwrap_or(false); - let mut hashmap: HashMap> = HashMap::new(); - let mut node = CommentNode { - item: ItemWithAuthor { - parent: ItemWithAuthorInner::Submission(submission), - author, - }, - is_self, - children: vec![], - }; - - for comment in items { - if let Some(viewer) = viewer { - if !can_view_comment_no_recurse(viewer, &comment, state).unwrap_or(false) { - continue; - } - } else { - if !can_view_comment_logged_out_no_recurse(&comment, state).unwrap_or(false) { - continue; - } - } - - let comment_id = comment.id(); - - if let Ok(author) = Profile::from_id(comment.profile_id(), state) { - let is_self = viewer.map(|id| id == author.id()).unwrap_or(false); - - if let Some(reply_to) = comment.comment_id() { - if let Some(indexes) = hashmap.get(&reply_to) { - if let Some(indexes) = node.insert(indexes.clone(), comment, author, is_self) { - hashmap.insert(comment_id, indexes); - } else { - log::warn!("Failed to insert nested comment {}", comment_id); - } - } else { - log::warn!("Reply To {} doesn't exist", reply_to); - } - } else { - if let Some(indexes) = node.insert(vec![], comment, author, is_self) { - hashmap.insert(comment_id, indexes); - } else { - log::warn!("Failed to insert top-level comment {}", comment_id); - } - } - } else { - log::warn!("Failed to get author for comment {}", comment_id); - } - } - - node } fn to_comment_page(comment_id: Uuid) -> HttpResponse { @@ -530,11 +446,16 @@ fn prepare_view( }; let author = Profile::from_id(comment.profile_id(), &state)?; - let node = - match CommentNode::from_root(comment, author, profile.as_ref().map(|p| p.id()), &state) { - Some(node) => node, - None => return Ok(None), - }; + let node = match CommentNode::from_root( + comment, + author, + profile.as_ref().map(|p| p.id()), + &mut HashMap::new(), + &state, + ) { + Some(node) => node, + None => return Ok(None), + }; let view = CommentView::new( submission, diff --git a/server/src/main.rs b/server/src/main.rs index 4914d5a..3ef23ac 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -299,58 +299,6 @@ fn body_class(dark: bool) -> &'static str { } } -fn time(time: chrono::DateTime) -> String { - time.format("%B %d, %Y, %I:%M %P").to_string() -} - -fn published_time(time: chrono::DateTime) -> String { - let duration = chrono::Utc::now() - time; - if let Some(years) = - duration - .num_days() - .checked_div(365) - .and_then(|years| if years > 0 { Some(years) } else { None }) - { - if years == 1 { - format!("{} year ago", years) - } else { - format!("{} years ago", years) - } - } else if duration.num_weeks() > 0 { - if duration.num_weeks() == 1 { - format!("{} week ago", duration.num_weeks()) - } else { - format!("{} weeks ago", duration.num_weeks()) - } - } else if duration.num_days() > 0 { - if duration.num_days() == 1 { - format!("{} day ago", duration.num_days()) - } else { - format!("{} days ago", duration.num_days()) - } - } else if duration.num_hours() > 0 { - if duration.num_hours() == 1 { - format!("{} hour ago", duration.num_hours()) - } else { - format!("{} hours ago", duration.num_hours()) - } - } else if duration.num_minutes() > 0 { - if duration.num_minutes() == 1 { - format!("{} minute ago", duration.num_minutes()) - } else { - format!("{} minutes ago", duration.num_minutes()) - } - } else if duration.num_seconds() > 0 { - if duration.num_seconds() == 1 { - format!("{} second ago", duration.num_seconds()) - } else { - format!("{} seconds ago", duration.num_seconds()) - } - } else { - "now".to_owned() - } -} - async fn home( req: HttpRequest, page: Option>, diff --git a/server/src/nav.rs b/server/src/nav.rs index af57a32..b7d5d7c 100644 --- a/server/src/nav.rs +++ b/server/src/nav.rs @@ -24,13 +24,11 @@ impl FromRequest for NavState { let mut nav = vec![]; if let Some(logout_state) = logout { - let home = Button::primary("Home"); - let submission = Button::secondary("New Submission"); + let submission = Button::primary("New Submission"); let account = Button::secondary("Account"); let logout = Button::primary_outline("Logout"); logout_state.button(&logout); - home.href("/").dark(dark); submission.href("/submissions/create").dark(dark); account.href("/session/account").dark(dark); logout.dark(dark); @@ -45,7 +43,6 @@ impl FromRequest for NavState { btn }; - nav.push(home); nav.push(submission); nav.push(profile); nav.push(account); diff --git a/server/src/profiles/mod.rs b/server/src/profiles/mod.rs index 15214c0..7b839da 100644 --- a/server/src/profiles/mod.rs +++ b/server/src/profiles/mod.rs @@ -8,6 +8,7 @@ use actix_web::{web, HttpRequest, HttpResponse, Scope}; use hyaenidae_accounts::User; use hyaenidae_profiles::store::{File, Submission}; use hyaenidae_toolkit::Button; +use std::collections::HashMap; use uuid::Uuid; mod middleware; @@ -315,6 +316,8 @@ pub(crate) fn build_submissions( ) -> SubmissionAggregation { let mut nav = vec![]; let mut submissions: Vec; + let mut view_state = ViewState::new(); + match page { Some(SubmissionPage::Max { max }) => { submissions = if profile.is_some() { @@ -325,7 +328,12 @@ pub(crate) fn build_submissions( .submissions .drafted_older_than_for_profile(max) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id( + submission_id, + viewed_by, + &mut view_state, + &state, + ) }) .take(per_page + 1) .collect() @@ -336,7 +344,12 @@ pub(crate) fn build_submissions( .submissions .published_older_than_for_profile(max) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id( + submission_id, + viewed_by, + &mut view_state, + &state, + ) }) .take(per_page + 1) .collect() @@ -348,7 +361,7 @@ pub(crate) fn build_submissions( .submissions .published_older_than(max) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id(submission_id, viewed_by, &mut view_state, &state) }) .take(per_page + 1) .collect() @@ -386,7 +399,12 @@ pub(crate) fn build_submissions( .submissions .drafted_newer_than_for_profile(min) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id( + submission_id, + viewed_by, + &mut view_state, + &state, + ) }) .take(per_page + 2) .collect() @@ -397,7 +415,12 @@ pub(crate) fn build_submissions( .submissions .published_newer_than_for_profile(min) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id( + submission_id, + viewed_by, + &mut view_state, + &state, + ) }) .take(per_page + 2) .collect() @@ -409,7 +432,7 @@ pub(crate) fn build_submissions( .submissions .published_newer_than(min) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id(submission_id, viewed_by, &mut view_state, &state) }) .take(per_page + 2) .collect() @@ -450,7 +473,12 @@ pub(crate) fn build_submissions( .submissions .drafted_for_profile(profile.id()) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id( + submission_id, + viewed_by, + &mut view_state, + &state, + ) }) .take(per_page + 1) .collect() @@ -461,7 +489,12 @@ pub(crate) fn build_submissions( .submissions .published_for_profile(profile.id()) .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id( + submission_id, + viewed_by, + &mut view_state, + &state, + ) }) .take(per_page + 1) .collect() @@ -473,7 +506,7 @@ pub(crate) fn build_submissions( .submissions .published() .filter_map(|submission_id| { - SubmissionView::from_id(submission_id, viewed_by, &state) + SubmissionView::from_id(submission_id, viewed_by, &mut view_state, &state) }) .take(per_page + 1) .collect() @@ -567,6 +600,22 @@ pub struct SubmissionView { first_file: File, } +struct ViewState { + profiles: HashMap, + blocks: HashMap, + follows: HashMap, +} + +impl ViewState { + fn new() -> Self { + ViewState { + profiles: HashMap::new(), + blocks: HashMap::new(), + follows: HashMap::new(), + } + } +} + impl SubmissionView { fn id(&self) -> Uuid { self.submission.id() @@ -604,7 +653,12 @@ impl SubmissionView { format!("/submissions/{}", self.submission.id()) } - fn from_id(submission_id: Uuid, viewed_by: Option, state: &State) -> Option { + fn from_id( + submission_id: Uuid, + viewed_by: Option, + view_state: &mut ViewState, + state: &State, + ) -> Option { let submission = state .profiles .store @@ -620,54 +674,79 @@ impl SubmissionView { return None; } - let poster = Profile::from_id(submission.profile_id(), state).ok()?; + let poster = if let Some(profile) = view_state.profiles.get(&submission.profile_id()) { + profile.clone() + } else { + let profile = Profile::from_id(submission.profile_id(), state).ok()?; + view_state.profiles.insert(profile.id(), profile.clone()); + profile + }; - if let Some(profile_id) = viewed_by { - let blocking = state - .profiles - .store - .view - .blocks - .by_forward(submission.profile_id(), profile_id) - .ok()? - .is_some(); + if viewed_by.is_none() && poster.login_required() { + return None; + } - if blocking { - return None; - } - - let blocked = state - .profiles - .store - .view - .blocks - .by_forward(profile_id, submission.profile_id()) - .ok()? - .is_some(); - - if blocked { + if let Some(block) = view_state.blocks.get(&poster.id()) { + if *block { return None; } } else { - if poster.login_required() { - return None; + if let Some(profile_id) = viewed_by { + let blocking = state + .profiles + .store + .view + .blocks + .by_forward(submission.profile_id(), profile_id) + .ok()? + .is_some(); + + if blocking { + view_state.blocks.insert(poster.id(), true); + return None; + } + + let blocked = state + .profiles + .store + .view + .blocks + .by_forward(profile_id, submission.profile_id()) + .ok()? + .is_some(); + + if blocked { + view_state.blocks.insert(poster.id(), true); + return None; + } + + view_state.blocks.insert(poster.id(), false); } } if submission.is_followers_only() { - let profile_id = viewed_by?; + if let Some(follow) = view_state.follows.get(&poster.id()) { + if !follow { + return None; + } + } else { + let profile_id = viewed_by?; - if !is_self - && state - .profiles - .store - .view - .follows - .by_forward(submission.profile_id(), profile_id) - .ok()? - .is_none() - { - return None; + if !is_self + && state + .profiles + .store + .view + .follows + .by_forward(submission.profile_id(), profile_id) + .ok()? + .is_none() + { + view_state.follows.insert(poster.id(), false); + return None; + } + + view_state.follows.insert(poster.id(), true); } } diff --git a/server/src/submissions/mod.rs b/server/src/submissions/mod.rs index bfa3a5e..fcb945c 100644 --- a/server/src/submissions/mod.rs +++ b/server/src/submissions/mod.rs @@ -463,7 +463,7 @@ async fn update_page( return Ok(crate::to_404()); } - let state = SubmissionState::new(submission, true); + let state = SubmissionState::new(submission, nav_state.dark()); crate::rendered(HttpResponse::Ok(), |cursor| { crate::templates::submissions::update(cursor, &state, &nav_state) @@ -606,7 +606,7 @@ async fn update_submission( }; } - let mut state = SubmissionState::new(submission, true); + let mut state = SubmissionState::new(submission, nav_state.dark()); if let Some(error) = title_error { state.title_error(error); } @@ -651,7 +651,7 @@ async fn add_file( Err(e) => e.to_string(), }; - let mut state = SubmissionState::new(submission, true); + let mut state = SubmissionState::new(submission, nav_state.dark()); state.file_error(error); crate::rendered(HttpResponse::BadRequest(), |cursor| { @@ -690,7 +690,7 @@ async fn remove_file( Err(e) => e.to_string(), }; - let mut state = SubmissionState::new(submission, true); + let mut state = SubmissionState::new(submission, nav_state.dark()); state.remove_file_error(form.file_id, error); crate::rendered(HttpResponse::BadRequest(), |cursor| { @@ -724,7 +724,7 @@ async fn publish_submission( } }; - let mut state = SubmissionState::new(submission, true); + let mut state = SubmissionState::new(submission, nav_state.dark()); state.publish_error(error); crate::rendered(HttpResponse::BadRequest(), |cursor| { @@ -754,7 +754,7 @@ async fn delete_submission( Err(e) => e.to_string(), }; - let mut state = SubmissionState::new(submission, true); + let mut state = SubmissionState::new(submission, nav_state.dark()); state.delete_error(error); crate::rendered(HttpResponse::BadRequest(), |cursor| { diff --git a/server/templates/comments/edit.rs.html b/server/templates/comments/edit.rs.html index a87c7a2..bb4cdca 100644 --- a/server/templates/comments/edit.rs.html +++ b/server/templates/comments/edit.rs.html @@ -4,6 +4,7 @@ @use crate::nav::NavState; @use hyaenidae_toolkit::{templates::button_group, Button}; @use hyaenidae_toolkit::{templates::{card, card_title, card_body}, Card}; +@use hyaenidae_toolkit::templates::nested; @use hyaenidae_toolkit::{templates::text_input}; @(view: &CommentView, nav_state: &NavState) @@ -30,11 +31,11 @@ @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ Replies }) @:card_body({ -
+ @:nested(nav_state.dark(), { @for child in &view.comments.children { @:nodes(child, &view.comments.item, view.logged_in, nav_state.dark()) } -
+ }) }) }) } diff --git a/server/templates/comments/nodes.rs.html b/server/templates/comments/nodes.rs.html index e158a7c..612c93d 100644 --- a/server/templates/comments/nodes.rs.html +++ b/server/templates/comments/nodes.rs.html @@ -1,49 +1,38 @@ @use crate::comments::{CommentNode, ItemWithAuthor}; @use crate::templates::comments::{nodes, profile_box}; @use hyaenidae_toolkit::templates::link; +@use hyaenidae_toolkit::templates::{nested_children, nested_node}; @(node: &CommentNode, replying_to: &ItemWithAuthor, logged_in: bool, dark: bool) @if let Some((comment, author)) = node.item.comment() { -
-
- @:profile_box(author, comment, replying_to, dark, { - -} else { - @if node.has_children() { -
- @for child in &node.children { - @:nodes(child, &node.item, logged_in, dark) - } -
- } + }, { + @comment.body() + }) + }) +} +@if node.has_children() { + @:nested_children({ + @for child in &node.children { + @:nodes(child, &node.item, logged_in, dark) + } + }) } diff --git a/server/templates/comments/profile_box.rs.html b/server/templates/comments/profile_box.rs.html index db0da80..0ee7e08 100644 --- a/server/templates/comments/profile_box.rs.html +++ b/server/templates/comments/profile_box.rs.html @@ -1,46 +1,45 @@ @use crate::templates::profiles::icon; @use crate::{profiles::Profile, comments::{Comment, ItemWithAuthor}}; @use hyaenidae_toolkit::{templates::link, Link}; +@use hyaenidae_toolkit::templates::icon as tkicon; +@use hyaenidae_toolkit::templates::ago; @(profile: &Profile, comment: &Comment, replying_to: &ItemWithAuthor, dark: bool, meta: Content, body: Content)
- @:link(&Link::current_tab(&profile.view_path()).dark(dark), { -
- @if let Some(key) = profile.icon_key() { - @:icon(key, &profile.name()) - } -
+ @:tkicon(&profile.view_path(), true, dark, { + @if let Some(key) = profile.icon_key() { + @:icon(key, &profile.name()) + } })
-
- @if let Some(name) = profile.display_name() { -
+
+
+ @if let Some(name) = profile.display_name() { +
+ @:link(&Link::current_tab(&profile.view_path()).plain(true).dark(dark), { + @name + }) +
+ } +
@:link(&Link::current_tab(&profile.view_path()).plain(true).dark(dark), { - @name + @profile.full_handle() })
- } -
- @:link(&Link::current_tab(&profile.view_path()).plain(true).dark(dark), { - @profile.full_handle() - }) +
+ posted @:ago(comment.published(), dark) +
-
- posted - @:link(&Link::current_tab("#").title(&crate::time(comment.published())).plain(true).dark(dark), { - @crate::published_time(comment.published()) +
+ @:link(&replying_to.link(dark), { + Replying to @replying_to.name() })
@:meta()
-
- @:link(&replying_to.link(dark), { - Replying to @replying_to.name() - }) -
@:body()
diff --git a/server/templates/comments/public.rs.html b/server/templates/comments/public.rs.html index ee1ae56..281bfae 100644 --- a/server/templates/comments/public.rs.html +++ b/server/templates/comments/public.rs.html @@ -5,6 +5,7 @@ @use hyaenidae_toolkit::{templates::button_group, Button}; @use hyaenidae_toolkit::{templates::{card, card_title, card_body}, Card}; @use hyaenidae_toolkit::templates::link; +@use hyaenidae_toolkit::templates::{nested, nested_children}; @use hyaenidae_toolkit::templates::text_input; @(view: &CommentView, nav_state: &NavState) @@ -53,11 +54,13 @@ @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ Replies }) @:card_body({ -
- @for child in &view.comments.children { - @:nodes(child, &view.comments.item, view.logged_in, nav_state.dark()) - } -
+ @:nested(nav_state.dark(), { + @:nested_children({ + @for child in &view.comments.children { + @:nodes(child, &view.comments.item, view.logged_in, nav_state.dark()) + } + }) + }) }) }) } diff --git a/server/templates/layouts/home.rs.html b/server/templates/layouts/home.rs.html index ef11b5b..36c1499 100644 --- a/server/templates/layouts/home.rs.html +++ b/server/templates/layouts/home.rs.html @@ -34,14 +34,16 @@
}) diff --git a/server/templates/profiles/create/banner.rs.html b/server/templates/profiles/create/banner.rs.html index 1217a2b..1bcb5bd 100644 --- a/server/templates/profiles/create/banner.rs.html +++ b/server/templates/profiles/create/banner.rs.html @@ -36,7 +36,7 @@ }) @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ Preview }) - @:view("", profile) + @:view(profile, nav_state.dark()) }) }) diff --git a/server/templates/profiles/create/bio.rs.html b/server/templates/profiles/create/bio.rs.html index 1e358ae..9edf120 100644 --- a/server/templates/profiles/create/bio.rs.html +++ b/server/templates/profiles/create/bio.rs.html @@ -30,6 +30,6 @@ }) @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ Preview }) - @:view("", profile) + @:view(profile, nav_state.dark()) }) }) diff --git a/server/templates/profiles/create/done.rs.html b/server/templates/profiles/create/done.rs.html index 62bd08b..f08cbcc 100644 --- a/server/templates/profiles/create/done.rs.html +++ b/server/templates/profiles/create/done.rs.html @@ -25,6 +25,6 @@ }) @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ Preview }) - @:view("", profile) + @:view(profile, nav_state.dark()) }) }) diff --git a/server/templates/profiles/create/icon.rs.html b/server/templates/profiles/create/icon.rs.html index 7bd497d..8873e47 100644 --- a/server/templates/profiles/create/icon.rs.html +++ b/server/templates/profiles/create/icon.rs.html @@ -36,7 +36,7 @@ }) @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ Preview }) - @:view("", profile) + @:view(profile, nav_state.dark()) }) }) diff --git a/server/templates/profiles/create/require_login.rs.html b/server/templates/profiles/create/require_login.rs.html index 9ca9bf3..49908e2 100644 --- a/server/templates/profiles/create/require_login.rs.html +++ b/server/templates/profiles/create/require_login.rs.html @@ -48,6 +48,6 @@ }) @:card(&Card::full_width().dark(true), { @:card_title({ Preview }) - @:view("", profile) + @:view(profile, nav_state.dark()) }) }) diff --git a/server/templates/profiles/current.rs.html b/server/templates/profiles/current.rs.html index fda199a..277a8cb 100644 --- a/server/templates/profiles/current.rs.html +++ b/server/templates/profiles/current.rs.html @@ -7,8 +7,8 @@ }, { - @:view("standalone", &state.profile) - @:card(&Card::full_width().dark(nav_state.dark()), { + @:card(Card::full_width().dark(nav_state.dark()), { @:view(&state.profile, nav_state.dark()) }) + @:card(Card::full_width().dark(nav_state.dark()), { @:card_title({ Profile Actions }) @:card_body({ @:button_group(&[ @@ -17,7 +17,7 @@ ]) }) }) - @:card(&Card::full_width().dark(nav_state.dark()), { + @:card(Card::full_width().dark(nav_state.dark()), { @:card_title({ Update Profile }) @:card_body({
@@ -50,9 +50,7 @@
@if let Some(key) = state.profile.icon_key() { -
- @:icon(key, &state.profile.name()) -
+ @:icon(key, &state.profile.name()) } else {

No icon set

} @@ -85,9 +83,7 @@
@if let Some(key) = state.profile.banner_key() { -
- @:banner(key, &state.profile.name()) -
+ @:banner(key, &state.profile.name()) } else {

No banner set

} @@ -144,7 +140,7 @@ }) }) - @:card(&Card::full_width().dark(nav_state.dark()), { + @:card(Card::full_width().dark(nav_state.dark()), { @:card_title({ Danger }) @:card_body({ @:button_group(&[ diff --git a/server/templates/profiles/delete.rs.html b/server/templates/profiles/delete.rs.html index 058ddae..8f9a2a9 100644 --- a/server/templates/profiles/delete.rs.html +++ b/server/templates/profiles/delete.rs.html @@ -6,8 +6,8 @@ @:home("Delete Profile", &format!("Delete {}", profile.name()), nav_state, { }, { - @:view("standalone account-page", profile) - @:card(&Card::full_width().dark(nav_state.dark()), { + @:card(Card::full_width().dark(nav_state.dark()), { @:view(profile, nav_state.dark()) }) + @:card(Card::full_width().dark(nav_state.dark()), {
@:card_title({ Delete Profile }) @:card_body({ diff --git a/server/templates/profiles/drafts.rs.html b/server/templates/profiles/drafts.rs.html index 36f1996..1dd0283 100644 --- a/server/templates/profiles/drafts.rs.html +++ b/server/templates/profiles/drafts.rs.html @@ -5,7 +5,7 @@ @(pview: &ProfileView, nav_state: &NavState) @:home(&pview.profile.name(), pview.profile.description().unwrap_or(&format!("{}'s profile on Hyaenidae", pview.profile.name())), nav_state, {}, { - @:view("standalone account-page", &pview.profile) + @:card(Card::full_width().dark(nav_state.dark()), { @:view(&pview.profile, nav_state.dark()) }) @if pview.is_self { @:card(Card::full_width().dark(nav_state.dark()), { @:card_title({ Profile Actions }) diff --git a/server/templates/profiles/list.rs.html b/server/templates/profiles/list.rs.html index c67df2c..82efc27 100644 --- a/server/templates/profiles/list.rs.html +++ b/server/templates/profiles/list.rs.html @@ -6,7 +6,7 @@ @:home("Switch Profile", "Select the profile you wish to use", nav_state, {}, { @for profile in profiles { @:card(&Card::full_width().dark(nav_state.dark()), { - @:view("card-top", profile) + @:view(profile, nav_state.dark()) @:card_body({ diff --git a/server/templates/profiles/public.rs.html b/server/templates/profiles/public.rs.html index 49ad12c..e8afedd 100644 --- a/server/templates/profiles/public.rs.html +++ b/server/templates/profiles/public.rs.html @@ -5,7 +5,7 @@ @(pview: &ProfileView, nav_state: &NavState) @:home(&pview.profile.name(), pview.profile.description().unwrap_or(&format!("{}'s profile on Hyaenidae", pview.profile.name())), nav_state, {}, { - @:view("standalone", &pview.profile) + @:card(Card::full_width().dark(nav_state.dark()), { @:view(&pview.profile, nav_state.dark()) }) @if pview.is_self { @:card(Card::full_width().dark(nav_state.dark()), { @:card_title({ Profile Actions }) @@ -18,19 +18,21 @@ }) }) } - @:card(Card::full_width().dark(nav_state.dark()), { - @:card_title({ Submissions }) - @:card_section({ -
- @for submission in &pview.submissions { - @:submission_tile(submission, pview.viewer, nav_state.dark()) - } -
- }) - @if pview.nav().len() > 0 { - @:card_body({ - @:button_group(&pview.nav()) + @if !pview.submissions.is_empty() { + @:card(Card::full_width().dark(nav_state.dark()), { + @:card_title({ Submissions }) + @:card_section({ +
+ @for submission in &pview.submissions { + @:submission_tile(submission, pview.viewer, nav_state.dark()) + } +
}) - } - }) + @if pview.nav().len() > 0 { + @:card_body({ + @:button_group(&pview.nav()) + }) + } + }) + } }) diff --git a/server/templates/profiles/submission_tile.rs.html b/server/templates/profiles/submission_tile.rs.html index 6bd185a..d5c8c02 100644 --- a/server/templates/profiles/submission_tile.rs.html +++ b/server/templates/profiles/submission_tile.rs.html @@ -7,7 +7,7 @@ @if let Some(key) = submission.pictrs_key() { @:link(&Link::current_tab(&submission.view_path()).plain(true).dark(dark), { -
+
@if submission.is_published() {
diff --git a/server/templates/profiles/view.rs.html b/server/templates/profiles/view.rs.html index 52b8631..70ca0c4 100644 --- a/server/templates/profiles/view.rs.html +++ b/server/templates/profiles/view.rs.html @@ -1,35 +1,14 @@ @use crate::{templates::profiles::{banner, icon}, profiles::Profile}; +@use hyaenidae_toolkit::templates::profile as tkprofile; -@(classes: &str, profile: &Profile) +@(profile: &Profile, dark: bool) -
-
- @if let Some(key) = profile.banner_key() { - @:banner(key, &profile.name()) - } else { -
- } -
-
-
-
- @if let Some(key) = profile.icon_key() { - @:icon(key, &profile.name()) - } -
-
-
- @if let Some(name) = profile.display_name() { - @name - } else { -   - } -
-
@profile.full_handle()
-
-
- @if let Some(description) = profile.description() { -
@description
- } -
-
+@:tkprofile(&profile.view_path(), profile.display_name(), &profile.full_handle(), profile.description(), dark, { + @if let Some(key) = profile.icon_key() { + @:icon(key, &profile.name()) + } +}, { + @if let Some(key) = profile.banner_key() { + @:banner(key, &profile.name()) + } +}) diff --git a/server/templates/submissions/profile_box.rs.html b/server/templates/submissions/profile_box.rs.html index 9346576..a7c68b8 100644 --- a/server/templates/submissions/profile_box.rs.html +++ b/server/templates/submissions/profile_box.rs.html @@ -1,16 +1,16 @@ @use crate::templates::profiles::icon; @use crate::{profiles::Profile, submissions::Submission}; @use hyaenidae_toolkit::{templates::link, Link}; +@use hyaenidae_toolkit::templates::ago; +@use hyaenidae_toolkit::templates::icon as tkicon; @(profile: &Profile, submission: &Submission, dark: bool, body: Content)
- @:link(&Link::current_tab(&profile.view_path()).dark(dark), { -
- @if let Some(key) = profile.icon_key() { - @:icon(key, &profile.name()) - } -
+ @:tkicon(&profile.view_path(), true, dark, { + @if let Some(key) = profile.icon_key() { + @:icon(key, &profile.name()) + } })
@@ -30,9 +30,7 @@ @if let Some(published) = submission.published() {
posted - @:link(&Link::current_tab("#").title(&crate::time(published)).plain(true).dark(dark), { - @crate::published_time(published) - }) + @:ago(published, dark)
}
diff --git a/server/templates/submissions/public.rs.html b/server/templates/submissions/public.rs.html index 93d3734..4551a42 100644 --- a/server/templates/submissions/public.rs.html +++ b/server/templates/submissions/public.rs.html @@ -4,6 +4,7 @@ @use crate::{nav::NavState, submissions::SubmissionView}; @use hyaenidae_toolkit::{templates::button_group, Button}; @use hyaenidae_toolkit::{templates::{card, card_body, card_section, card_title}, Card}; +@use hyaenidae_toolkit::templates::nested; @use hyaenidae_toolkit::templates::text_input; @(view: &SubmissionView, nav_state: &NavState) @@ -15,7 +16,7 @@ }) @:card_section({ @if let Some(file) = view.current_file.pictrs() { -
+
@:image(file.key(), &view.submission.title(), view.file_num)
} else { @@ -64,9 +65,9 @@ @:card(&Card::full_width().dark(nav_state.dark()), { @:card_title({ Comments }) @:card_body({ -
+ @:nested(nav_state.dark(), { @:nodes(&view.comments, &view.replying_to(), view.profile.is_some(), nav_state.dark()) -
+ }) }) }) } diff --git a/server/templates/submissions/update.rs.html b/server/templates/submissions/update.rs.html index 1785fa8..d181eee 100644 --- a/server/templates/submissions/update.rs.html +++ b/server/templates/submissions/update.rs.html @@ -45,9 +45,7 @@ @:card_body({
-
- @:image(file.key(), &state.submission.title(), tup.0) -
+ @:image(file.key(), &state.submission.title(), tup.0)

Remove Image From Submission

@@ -84,7 +82,7 @@ @:card_title({ View Submission }) @:card_body({ @:button_group(&[ - &Button::primary("View").href(&state.view_path()), + &Button::primary("View").href(&state.view_path()).dark(nav_state.dark()), ]) }) })