Cache associated records when iterating over comments or submissions
Rely more on the toolkit for UI Fix a couple hard-coded dark modes
This commit is contained in:
parent
979c6386ef
commit
73d40e2f75
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Uuid, Profile> = 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<Uuid>,
|
||||
profiles: &mut HashMap<Uuid, Profile>,
|
||||
state: &State,
|
||||
) -> Option<Self> {
|
||||
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<usize>,
|
||||
comment: Comment,
|
||||
author: Profile,
|
||||
is_self: bool,
|
||||
) -> Option<Vec<usize>> {
|
||||
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<Uuid>,
|
||||
items: impl IntoIterator<Item = Comment>,
|
||||
state: &State,
|
||||
) -> CommentNode {
|
||||
let is_self = viewer.map(|id| id == author.id()).unwrap_or(false);
|
||||
let mut hashmap: HashMap<Uuid, Vec<usize>> = 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,
|
||||
|
|
|
@ -299,58 +299,6 @@ fn body_class(dark: bool) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
fn time(time: chrono::DateTime<chrono::Utc>) -> String {
|
||||
time.format("%B %d, %Y, %I:%M %P").to_string()
|
||||
}
|
||||
|
||||
fn published_time(time: chrono::DateTime<chrono::Utc>) -> 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<web::Query<SubmissionPage>>,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<SubmissionView>;
|
||||
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<Uuid, Profile>,
|
||||
blocks: HashMap<Uuid, bool>,
|
||||
follows: HashMap<Uuid, bool>,
|
||||
}
|
||||
|
||||
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<Uuid>, state: &State) -> Option<Self> {
|
||||
fn from_id(
|
||||
submission_id: Uuid,
|
||||
viewed_by: Option<Uuid>,
|
||||
view_state: &mut ViewState,
|
||||
state: &State,
|
||||
) -> Option<Self> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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({
|
||||
<div class="comment-box">
|
||||
@:nested(nav_state.dark(), {
|
||||
@for child in &view.comments.children {
|
||||
@:nodes(child, &view.comments.item, view.logged_in, nav_state.dark())
|
||||
}
|
||||
</div>
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
<div class="comment">
|
||||
<div class="comment-body">
|
||||
@:profile_box(author, comment, replying_to, dark, {
|
||||
<div class="comment-links">
|
||||
<div>
|
||||
@:link(&node.item.link(dark), {
|
||||
@if logged_in {
|
||||
reply
|
||||
} else {
|
||||
view
|
||||
}
|
||||
})
|
||||
</div>
|
||||
@if let Some(state) = node.edit_link(dark) {
|
||||
<div>
|
||||
@:link(&state, { edit })
|
||||
</div>
|
||||
@:nested_node({
|
||||
@:profile_box(author, comment, replying_to, dark, {
|
||||
<div class="comment-links">
|
||||
<div>
|
||||
@:link(&node.item.link(dark), {
|
||||
@if logged_in {
|
||||
reply
|
||||
} else {
|
||||
view
|
||||
}
|
||||
})
|
||||
</div>
|
||||
}, {
|
||||
<div class="comment-text">
|
||||
@comment.body()
|
||||
</div>
|
||||
})
|
||||
</div>
|
||||
@if node.has_children() {
|
||||
<div class="comment-children">
|
||||
@for child in &node.children {
|
||||
@:nodes(child, &node.item, logged_in, dark)
|
||||
@if let Some(state) = node.edit_link(dark) {
|
||||
<div>
|
||||
@:link(&state, { edit })
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} else {
|
||||
@if node.has_children() {
|
||||
<div class="comment-children">
|
||||
@for child in &node.children {
|
||||
@:nodes(child, &node.item, logged_in, dark)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}, {
|
||||
@comment.body()
|
||||
})
|
||||
})
|
||||
}
|
||||
@if node.has_children() {
|
||||
@:nested_children({
|
||||
@for child in &node.children {
|
||||
@:nodes(child, &node.item, logged_in, dark)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
<div class="profile-box">
|
||||
@:link(&Link::current_tab(&profile.view_path()).dark(dark), {
|
||||
<div class="profile-box--icon">
|
||||
@if let Some(key) = profile.icon_key() {
|
||||
@:icon(key, &profile.name())
|
||||
}
|
||||
</div>
|
||||
@:tkicon(&profile.view_path(), true, dark, {
|
||||
@if let Some(key) = profile.icon_key() {
|
||||
@:icon(key, &profile.name())
|
||||
}
|
||||
})
|
||||
<div class="profile-box--content">
|
||||
<div class="profile-box--all-meta">
|
||||
<div class="profile-box--meta">
|
||||
@if let Some(name) = profile.display_name() {
|
||||
<div class="profile-box--meta--display">
|
||||
<div>
|
||||
<div class="profile-box--meta">
|
||||
@if let Some(name) = profile.display_name() {
|
||||
<div class="profile-box--meta--display">
|
||||
@:link(&Link::current_tab(&profile.view_path()).plain(true).dark(dark), {
|
||||
@name
|
||||
})
|
||||
</div>
|
||||
}
|
||||
<div class="profile-box--meta--handle">
|
||||
@:link(&Link::current_tab(&profile.view_path()).plain(true).dark(dark), {
|
||||
@name
|
||||
@profile.full_handle()
|
||||
})
|
||||
</div>
|
||||
}
|
||||
<div class="profile-box--meta--handle">
|
||||
@:link(&Link::current_tab(&profile.view_path()).plain(true).dark(dark), {
|
||||
@profile.full_handle()
|
||||
})
|
||||
<div class="profile-box--meta--date">
|
||||
posted @:ago(comment.published(), dark)
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-box--meta--date">
|
||||
posted
|
||||
@:link(&Link::current_tab("#").title(&crate::time(comment.published())).plain(true).dark(dark), {
|
||||
@crate::published_time(comment.published())
|
||||
<div class="profile-box--replying-to">
|
||||
@:link(&replying_to.link(dark), {
|
||||
Replying to @replying_to.name()
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-box--extra-meta">@:meta()</div>
|
||||
</div>
|
||||
<div class="profile-box--replying-to">
|
||||
@:link(&replying_to.link(dark), {
|
||||
Replying to @replying_to.name()
|
||||
})
|
||||
</div>
|
||||
<div class="profile-box--body">@:body()</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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({
|
||||
<div class="comment-box">
|
||||
@for child in &view.comments.children {
|
||||
@:nodes(child, &view.comments.item, view.logged_in, nav_state.dark())
|
||||
}
|
||||
</div>
|
||||
@:nested(nav_state.dark(), {
|
||||
@:nested_children({
|
||||
@for child in &view.comments.children {
|
||||
@:nodes(child, &view.comments.item, view.logged_in, nav_state.dark())
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -34,14 +34,16 @@
|
|||
</div>
|
||||
})
|
||||
<nav class="nav-links">
|
||||
@:card(&Card::full_width().classes(&["nav"]).dark(nav_state.dark()), {
|
||||
@:card_body({
|
||||
@:nav(nav_state)
|
||||
})
|
||||
@:card_body({
|
||||
@:button_group(&[
|
||||
Button::primary_outline("Close").href(nav_state.href()).dark(nav_state.dark()),
|
||||
])
|
||||
@:centered(false, {
|
||||
@:card(&Card::full_width().classes(&["nav"]).dark(nav_state.dark()), {
|
||||
@:card_body({
|
||||
@:nav(nav_state)
|
||||
})
|
||||
@:card_body({
|
||||
@:button_group(&[
|
||||
Button::primary_outline("Close").href(nav_state.href()).dark(nav_state.dark()),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
</nav>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
})
|
||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
@:card_title({ Preview })
|
||||
@:view("", profile)
|
||||
@:view(profile, nav_state.dark())
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -30,6 +30,6 @@
|
|||
})
|
||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
@:card_title({ Preview })
|
||||
@:view("", profile)
|
||||
@:view(profile, nav_state.dark())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -25,6 +25,6 @@
|
|||
})
|
||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
@:card_title({ Preview })
|
||||
@:view("", profile)
|
||||
@:view(profile, nav_state.dark())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
})
|
||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
@:card_title({ Preview })
|
||||
@:view("", profile)
|
||||
@:view(profile, nav_state.dark())
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -48,6 +48,6 @@
|
|||
})
|
||||
@:card(&Card::full_width().dark(true), {
|
||||
@:card_title({ Preview })
|
||||
@:view("", profile)
|
||||
@:view(profile, nav_state.dark())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
<script src="/toolkit/@file_input_js.name"></script>
|
||||
<script src="/toolkit/@button_js.name"></script>
|
||||
}, {
|
||||
@: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({
|
||||
<form method="POST" action="/profiles/update/bio">
|
||||
|
@ -50,9 +50,7 @@
|
|||
<div class="columns">
|
||||
<div class="columns--column">
|
||||
@if let Some(key) = state.profile.icon_key() {
|
||||
<div class="image-box">
|
||||
@:icon(key, &state.profile.name())
|
||||
</div>
|
||||
@:icon(key, &state.profile.name())
|
||||
} else {
|
||||
<p>No icon set</p>
|
||||
}
|
||||
|
@ -85,9 +83,7 @@
|
|||
<div class="columns">
|
||||
<div class="columns--column">
|
||||
@if let Some(key) = state.profile.banner_key() {
|
||||
<div class="image-box">
|
||||
@:banner(key, &state.profile.name())
|
||||
</div>
|
||||
@:banner(key, &state.profile.name())
|
||||
} else {
|
||||
<p>No banner set</p>
|
||||
}
|
||||
|
@ -144,7 +140,7 @@
|
|||
</form>
|
||||
})
|
||||
})
|
||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
@:card(Card::full_width().dark(nav_state.dark()), {
|
||||
@:card_title({ Danger })
|
||||
@:card_body({
|
||||
@:button_group(&[
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
@:home("Delete Profile", &format!("Delete {}", profile.name()), nav_state, {
|
||||
<script src="/toolkit/@button_js.name"></script>
|
||||
}, {
|
||||
@: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()), {
|
||||
<form method="POST" action="/profiles/delete">
|
||||
@:card_title({ Delete Profile })
|
||||
@:card_body({
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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({
|
||||
<form method="POST" action="/profiles/change">
|
||||
<input type="hidden" name="profile_id" value="@profile.id()" />
|
||||
|
|
|
@ -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({
|
||||
<div class="submission-tiles">
|
||||
@for submission in &pview.submissions {
|
||||
@:submission_tile(submission, pview.viewer, nav_state.dark())
|
||||
}
|
||||
</div>
|
||||
})
|
||||
@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({
|
||||
<div class="submission-tiles">
|
||||
@for submission in &pview.submissions {
|
||||
@:submission_tile(submission, pview.viewer, nav_state.dark())
|
||||
}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
})
|
||||
@if pview.nav().len() > 0 {
|
||||
@:card_body({
|
||||
@:button_group(&pview.nav())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
@if let Some(key) = submission.pictrs_key() {
|
||||
@:link(&Link::current_tab(&submission.view_path()).plain(true).dark(dark), {
|
||||
<div class="submission-icon image-box">
|
||||
<div class="submission-icon">
|
||||
@if submission.is_published() {
|
||||
<div class="submission-icon--overlay">
|
||||
<div class="submission-icon--meta">
|
||||
|
|
|
@ -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)
|
||||
|
||||
<div class="profile-view @classes">
|
||||
<div class="profile-view--banner">
|
||||
@if let Some(key) = profile.banner_key() {
|
||||
@:banner(key, &profile.name())
|
||||
} else {
|
||||
<div class="profile-view--banner--placeholder"></div>
|
||||
}
|
||||
</div>
|
||||
<div class="profile-view--content">
|
||||
<div class="profile-view--content--top">
|
||||
<div class="profile-view--icon">
|
||||
@if let Some(key) = profile.icon_key() {
|
||||
@:icon(key, &profile.name())
|
||||
}
|
||||
</div>
|
||||
<div class="profile-view--meta">
|
||||
<div class="profile-view--meta--display">
|
||||
@if let Some(name) = profile.display_name() {
|
||||
@name
|
||||
} else {
|
||||
|
||||
}
|
||||
</div>
|
||||
<div class="profile-view--meta--handle">@profile.full_handle()</div>
|
||||
</div>
|
||||
</div>
|
||||
@if let Some(description) = profile.description() {
|
||||
<div class="profile-view--description">@description</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@: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())
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
||||
<div class="profile-box">
|
||||
@:link(&Link::current_tab(&profile.view_path()).dark(dark), {
|
||||
<div class="profile-box--icon">
|
||||
@if let Some(key) = profile.icon_key() {
|
||||
@:icon(key, &profile.name())
|
||||
}
|
||||
</div>
|
||||
@:tkicon(&profile.view_path(), true, dark, {
|
||||
@if let Some(key) = profile.icon_key() {
|
||||
@:icon(key, &profile.name())
|
||||
}
|
||||
})
|
||||
<div class="profile-box--content">
|
||||
<div class="profile-box--all-meta">
|
||||
|
@ -30,9 +30,7 @@
|
|||
@if let Some(published) = submission.published() {
|
||||
<div class="profile-box--meta--date">
|
||||
posted
|
||||
@:link(&Link::current_tab("#").title(&crate::time(published)).plain(true).dark(dark), {
|
||||
@crate::published_time(published)
|
||||
})
|
||||
@:ago(published, dark)
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -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() {
|
||||
<div class="submission-box image-box">
|
||||
<div class="submission-box">
|
||||
@:image(file.key(), &view.submission.title(), view.file_num)
|
||||
</div>
|
||||
} else {
|
||||
|
@ -64,9 +65,9 @@
|
|||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||
@:card_title({ Comments })
|
||||
@:card_body({
|
||||
<div class="comment-box">
|
||||
@:nested(nav_state.dark(), {
|
||||
@:nodes(&view.comments, &view.replying_to(), view.profile.is_some(), nav_state.dark())
|
||||
</div>
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -45,9 +45,7 @@
|
|||
@:card_body({
|
||||
<div class="columns">
|
||||
<div class="columns--column">
|
||||
<div class="image-box">
|
||||
@:image(file.key(), &state.submission.title(), tup.0)
|
||||
</div>
|
||||
@:image(file.key(), &state.submission.title(), tup.0)
|
||||
</div>
|
||||
<div class="columns--column">
|
||||
<h3>Remove Image From Submission</h3>
|
||||
|
@ -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()),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue