website/web/templates/account/dashboard.html
Noah 47f898561c Forum Photo Attachments
* Add support to upload a picture to forum posts and replies, in forums that
  have the PermitPhotos setting enabled.
* New DB table: CommentPhoto holds the association between a photo and a
  forum ID. Photos can be uploaded at preview time (before a CommentID is
  available) and get associated to the CommentID on save.
* Cron endpoint /v1/comment-photos/remove-orphaned can clean up orphaned
  photos without a CommentID older than 24 hours.
* Add "Photo Boards" as a default forum category for new boards.
2022-10-20 21:02:30 -07:00

406 lines
22 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
</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}}