website/web/templates/account/dashboard.html
2024-06-29 20:52:23 -07:00

846 lines
49 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>
<aside class="menu">
<ul class="menu-list">
<li>
<a href="/photo/upload?intent=profile_pic">
<div class="columns is-mobile is-gapless">
<div class="column is-narrow mr-2">
{{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-xmark has-text-danger"></i></span>
{{end}}
</div>
<div class="column">
Upload a Profile Picture to your account that shows your face
<p class="help">
Click here to upload a new profile picture
<i class="fa fa-external-link ml-1"></i>
</p>
</div>
</div>
</a>
</li>
<li>
<a href="/photo/certification">
<div class="columns is-mobile is-gapless">
<div class="column is-narrow mr-2">
{{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-xmark has-text-danger"></i></span>
{{end}}
</div>
<div class="column">
Get certified by uploading a verification selfie
<p class="help">
Click here to go to the Certification Photo upload page
<i class="fa fa-external-link ml-1"></i>
</p>
</div>
</div>
</a>
</li>
</ul>
</aside>
</div>
</div>
{{end}}
<!-- "Very Private" Restricted Account warning for certified users -->
{{if and .CurrentUser.Certified .IsShyUser}}
<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-exclamation-triangle"></i></span>
<span>Your profile page is too private</span>
</p>
</header>
<div class="card-content">
<p class="block">
You are considered to be a <strong>Shy Account</strong> because your profile and
photos are all set to Private or Friends-only visibility, so that to other members
of {{PrettyTitle}} you appear like a blank, faceless profile.
</p>
<p class="block">
While in this restricted state, you are grouped into a cohort with other members who
are as shy as you are and have limited contact options to connect with our other,
{{PrettyTitle}} members who are sharing their nudes on public.
</p>
<p class="block">
<a href="/faq#shy-faqs">Click here to learn more</a> about your Shy Account. To
remedy this, please see the following steps:
</p>
<aside class="menu">
<ul class="menu-list">
<li>
<a href="/settings">
{{if eq .CurrentUser.Visibility "public"}}
<span class="icon"><i class="fa fa-circle-check has-text-success"></i></span>
{{else}}
<span class="icon"><i class="fa fa-circle-xmark has-text-danger"></i></span>
{{end}}
<span>
Have your profile visibility set to <strong>Public</strong>.
{{if not (eq .CurrentUser.Visibility "public")}}
<span class="icon"><i class="fa fa-external-link"></i></span>
{{end}}
</span>
</a>
</li>
<li>
<a href="/u/{{.CurrentUser.Username}}/photos">
{{if .HasPublicPhoto}}
<span class="icon"><i class="fa fa-circle-check has-text-success"></i></span>
{{else}}
<span class="icon"><i class="fa fa-circle-xmark has-text-danger"></i></span>
{{end}}
<span>
Have at least one <strong>Public</strong> photo on your profile.
{{if not .HasPublicPhoto}}
<span class="icon"><i class="fa fa-external-link"></i></span>
{{end}}
</span>
</a>
</li>
</ul>
</aside>
</div>
</div>
{{end}}
<!-- New Feature -->
{{if not (.CurrentUser.GetProfileField "hero-color-start")}}
<div class="card block">
<header class="card-header has-background-success">
<p class="card-header-title has-text-light">
<i class="fa fa-gift mr-2"></i>
New Feature: Profile Look &amp; Feel
</p>
</header>
<div class="card-content">
<p class="block">
A new feature is now available:
<a href="/settings#look">customize your profile page look &amp; feel!</a>
</p>
<p class="block">
You can now personalize your profile page with a custom color scheme for
your header and profile cards. In the future, it will also be possible to
set a custom wallpaper background image and stylize your profile page
even further!
</p>
<p class="block">
Check it out on your Settings &gt; <a href="/settings#look">Look &amp; Feel</a>
page!
</p>
</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">
<aside class="menu">
<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="/u/{{.CurrentUser.Username}}/photos">
<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>
{{if .CurrentUser.IsInnerCircle}}
<li>
<a href="/inner-circle">
<span class="icon"><img src="/static/img/circle-16.png"></span>
{{PrettyCircle}}
</a>
</li>
{{end}}
<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="/notes/me">
<span class="icon"><i class="fa fa-pen-to-square mr-1"></i></span>
My User Notes
</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="/settings#deactivate">
<span class="icon"><i class="fa fa-trash"></i></span>
Delete account
</a>
</li>
</ul>
</aside>
</div>
</div>
</div>
{{$Root := .}}
<div class="column">
<div class="card" id="notifications">
<header class="card-header has-background-warning">
<p class="card-header-title">Notifications</p>
</header>
<div class="card-content">
<!-- Notifications header row: tablets on upwards -->
<div class="is-hidden-mobile">
<form method="POST" action="{{.Request.URL.Path}}">
{{InputCSRF}}
<div class="columns mb-2">
<div class="column">
{{if gt .NavUnreadNotifications 0}}
{{.NavUnreadNotifications}} unread notification{{Pluralize64 .NavUnreadNotifications}}.
{{else}}
No unread notifications.
{{end}}
</div>
<div class="column is-narrow has-text-right">
<button type="submit" name="intent" value="clear-all"
class="button is-danger is-light is-small"
onclick="return window.confirm('Are you sure you want to REMOVE all notifications?')"
title="Remove all notifications from your feed">
<i class="fa fa-xmark mr-1"></i>
Clear all
</button>
<button type="submit" name="intent" value="read-notifications"
class="button is-link is-light is-small">
<i class="fa fa-check mr-1"></i>
Mark all as read
</button>
</div>
</div>
</form>
</div>
<!-- Notifications header: for mobiles only version -->
<div class="is-hidden-tablet">
<p>
{{if gt .NavUnreadNotifications 0}}
{{.NavUnreadNotifications}} unread notification{{Pluralize64 .NavUnreadNotifications}}.
{{else}}
No unread notifications.
{{end}}
</p>
<form method="POST" action="{{.Request.URL.Path}}">
{{InputCSRF}}
<div class="my-2 has-text-right">
<button type="submit" name="intent" value="clear-all"
class="button is-danger is-light is-small"
onclick="return window.confirm('Are you sure you want to REMOVE all notifications?')">
<i class="fa fa-xmark mr-1"></i>
Clear all
</button>
<button type="submit" name="intent" value="read-notifications"
class="button is-link is-light is-small">
<i class="fa fa-check mr-1"></i>
Mark all as read
</button>
</div>
</form>
<hr>
</div>
<!-- Filters -->
<div class="block">
<form action="{{.Request.URL.Path}}" method="GET">
<div class="card nonshy-collapsible-mobile nonshy-collapsible-always mb-5">
<header class="card-header has-background-link-light">
<p class="card-header-title has-text-dark">
<i class="fa fa-list mr-2"></i> Notification Types
</p>
<button class="card-header-icon" type="button">
<span class="icon">
<i class="fa fa-angle-up"></i>
</span>
</button>
</header>
<div class="card-content">
<p class="block">
<a href="/settings#notifications">
<i class="fa fa-gear mr-1"></i>
Manage notification settings
</a>
</p>
<div class="columns is-multiline mb-0">
<div class="column is-half">
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="likes"
value="true"
{{if .Filters.Likes}}checked{{end}}
>
Likes
<p class="help">
on your photos, profile or comments
</p>
</label>
</div>
</div>
<div class="column is-half">
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="comments"
value="true"
{{if .Filters.Comments}}checked{{end}}
>
Comments
<p class="help">
on your photos
</p>
</label>
</div>
</div>
<div class="column is-half">
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="photos"
value="true"
{{if .Filters.NewPhotos}}checked{{end}}
>
New Photos
<p class="help">
of your friends
</p>
</label>
</div>
</div>
<div class="column is-half">
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="replies"
value="true"
{{if .Filters.AlsoCommented}}checked{{end}}
>
Replies
<p class="help">
on comment threads you follow
</p>
</label>
</div>
</div>
<div class="column is-half">
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="private"
value="true"
{{if .Filters.PrivatePhoto}}checked{{end}}
>
Private photos
<p class="help">
unlock notifications
</p>
</label>
</div>
</div>
<div class="column is-half">
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="misc"
value="true"
{{if .Filters.Misc}}checked{{end}}
>
Miscellaneous
<p class="help">
new friends, certification photos, etc.
</p>
</label>
</div>
</div>
</div>
<div class="block has-text-centered">
<a href="{{.Request.URL.Path}}" class="button">
Reset
</a>
<button type="submit" class="button is-success">
Apply Filters
</button>
</div>
</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 "new_photo"}}
<span class="icon">
{{if and $Body.Photo (eq $Body.Photo.Visibility "private")}}
<i class="fa fa-eye {{if $Body.Photo.Explicit}}has-text-danger{{else}}has-text-private{{end}}"></i>
{{else if and $Body.Photo (eq $Body.Photo.Visibility "circle")}}
<img src="/static/img/circle-16.png">
{{else}}
<i class="fa fa-image {{if $Body.Photo.Explicit}}has-text-danger{{else}}has-text-link{{end}}"></i>
{{end}}
</span>
<span>
<a href="/u/{{.AboutUser.Username}}"><strong>{{.AboutUser.Username}}</strong></a>
has uploaded a new
{{if and $Body.Photo (eq $Body.Photo.Visibility "private")}}
<span class="has-text-private">private photo!</span>
{{else}}
photo!
{{end}}
</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_secondary"}}
<span class="icon"><i class="fa fa-certificate has-text-warning"></i></span>
<span>
About your <strong>certification photo:</strong>
</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 if eq .Type "inner_circle"}}
<span class="icon"><img src="/static/img/circle-16.png"></span>
<span>
You have been added to the {{PrettyCircle}} of nonshy.
</span>
<div class="block content mt-2">
<a href="/inner-circle">Click to learn more</a> about the inner circle.
</div>
{{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}}
<!-- Admin action: mark this pic explicit? -->
{{if and ($Root.CurrentUser.IsAdmin) (not $Body.Photo.Explicit)}}
<div class="mt-2">
<a href="/admin/photo/mark-explicit?photo_id={{$Body.Photo.ID}}&next={{$Root.Request.URL}}"
class="has-text-danger is-size-7"
onclick="return confirm('Do you want to mark this photo as Explicit?')">
<i class="fa fa-peace mr-1"></i>
Mark photo as Explicit
</a>
</div>
{{end}}
</div>
{{end}}
<hr class="has-background-light mb-1">
<small title="{{.CreatedAt.Format "2006-01-02 15:04:05"}}">
{{SincePrettyCoarse .CreatedAt}} ago.
<!-- Delete button for just this notification -->
<button type="button"
class="button is-danger is-light is-small nonshy-notif-delete-button"
data-notification-id="{{.ID}}"
title="Remove this notification from your feed">
<i class="fa fa-xmark mr-1"></i> Clear
</button>
</small>
</div>
<!-- Attached photo? -->
{{if $Body.PhotoID}}
<div class="column is-one-quarter is-clipped">
<!-- GIF video? -->
{{if HasSuffix $Body.Photo.Filename ".mp4"}}
<video loop controls controlsList="nodownload" playsinline
{{if $Body.Photo.AltText}}title="{{$Body.Photo.AltText}}"{{end}}
{{if BlurExplicit $Body.Photo}}class="blurred-explicit"
{{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay
{{end}}>
<source src="{{PhotoURL $Body.Photo.Filename}}" type="video/mp4">
</video>
<div>
<a href="/photo/view?id={{$Body.Photo.ID}}" class="is-size-7">
<i class="fa fa-arrow-right"></i> View GIF
</a>
</div>
{{else}}
<a href="/photo/view?id={{$Body.Photo.ID}}">
<img src="{{PhotoURL $Body.Photo.Filename}}" loading="lazy"
{{if BlurExplicit $Body.Photo}} class="blurred-explicit"{{end}}
{{if $Body.Photo.AltText}}title="{{$Body.Photo.AltText}}" alt="{{$Body.Photo.AltText}}"{{end}}>
</a>
{{end}}
{{if $Body.Photo.Caption}}
<small>{{$Body.Photo.Caption}}</small>
{{else}}
<small><em>No caption.</em></small>
{{end}}
<!-- Like button for the photo right here -->
{{if ne $Body.Photo.UserID $Root.CurrentUser.ID}}
<div class="mt-1">
{{$Like := $Root.PhotoLikeMap.Get $Body.PhotoID}}
<button type="button" class="button is-small nonshy-like-button"
data-table-name="photos" data-table-id="{{$Body.PhotoID}}"
title="Like">
<span class="icon{{if $Like.UserLikes}} has-text-danger{{end}}"><i class="fa fa-heart"></i></span>
<span class="nonshy-likes">
Like
{{if gt $Like.Count 0}}
({{$Like.Count}})
{{end}}
</span>
</button>
</div>
{{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;
// For delete buttons: if they click thru the first confirm, do not ask every single time
// for the rest of the current page load.
let dontAskAgain = false;
// Bind to the notification table rows.
(document.querySelectorAll(".nonshy-notification-row") || []).forEach(node => {
let $newBadge = node.querySelector(".nonshy-notification-new"),
$deleteButton = node.querySelector(".nonshy-notif-delete-button"),
ID = node.dataset.notificationId;
// Delete buttons for individual notifications.
$deleteButton.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
if (!dontAskAgain) {
if (!window.confirm(
"Do you want to DELETE this notification?\n\nNote: If you click Ok, you will not be asked "+
"the next time you want to delete another notification until your next page reload."
)) {
return;
}
dontAskAgain = true;
}
busy = true;
return fetch("/v1/notifications/delete", {
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);
// Hide the notification row immediately.
node.style.display = 'none';
}).catch(resp => {
window.alert(resp);
}).finally(() => {
busy = false;
});
});
// 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}}