{{define "title"}}My Dashboard{{end}} {{define "content"}} <div class="container"> <section class="hero is-link 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-user-tie 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}}