ed4a9f8c89
* The "Newest" tab of the forum is updated with new filter options. * Which forums: All, Official, Community, My List * Show: By threads, All posts * The option for "Which forums" is saved in the user's preferences and set as their default on future visits, similar to the Site Gallery "Whose photos" option. * So users can subscribe to their favorite forums and always get their latest posts easily while filtering out the rest. * Forum Moderators * Add the ability to add and remove moderators for your forum. * Users are notified when they are added as a moderator. * Moderators can opt themselves out by unfollowing the forum. * ForumMembership: add unique constraint on user_id,forum_id.
873 lines
52 KiB
HTML
873 lines
52 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 & 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 & 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 > <a href="/settings#look">Look & 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 & 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="/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}}">
|
|
<!-- Delete button for just this notification -->
|
|
<button type="button"
|
|
class="button is-danger px-2 py-1 is-small nonshy-notif-delete-button"
|
|
data-notification-id="{{.ID}}"
|
|
title="Remove this notification from your feed">
|
|
<i class="fa fa-xmark mr"></i>
|
|
</button>
|
|
|
|
<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 pr-4">
|
|
{{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}}
|
|
<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 "forum_moderator"}}
|
|
<span class="icon"><i class="fa fa-peace has-text-success"></i></span>
|
|
<span>
|
|
You have been appointed as a <strong class="has-text-success">moderator</strong>
|
|
for the forum <a href="/f/{{$Body.Forum.Fragment}}">{{$Body.Forum.Title}}</a>!
|
|
</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}}
|
|
|
|
<!-- 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.
|
|
|
|
<!-- Contextual "Unsubscribe" buttons -->
|
|
{{if or (eq .Type "also_posted") (eq .Type "also_comment")}}
|
|
<small>
|
|
<a href="#"
|
|
data-link="/comments/subscription?table_name={{.TableName}}&table_id={{.TableID}}&next={{UrlEncode $Root.Request.URL.String}}&subscribe=false"
|
|
data-confirm="Do you want to TURN OFF notifications about this comment thread?"
|
|
class="has-text-warning is-small nonshy-mute-notification-link"
|
|
title="Turn off notifications about this thread">
|
|
<i class="fa fa-microphone-slash mr-1"></i> Mute this thread
|
|
</a>
|
|
</small>
|
|
{{else if eq .Type "new_photo"}}
|
|
<small>
|
|
<a href="#"
|
|
data-link="/comments/subscription?table_name=friend.photos&table_id={{.AboutUser.Username}}&next={{UrlEncode $Root.Request.URL.String}}&subscribe=false"
|
|
data-confirm="Do you want to TURN OFF notifications about @{{.AboutUser.Username}}'s new photo uploads?\n\nNote: to re-subscribe to their new photo notifications, see the link at the top of @{{.AboutUser.Username}}'s Photo Gallery page."
|
|
class="has-text-warning is-small nonshy-mute-notification-link"
|
|
title="Turn off notifications about @{{.AboutUser.Username}}'s new photo uploads">
|
|
<i class="fa fa-microphone-slash mr-1"></i> Mute these notifications
|
|
</a>
|
|
</small>
|
|
{{end}}
|
|
</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 && href !== "#") {
|
|
window.location.href = href;
|
|
}
|
|
});
|
|
})
|
|
});
|
|
});
|
|
|
|
// "Mute notification" links.
|
|
// NOTE: to avoid conflict with the "mark notification as read, then follow href" code
|
|
// above, the mute buttons href is made safer and this function will get its follow-thru
|
|
// URL from a data attribute.
|
|
(document.querySelectorAll(".nonshy-mute-notification-link") || []).forEach(node => {
|
|
node.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
const link = node.dataset.link,
|
|
prompt = node.dataset.confirm.replace(/\\n/g, "\n");
|
|
if (!window.confirm(prompt)) return;
|
|
|
|
window.location = link;
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{{end}}
|