website/web/templates/account/dashboard.html
Noah 8085e092bc Forum Reply Enhancements + Better Pagers
* Enhance user experience replying to a forum thread. An inline reply textarea
  is added to page footers, "Quote" buttons on posts will quote the markdown
  source and focus the reply textarea, and "Reply" buttons will put an
  "@ mention" and focus the reply textarea. Users with scripts disabled will
  still be sent to the regular reply page as before.
* Improve all pagers by adding a "QueryPlus" template function that merges the
  page number with other current query parameters.
* Fix private profile picture avatars not displaying in your Notifications for
  profile pics you're allowed to see.
2022-09-10 12:09:46 -07:00

407 lines
23 KiB
HTML

{{define "title"}}My Dashboard{{end}}
{{define "content"}}
<div class="container">
<section class="hero is-info is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">User Dashboard</h1>
<h2 class="subtitle">to your account</h2>
</div>
</div>
</section>
<div class="block p-4">
<div class="columns">
<div class="column">
<!-- Onboarding Checklist -->
{{if or (not .CurrentUser.Certified) (not .CurrentUser.ProfilePhoto.ID)}}
<div class="card block">
<header class="card-header has-background-danger">
<p class="card-header-title has-text-light">
<span class="icon"><i class="fa fa-check"></i></span>
<span>Onboarding Checklist</span>
</p>
</header>
<div class="card-content">
<p class="block">
You're almost there! Please review the following checklist items to gain
full access to this website. Members are expected to have a face picture
as their default Profile Pic and upload a Verification Photo to become
certified as being a real person.
</p>
<ul class="menu-list block">
<li>
<a href="/photo/upload?intent=profile_pic">
{{if .CurrentUser.ProfilePhoto.ID}}
<span class="icon"><i class="fa fa-circle-check has-text-success"></i></span>
{{else}}
<span class="icon"><i class="fa fa-circle has-text-danger"></i></span>
{{end}}
<span>
Add a Profile Picture
{{if not .CurrentUser.ProfilePhoto.ID}}
<span class="icon"><i class="fa fa-external-link"></i></span>
{{end}}
</span>
</a>
</li>
<li>
<a href="/photo/certification">
{{if .CurrentUser.Certified}}
<span class="icon"><i class="fa fa-circle-check has-text-success"></i></span>
{{else}}
<span class="icon"><i class="fa fa-circle has-text-danger"></i></span>
{{end}}
<span>
Get certified by uploading a verification selfie
{{if not .CurrentUser.Certified}}
<span class="icon"><i class="fa fa-external-link"></i></span>
{{end}}
</span>
</a>
</li>
</ul>
</div>
</div>
{{end}}
<div class="card block">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">My Account</p>
</header>
<div class="card-content">
<ul class="menu-list">
<li>
<a href="/u/{{.CurrentUser.Username}}">
<span class="icon"><i class="fa fa-user"></i></span>
My Profile
</a>
</li>
<li>
<a href="/photo/u/{{.CurrentUser.Username}}">
<span class="icon"><i class="fa fa-image"></i></span>
My Photos
</a>
</li>
<li>
<a href="/photo/upload">
<span class="icon"><i class="fa fa-upload"></i></span>
Upload Photos
</a>
</li>
<li>
<a href="/photo/private">
<span class="icon"><i class="fa fa-eye"></i></span>
Manage Private Photos
<span class="tag is-success ml-1">NEW!</span>
</a>
</li>
<li>
<a href="/settings">
<span class="icon"><i class="fa fa-edit"></i></span>
Edit Profile &amp; Settings
</a>
</li>
<li>
<a href="/photo/certification">
<span class="icon"><i class="fa fa-certificate"></i></span>
Certification Photo
</a>
</li>
<li>
<a href="/users/blocked">
<span class="icon"><i class="fa fa-hand"></i></span>
Blocked Users
</a>
</li>
<li>
<a href="/logout">
<span class="icon"><i class="fa fa-arrow-right-from-bracket"></i></span>
Log out
</a>
</li>
{{if .SessionImpersonated}}
<li>
<a href="/admin/unimpersonate" class="has-text-danger">
<span class="icon"><i class="fa fa-ghost"></i></span>
<span>Unimpersonate</span>
</a>
</li>
{{end}}
<li>
<a href="/account/delete">
<span class="icon"><i class="fa fa-trash"></i></span>
Delete account
</a>
</li>
</ul>
</div>
</div>
</div>
{{$Root := .}}
<div class="column">
<div class="card" id="notifications">
<header class="card-header has-background-warning">
<p class="card-header-title has-text-dark-dark">Notifications</p>
</header>
<div class="card-content">
<div class="columns">
<div class="column">
{{if gt .NavUnreadNotifications 0}}
{{.NavUnreadNotifications}} unread notification{{Pluralize64 .NavUnreadNotifications}}.
{{else}}
No unread notifications.
{{end}}
</div>
<div class="column is-narrow">
<a href="/me?intent=read-notifications" class="button is-link is-light is-small">
<span class="icon-text">
<span class="icon"><i class="fa fa-check"></i></span>
<span>Mark all as read</span>
</span>
</a>
</div>
</div>
<table class="table is-striped is-fullwidth is-hoverable">
<tbody>
{{range .Notifications}}
{{$Body := $Root.NotifMap.Get .ID}}
<tr>
<td class="nonshy-notification-row" data-notification-id="{{.ID}}">
<div class="columns">
<div class="column is-narrow has-text-centered">
{{if not .Read}}
<div class="mb-2 nonshy-notification-new">
<strong class="tag is-success">NEW!</strong>
</div>
{{end}}
<a href="/u/{{.AboutUser.Username}}">
{{template "avatar-48x48" .AboutUser}}
</a>
</div>
<div class="column">
<div class="mb-1">
{{if eq .Type "like"}}
<span class="icon"><i class="fa fa-heart has-text-danger"></i></span>
<span>
<a href="/u/{{.AboutUser.Username}}"><strong>{{.AboutUser.Username}}</strong></a>
liked your
{{if eq .TableName "photos"}}
{{if $Body.Photo}}
<a href="/photo/view?id={{$Body.Photo.ID}}">photo</a>.
{{else}}
photo.
{{end}}
{{else if eq .TableName "users"}}
profile page.
{{else if eq .TableName "comments"}}
{{if .Link}}
<a href="{{.Link}}">comment</a>:
{{else}}
comment.
{{end}}
{{else}}
{{.TableName}}.
{{end}}
</span>
{{else if eq .Type "comment"}}
<span class="icon"><i class="fa fa-comment has-text-success"></i></span>
<span>
<a href="/u/{{.AboutUser.Username}}"><strong>{{.AboutUser.Username}}</strong></a>
commented on your
<a href="{{.Link}}">
{{if eq .TableName "photos"}}
photo:
{{else}}
{{.TableName}}:
{{end}}
</a>
</span>
{{else if eq .Type "also_comment"}}
<span class="icon"><i class="fa fa-comment has-text-success"></i></span>
<span>
<a href="/u/{{.AboutUser.Username}}"><strong>{{.AboutUser.Username}}</strong></a>
also commented on a
<a href="{{.Link}}">
{{if eq .TableName "photos"}}
photo
{{else}}
{{.TableName}}
{{end}}
</a>
that you replied to:
</span>
{{else if eq .Type "also_posted"}}
<span class="icon"><i class="fa fa-comments has-text-success"></i></span>
<span>
<a href="/u/{{.AboutUser.Username}}"><strong>{{.AboutUser.Username}}</strong></a>
replied to <a href="{{.Link}}">a forum thread</a> that you follow:
</span>
{{else if eq .Type "friendship_approved"}}
<span class="icon"><i class="fa fa-user-group has-text-success"></i></span>
<span>
<a href="/u/{{.AboutUser.Username}}"><strong>{{.AboutUser.Username}}</strong></a>
accepted your friend request!
</span>
{{else if eq .Type "private_photo"}}
<span class="icon"><i class="fa fa-unlock has-text-private"></i></span>
<span>
<a href="/u/{{.AboutUser.Username}}"><strong>{{.AboutUser.Username}}</strong></a>
has granted you access to see their
<a href="{{.Link}}" class="has-text-private">private photos</a>!
</span>
{{else if eq .Type "cert_approved"}}
<span class="icon"><i class="fa fa-certificate has-text-success"></i></span>
<span>
Your <strong>certification photo</strong> was approved!
</span>
{{else if eq .Type "cert_rejected"}}
<span class="icon"><i class="fa fa-certificate has-text-danger"></i></span>
<span>
Your <strong>certification photo</strong> was rejected!
</span>
{{else}}
{{.AboutUser.Username}} {{.Type}} {{.TableName}} {{.TableID}}
{{end}}
</div>
<!-- Attached message? -->
{{if .Message}}
<div class="block content mb-1">
<blockquote class="p-2 pl-4">{{ToMarkdown (TrimEllipses .Message 256)}}</blockquote>
</div>
{{end}}
<!-- Attached forum thread? -->
{{if $Body.Thread}}
<div>
On thread: <a href="{{.Link}}">{{$Body.Thread.Title}}</a>
</div>
{{end}}
<!-- Photo caption? -->
{{if $Body.Photo}}
<div class="block">
<!-- If it's a comment, have a link to view it -->
{{if eq .Type "comment"}}
<div class="is-size-7 pt-1">
<span class="icon"><i class="fa fa-arrow-right"></i></span>
<a href="{{.Link}}">See all comments</a>
</div>
{{else}}
<em>{{or $Body.Photo.Caption "No caption."}}</em>
{{end}}
</div>
{{end}}
<hr class="has-background-light mb-1">
<small title="{{.CreatedAt.Format "2006-01-02 15:04:05"}}">
{{SincePrettyCoarse .CreatedAt}} ago
</small>
</div>
<!-- Attached photo? -->
{{if $Body.PhotoID}}
<div class="column is-one-quarter">
<a href="/photo/view?id={{$Body.Photo.ID}}">
<img src="{{PhotoURL $Body.Photo.Filename}}">
</a>
{{if $Body.Photo.Caption}}
<small>{{$Body.Photo.Caption}}</small>
{{else}}
<small><em>No caption.</em></small>
{{end}}
</div>
{{end}}
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{if .Pager.HasNext}}
<div class="has-text-centered">
<a href="{{.Request.URL.Path}}?{{QueryPlus "page" .Pager.Next}}" class="button">View older notifications</a>
</div>
{{end}}
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
// Notifications helper.
document.addEventListener('DOMContentLoaded', () => {
let busy = false;
// Bind to the notification table rows.
(document.querySelectorAll(".nonshy-notification-row") || []).forEach(node => {
let $newBadge = node.querySelector(".nonshy-notification-new"),
ID = node.dataset.notificationId;
// If the notification doesn't have a "NEW!" badge, no action needed.
if ($newBadge === null) return;
// Collect any hyperlinks in this row.
let links = Array.from(node.querySelectorAll("a"));
links.push(node);
// Apply a "click" handler to the notification row as a whole, and to all of the hyperlinks in it.
// For the hyperlinks: prevent the browser following the link UNTIL the successful ajax request to
// mark the notification "read" has run.
links.forEach(link => {
link.addEventListener("click", (e) => {
if (busy) return;
// In case it's a hyperlink, grab the href.
let href = link.attributes.href;
if (href !== undefined) {
e.preventDefault();
href = href.textContent;
}
$newBadge.style.display = "none";
busy = true;
return fetch("/v1/notifications/read", {
method: "POST",
mode: "same-origin",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"id": parseInt(ID),
}),
})
.then((response) => response.json())
.then((data) => {
console.log(data);
}).catch(resp => {
window.alert(resp);
}).finally(() => {
busy = false;
if (href !== undefined) {
window.location.href = href;
}
});
})
})
});
});
</script>
{{end}}