DM Privacy + Settings Page Tabs

* Refactor the Settings page into a tabbed UI to reduce confusion with
  all the different forms and save buttons
* Add a DM Privacy setting to your page
* Update the About page
This commit is contained in:
Noah Petherbridge 2023-06-23 22:18:09 -07:00
parent 0f6b627156
commit 45cb4d260e
8 changed files with 701 additions and 352 deletions

View File

@ -64,6 +64,9 @@ var (
"interests",
"music_movies",
"hide_age",
// Site prefs
"dm_privacy",
}
// Choices for the Contact Us subject

View File

@ -46,12 +46,16 @@ func Settings() http.HandlerFunc {
return
}
// URL hashtag to redirect to
var hashtag string
// Are we POSTing?
if r.Method == http.MethodPost {
intent := r.PostFormValue("intent")
switch intent {
case "profile":
// Setting profile values.
hashtag = "#profile"
var (
displayName = r.PostFormValue("display_name")
dob = r.PostFormValue("dob")
@ -95,6 +99,7 @@ func Settings() http.HandlerFunc {
session.Flash(w, r, "Profile settings updated!")
case "preferences":
hashtag = "#prefs"
var (
explicit = r.PostFormValue("explicit") == "true"
visibility = models.UserVisibility(r.PostFormValue("visibility"))
@ -109,12 +114,16 @@ func Settings() http.HandlerFunc {
}
}
// Set profile field prefs.
user.SetProfileField("dm_privacy", r.PostFormValue("dm_privacy"))
if err := user.Save(); err != nil {
session.FlashError(w, r, "Failed to save user to database: %s", err)
}
session.Flash(w, r, "Website preferences updated!")
case "settings":
hashtag = "#account"
var (
oldPassword = r.PostFormValue("old_password")
changeEmail = strings.TrimSpace(strings.ToLower(r.PostFormValue("change_email")))
@ -197,7 +206,7 @@ func Settings() http.HandlerFunc {
session.FlashError(w, r, "Unknown POST intent value. Please try again.")
}
templates.Redirect(w, r.URL.Path)
templates.Redirect(w, r.URL.Path+hashtag+".")
return
}

View File

@ -72,9 +72,10 @@ func Compose() http.HandlerFunc {
// On GET request (come from a user profile page):
// Do not allow a shy user to initiate DMs with a non-shy one.
var (
imShy = currentUser.IsShy()
theyreShy = user.IsShy()
isShyFrom = currentUser.IsShyFrom(user) || (imShy && !models.AreFriends(currentUser.ID, user.ID))
imShy = currentUser.IsShy()
theyreShy = user.IsShy()
areFriends = models.AreFriends(currentUser.ID, user.ID)
isShyFrom = currentUser.IsShyFrom(user) || (imShy && !areFriends)
)
if imShy && isShyFrom && !theyreShy && !user.IsAdmin {
session.FlashError(w, r, "You have a Shy Account and can not initiate Direct Messages with a non-shy member.")
@ -82,6 +83,22 @@ func Compose() http.HandlerFunc {
return
}
// Does the recipient have a privacy control on their DMs?
switch user.GetProfileField("dm_privacy") {
case "friends":
if !areFriends && !currentUser.IsAdmin {
session.FlashError(w, r, "This user only wants to receive new DMs from their friends.")
templates.Redirect(w, "/u/"+user.Username)
return
}
case "nobody":
if !currentUser.IsAdmin {
session.FlashError(w, r, "This user's DMs are closed and they do not want any new conversations.")
templates.Redirect(w, "/u/"+user.Username)
return
}
}
var vars = map[string]interface{}{
"User": user,
}

View File

@ -389,6 +389,7 @@ func (u *User) SetProfileField(name, value string) {
return
}
log.Debug("User(%s): append ProfileField %s", u.Username, name)
u.ProfileField = append(u.ProfileField, ProfileField{
Name: name,
Value: value,

View File

@ -80,6 +80,155 @@
</li>
</ul>
<h2>What features does this site have?</h2>
<p>
We have so far:
</p>
<ul>
<li>
A <a href="#webcam-chat-room"><strong>Webcam Chat Room</strong></a> where you can chat, share images,
and go on camera with other website members. 'Explicit' cameras are allowed, just mark
your camera feed as such so other members might know what they're getting into!
Read more about the <a href="#webcam-chat-room">chat room</a>, below.
</li>
<li>
<strong>Forums</strong> where you can meet and converse with members of the
site around a variety of themes and topics.
</li>
<li>
A <strong>Site Photo Gallery</strong> where members may opt-in their profile gallery
photos to be seen by the whole site (or at least to their friends when they look at
the Site Gallery).
</li>
<li>
<strong>Friend Requests</strong> so you can make some new connections through this site.
You can also tag some of your photos as "Friends only" and make sure only your approved
friends can see those!
</li>
<li>
<strong>Profile pages &amp; Photo Galleries</strong>: members may upload up to 100 photos
on their page and fill out a profile with the usual details.
</li>
<li>
A <strong>Member Directory</strong> to browse and search for people on the site.
</li>
</ul>
<p>
We also have some <strong>privacy controls</strong> that members on any stage of their
nudist journey may enjoy to have some control over what they share and with whom:
</p>
<ul>
<li>
By default, <strong>only logged-in members</strong> can see your profile page or
photos at all. (You may opt to have a "slightly public" profile page that you could
link to from your Twitter, which reveals little information beyond your username
and profile pic).
</li>
<li>
When you upload a photo you may mark it as for <strong>"Friends only"</strong> or <strong>"Private."</strong> This way
you can limit the audience of certain pictures to only your approved friendships, or
in the case of Private photos, to specific members to whom you grant access. You may
also revoke your private photos from one or all members at once.
</li>
<li>
If you are concerned about unsolicited messages by randoms, you may limit <strong>who is allowed
to slide into your DMs</strong> to "Friends only" or even to "Nobody." People may always reply to
messages that you send them first, but if they haven't contacted you before, you can prevent
them from doing so. "Nobody" is like having your DMs closed but you can still continue conversations
you had open already.
</li>
<li>
You may mark your whole profile page as "private" and then only your Friends may see its
contents. But, this will make you a "shy account" and see the next point.
</li>
<li>
Members who are <em>very</em> shy (they're Certified so the admin has seen their face,
but they keep all their photos or profile on Private so they look to others like a 'blank
profile') are considered by the site to be <a href="/faq#shy-faqs">"Shy Accounts"</a> and
they can <em>not</em> slide into your DMs if you are sharing photos on public but they are
not. They also won't see <em>any</em> of your photos (unless you are friends). See the
<a href="/faq#shy-faqs">FAQs</a> for more details.
</li>
</ul>
<h4 id="webcam-chat-room">A Webcam Chat Room</h4>
<p>
The chat room is a completely custom-built app for {{PrettyTitle}} (and it's
<a href="#open-source">open source</a> too!)
and here are some of its features in detail:
</p>
<ul>
<li>
It's a classic chat room featuring multiple public channels, Private Messages/DMs,
a list of online chatters and links to see their profile or open their webcam
if they're sharing.
</li>
<li>
You may broadcast your webcam and microphone, and other users in the
room may tune in and watch your camera.
</li>
<li>
You are permitted to get "sexual" on camera if you want. Just mark your camera
as <span class="has-text-danger">'Explicit'</span> so other chatters may know what
they're getting into before they click in.
</li>
<li>
You could have "infinite" cameras open with other chatters: as long as your
screen size and network can support it (there is no enforced limit, as it makes
no difference to my chat server how many cams you open -- the connections are
usually peer-to-peer!)
</li>
<li>
You can see who all is watching your camera, and you can "boot" somebody
off if you don't want them to watch. When booted, they will not be able to
watch again, and your camera will appear 'offline' to them so they can't
be sure you didn't simply turn your camera off.
</li>
<li>
You can 'mute' people if you no longer want to see their messages in chat
or receive any further DMs from them (and this also prevents them from
seeing your camera - like a 'boot' they won't see if your camera is even
broadcasting!)
</li>
<li>
You can upload photos (and GIFs up to 8 MB!) to share with the chat room
or over Direct Messages.
</li>
<li>
All chatters are guaranteed to be <a href="/faq#certification-faqs">"Certified"</a>
real human beings and have at least one 'public' photo on their page that you
could see. Certification is <strong>required</strong> for <em>all</em> site
features (see <a href="/faq#uncertified">what non-certified members can even do</a>)
but additionally the "certified, <a href="/faq#shy-faqs">but shy</a>" members are
not allowed in the chat room <strong>at all.</strong>
</li>
</ul>
<p>
A unique feature of the chat room are the <strong>mutual webcam options</strong>.
Some nudists feel weirded out if they are on camera and they are being watched
by some silent lurker who isn't sharing their own camera in return. On the
{{PrettyTitle}} chat room, you can opt-in to "mutual webcam" options if you like:
</p>
<ul>
<li>
You can require your webcam viewers to first be sharing <em>their own</em>
webcam in return, before they can open yours.
</li>
<li>
You may also automatically open the viewer's webcam (if they are broadcasting)
when they click in to see your camera. This can save you a click of needing to
open the camera of the person who just opened yours.
</li>
</ul>
<h2>Who is this site for?</h2>
<p>
@ -128,15 +277,17 @@
</div>
</div>
<div class="content block pt-1">
<div class="content block pt-1" id="open-source">
<h3><i class="fa fa-code"></i> This website is open source!</h3>
<p>
If you would like to see the source code or contribute bug fixes or new features, the
source code behind this website is available at
<a href="https://code.nonshy.com/nonshy/website" target="_blank">code.nonshy.com</a>.
The chat room is an independent open source app that you may use with <em>your</em>
website too and it's called <a href="https://git.kirsle.net/apps/BareRTC">BareRTC</a>.
</p>
</div>
</div>
{{end}}
{{end}}

View File

@ -155,16 +155,23 @@
</form>
</div>
<div class="column is-narrow has-text-centered">
<a href="/messages/compose?to={{.User.Username}}" class="button is-fullwidth">
<span class="icon-text">
<span class="icon">
<i class="fa fa-message"></i>
<!-- DM button -->
{{if and (eq (.User.GetProfileField "dm_privacy") "friends") (not (eq .IsFriend "approved")) (not .CurrentUser.IsAdmin)}}
<!-- Only friends can send them a DM -->
{{else if and (eq (.User.GetProfileField "dm_privacy") "nobody") (not .CurrentUser.IsAdmin)}}
<!-- They set "Nobody" can send them a DM -->
{{else}}
<div class="column is-narrow has-text-centered">
<a href="/messages/compose?to={{.User.Username}}" class="button is-fullwidth">
<span class="icon-text">
<span class="icon">
<i class="fa fa-message"></i>
</span>
<span>Message</span>
</span>
<span>Message</span>
</span>
</a>
</div>
</a>
</div>
{{end}}
<!-- Like button -->
{{if not .IsPrivate}}
@ -431,4 +438,4 @@
</div>
{{end}}<!-- not IsPrivate -->
</div>
{{end}}
{{end}}

View File

@ -12,376 +12,537 @@
{{ $User := .CurrentUser }}
<div class="block p-4">
<div class="columns">
<div class="tabs is-boxed">
<ul>
<li class="is-active">
<a href="/settings#profile" class="nonshy-tab-button">
Profile
</a>
</li>
<li>
<a href="/settings#prefs" class="nonshy-tab-button">
Preferences
</a>
</li>
<div class="column is-hidden-tablet p-4">
<label class="label">Jump to section:</label>
<ul class="menu-list">
<li><a href="#profile">My Profile</a></li>
<li><a href="#verification">Verification Photo</a></li>
<li><a href="#prefs">Website Preferences</a></li>
<li><a href="#account">Account Settings <small class="has-text-grey ml-2">Email &amp; password</small></a></li>
</ul>
</div>
<li>
<a href="/settings#account" class="nonshy-tab-button">
Account
</a>
</li>
</ul>
</div>
<div class="column">
<div class="card" id="profile">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-user pr-2"></i>
My Profile
</p>
</header>
<div class="card" id="profile">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-user pr-2"></i>
My Profile
</p>
</header>
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="profile">
{{InputCSRF}}
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="profile">
{{InputCSRF}}
<div class="card-content">
<p class="block">
The fields here are shown on your <a href="/u/{{.CurrentUser.Username}}">profile page</a>
and are all optional. Fields with a <i class="fa fa-lock"></i> icon are not shown on
your page but may drive some data that is (e.g., your current age derived from your birthdate).
<div class="card-content">
<p class="block">
The fields here are shown on your <a href="/u/{{.CurrentUser.Username}}">profile page</a>
and are all optional. Fields with a <i class="fa fa-lock"></i> icon are not shown on
your page but may drive some data that is (e.g., your current age derived from your birthdate).
</p>
<div class="columns">
<div class="column field is-half">
<label class="label" for="display_name">Display Name</label>
<input type="text" class="input"
id="display_name"
name="display_name"
placeholder="John Doe"
value="{{or $User.Name ""}}">
</div>
<div class="column field is-half">
<label class="label" for="dob">Birthdate <i class="fa fa-lock"></i></label>
<input type="date" class="input{{if not $User.Birthdate.IsZero}} cursor-not-allowed{{end}}"
id="dob"
name="dob"
value="{{if not $User.Birthdate.IsZero}}{{$User.Birthdate.Format "2006-01-02"}}{{end}}"
required
{{if not $User.Birthdate.IsZero}}readonly{{end}}>
<p class="help">
Used to show your age on your profile.
{{if not $User.Birthdate.IsZero}}
If you entered a wrong birthdate, <a href="/contact">contact</a> support to change it.
{{end}}
</p>
<div class="columns">
<div class="column field is-half">
<label class="label" for="display_name">Display Name</label>
<input type="text" class="input"
id="display_name"
name="display_name"
placeholder="John Doe"
value="{{or $User.Name ""}}">
</div>
<!-- Don't show age checkbox -->
<label class="checkbox">
<input type="checkbox"
name="hide_age"
value="true"
{{if eq ($User.GetProfileField "hide_age") "true"}}checked{{end}}>
Don't show my age on my profile
</label>
</div>
</div>
<div class="column field is-half">
<label class="label" for="dob">Birthdate <i class="fa fa-lock"></i></label>
<input type="date" class="input{{if not $User.Birthdate.IsZero}} cursor-not-allowed{{end}}"
id="dob"
name="dob"
value="{{if not $User.Birthdate.IsZero}}{{$User.Birthdate.Format "2006-01-02"}}{{end}}"
required
{{if not $User.Birthdate.IsZero}}readonly{{end}}>
<p class="help">
Used to show your age on your profile.
{{if not $User.Birthdate.IsZero}}
If you entered a wrong birthdate, <a href="/contact">contact</a> support to change it.
{{end}}
</p>
<!-- Don't show age checkbox -->
<label class="checkbox">
<input type="checkbox"
name="hide_age"
value="true"
{{if eq ($User.GetProfileField "hide_age") "true"}}checked{{end}}>
Don't show my age on my profile
</label>
</div>
</div>
<div class="columns">
<div class="column field is-half">
<label class="label" for="gender">Gender</label>
<div class="select is-fullwidth">
<select id="gender" name="gender">
<option value="">No answer</option>
{{range .Enum.Gender}}
<option value="{{.}}"{{if eq ($User.GetProfileField "gender") .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
<div class="column field is-half">
<label class="label" for="pronouns">Pronouns</label>
<input type="text" class="input"
id="pronouns"
name="pronouns"
maxlength="30"
value="{{$User.GetProfileField "pronouns"}}">
<p class="help">e.g. he/him; she/her</p>
</div>
</div>
<div class="columns">
<div class="column field is-half">
<label class="label" for="city">City/Location</label>
<input type="text" class="input"
id="city"
name="city"
value="{{$User.GetProfileField "city"}}">
</div>
<div class="column field is-half">
<label class="label" for="job">Job/Occupation</label>
<input type="text" class="input"
id="job"
name="job"
value="{{$User.GetProfileField "job"}}">
</div>
</div>
<div class="columns">
<div class="column field is-half">
<label class="label" for="marital_status">Marital Status</label>
<div class="select is-fullwidth">
<select id="marital_status" name="marital_status">
<option value="">No answer</option>
{{range .Enum.MaritalStatus}}
<option value="{{.}}"{{if eq ($User.GetProfileField "marital_status") .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
<div class="column field is-half">
<label class="label" for="relationship_type">Relationship Type</label>
<div class="select is-fullwidth">
<select id="relationship_type" name="relationship_type">
<option value="">No answer</option>
{{range .Enum.RelationshipType}}
<option value="{{.}}"{{if eq ($User.GetProfileField "relationship_type") .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
<div class="columns">
<div class="column field is-half">
<label class="label" for="orientation">Orientation</label>
<div class="select is-fullwidth">
<select id="orientation" name="orientation">
<option value="">No answer</option>
{{range .Enum.Orientation}}
<option value="{{.}}"{{if eq ($User.GetProfileField "orientation") .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
<div class="field">
<label class="label">Here For</label>
<div class="columns is-multiline pb-5">
{{range .Enum.HereFor}}
<div class="column is-one-third pb-0">
<label class="checkbox">
<input type="checkbox"
name="here_for"
value="{{.}}"
{{if $User.ProfileFieldIn "here_for" .}}checked{{end}}>
{{.}}
</label>
</div>
<div class="columns">
<div class="column field is-half">
<label class="label" for="gender">Gender</label>
<div class="select is-fullwidth">
<select id="gender" name="gender">
<option value="">No answer</option>
{{range .Enum.Gender}}
<option value="{{.}}"{{if eq ($User.GetProfileField "gender") .}} selected{{end}}>{{.}}</option>
{{end}}
</div>
</div>
<div class="field">
<label class="label" for="about_me">About Me</label>
<textarea class="textarea" cols="60" rows="4"
id="about_me"
name="about_me"
placeholder="A little blurb about myself">{{$User.GetProfileField "about_me"}}</textarea>
<p class="help">
Write a bit about yourself. <a href="/markdown" target="_blank">Markdown formatting</a> is supported here.
</p>
</div>
<div class="field">
<label class="label" for="interests">My Interests</label>
<textarea class="textarea" cols="60" rows="4"
id="interests"
name="interests"
placeholder="What kinds of things make you curious?">{{$User.GetProfileField "interests"}}</textarea>
</div>
<div class="field">
<label class="label" for="music_movies">Music/Bands/Movies</label>
<textarea class="textarea" cols="60" rows="4"
id="music_movies"
name="music_movies"
placeholder="What is your style of music or movie?">{{$User.GetProfileField "music_movies"}}</textarea>
</div>
<div class="field">
<button type="submit" class="button is-primary">
Save Profile Settings
</button>
</select>
</div>
</div>
</form>
</div>
</div>
<div class="column">
<div class="column field is-half">
<label class="label" for="pronouns">Pronouns</label>
<input type="text" class="input"
id="pronouns"
name="pronouns"
maxlength="30"
value="{{$User.GetProfileField "pronouns"}}">
<p class="help">e.g. he/him; she/her</p>
</div>
</div>
<!-- Website Preferences -->
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="preferences">
{{InputCSRF}}
<div class="columns">
<div class="column field is-half">
<label class="label" for="city">City/Location</label>
<input type="text" class="input"
id="city"
name="city"
value="{{$User.GetProfileField "city"}}">
</div>
<div class="card mb-5" id="prefs">
<header class="card-header has-background-success">
<p class="card-header-title has-text-dark-dark">
<i class="fa fa-square-check pr-2"></i>
Website Preferences
</p>
</header>
<div class="card-content">
<div class="field">
<label class="label">Profile Visibility</label>
<label class="checkbox">
<input type="radio"
name="visibility"
value="public"
{{if eq .CurrentUser.Visibility "public"}}checked{{end}}>
Public + Login Required
</label>
<p class="help">
The default is that users must be logged-in to even look at your profile
page. If your profile URL (/u/{{.CurrentUser.Username}}) is visited by a
logged-out browser, they are prompted to log in.
</p>
<div class="column field is-half">
<label class="label" for="job">Job/Occupation</label>
<input type="text" class="input"
id="job"
name="job"
value="{{$User.GetProfileField "job"}}">
</div>
</div>
<label class="checkbox mt-2">
<input type="radio"
name="visibility"
value="external"
{{if eq .CurrentUser.Visibility "external"}}checked{{end}}>
Public + Limited Logged-out View
</label>
<p class="help">
Your profile is fully visible to logged-in users, but if a logged-out browser
visits your page they will see a very minimal view: only your profile picture
and display name are shown.
<a href="/u/{{.CurrentUser.Username}}?view=external" target="_blank">Preview <i class="fa fa-external-link"></i></a>
</p>
<label class="checkbox mt-2">
<input type="radio"
name="visibility"
value="private"
{{if eq .CurrentUser.Visibility "private"}}checked{{end}}>
Mark my profile page as "private"
</label>
<p class="help">
If you check this box then only friends who you have approved are able to
see your profile page and gallery. Your gallery photos also will NOT appear
on the Site Gallery page. If your profile page is visited by a logged-out
viewer, they are prompted to log in.
</p>
<div class="columns">
<div class="column field is-half">
<label class="label" for="marital_status">Marital Status</label>
<div class="select is-fullwidth">
<select id="marital_status" name="marital_status">
<option value="">No answer</option>
{{range .Enum.MaritalStatus}}
<option value="{{.}}"{{if eq ($User.GetProfileField "marital_status") .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
<div class="field">
<label class="label">Explicit Content Filter</label>
<div class="column field is-half">
<label class="label" for="relationship_type">Relationship Type</label>
<div class="select is-fullwidth">
<select id="relationship_type" name="relationship_type">
<option value="">No answer</option>
{{range .Enum.RelationshipType}}
<option value="{{.}}"{{if eq ($User.GetProfileField "relationship_type") .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
<div class="columns">
<div class="column field is-half">
<label class="label" for="orientation">Orientation</label>
<div class="select is-fullwidth">
<select id="orientation" name="orientation">
<option value="">No answer</option>
{{range .Enum.Orientation}}
<option value="{{.}}"{{if eq ($User.GetProfileField "orientation") .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
<div class="field">
<label class="label">Here For</label>
<div class="columns is-multiline pb-5">
{{range .Enum.HereFor}}
<div class="column is-one-third pb-0">
<label class="checkbox">
<input type="checkbox"
name="explicit"
value="true"
{{if .CurrentUser.Explicit}}checked{{end}}>
Show explicit content
name="here_for"
value="{{.}}"
{{if $User.ProfileFieldIn "here_for" .}}checked{{end}}>
{{.}}
</label>
<p class="help">
Check this box if you are OK seeing explicit content on this site, which may
include erections or sexually charged content. These may appear on the Site
Gallery as well as user profile pages.
</p>
</div>
<div class="field">
<button type="submit" class="button is-primary">
Save Website Preferences
</button>
</div>
{{end}}
</div>
</div>
</form>
<!-- Account Settings -->
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="settings">
{{InputCSRF}}
<div class="card mb-5" id="account">
<header class="card-header has-background-warning">
<p class="card-header-title has-text-dark-dark">
<i class="fa fa-gear pr-2"></i>
Account Settings
</p>
</header>
<div class="card-content">
<div class="field">
<label class="label" for="old_password">
Current Password
</label>
<input type="password" class="input"
name="old_password"
id="old_password"
placeholder="Current password"
required>
<p class="help">
Enter your current password before making any changes to your
email address or setting a new password.
</p>
</div>
<div class="field">
<label class="label" for="change_email">Change Email</label>
<input type="email" class="input"
id="change_email"
name="change_email"
placeholder="name@domain.com"
value="{{.CurrentUser.Email}}">
</div>
<div class="field">
<label class="label">Change Password</label>
<input type="password" class="input mb-2"
name="new_password"
placeholder="New password">
<input type="password" class="input mb-2"
name="new_password2"
placeholder="Confirm new password">
</div>
<div class="field">
<button type="submit" class="button is-primary">
Save Account Settings
</button>
</div>
</div>
<div class="field">
<label class="label" for="about_me">About Me</label>
<textarea class="textarea" cols="60" rows="4"
id="about_me"
name="about_me"
placeholder="A little blurb about myself">{{$User.GetProfileField "about_me"}}</textarea>
<p class="help">
Write a bit about yourself. <a href="/markdown" target="_blank">Markdown formatting</a> is supported here.
</p>
</div>
</form>
<!-- Delete Account -->
<div class="card mb-5" id="account">
<header class="card-header has-background-danger">
<p class="card-header-title has-text-light">
<div class="field">
<label class="label" for="interests">My Interests</label>
<textarea class="textarea" cols="60" rows="4"
id="interests"
name="interests"
placeholder="What kinds of things make you curious?">{{$User.GetProfileField "interests"}}</textarea>
</div>
<div class="field">
<label class="label" for="music_movies">Music/Bands/Movies</label>
<textarea class="textarea" cols="60" rows="4"
id="music_movies"
name="music_movies"
placeholder="What is your style of music or movie?">{{$User.GetProfileField "music_movies"}}</textarea>
</div>
<div class="field">
<button type="submit" class="button is-primary">
Save Profile Settings
</button>
</div>
</div>
</form>
</div>
<!-- Website Preferences -->
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="preferences">
{{InputCSRF}}
<div class="card mb-5" id="prefs">
<header class="card-header has-background-success">
<p class="card-header-title has-text-dark-dark">
<i class="fa fa-square-check pr-2"></i>
Website Preferences
</p>
</header>
<div class="card-content">
<div class="field">
<label class="label">Profile Visibility</label>
<label class="checkbox">
<input type="radio"
name="visibility"
value="public"
{{if eq .CurrentUser.Visibility "public"}}checked{{end}}>
Public + Login Required
<i class="fa fa-eye ml-2 has-text-info"></i>
</label>
<p class="help">
The default is that users must be logged-in to even look at your profile
page. If your profile URL (/u/{{.CurrentUser.Username}}) is visited by a
logged-out browser, they are prompted to log in.
</p>
<label class="checkbox mt-2">
<input type="radio"
name="visibility"
value="external"
{{if eq .CurrentUser.Visibility "external"}}checked{{end}}>
Public + Limited Logged-out View
<i class="fa fa-eye ml-2 has-text-danger"></i>
</label>
<p class="help">
Your profile is fully visible to logged-in users, but if a logged-out browser
visits your page they will see a very minimal view: only your profile picture
and display name are shown.
<a href="/u/{{.CurrentUser.Username}}?view=external" target="_blank">Preview <i class="fa fa-external-link"></i></a>
</p>
<label class="checkbox mt-2">
<input type="radio"
name="visibility"
value="private"
{{if eq .CurrentUser.Visibility "private"}}checked{{end}}>
Mark my profile page as "private"
<i class="fa fa-lock ml-2 has-text-private"></i>
</label>
<p class="help">
If you check this box then only friends who you have approved are able to
see your profile page and gallery. Your gallery photos also will NOT appear
on the Site Gallery page. If your profile page is visited by a logged-out
viewer, they are prompted to log in.
</p>
</div>
<hr>
<div class="field">
<label class="label">Explicit Content Filter</label>
<label class="checkbox">
<input type="checkbox"
name="explicit"
value="true"
{{if .CurrentUser.Explicit}}checked{{end}}>
Show explicit content <i class="fa fa-fire has-text-danger ml-1"></i>
</label>
<p class="help">
Check this box if you are OK seeing explicit content on this site, which may
include erections or sexually charged content. These may appear on the Site
Gallery as well as user profile pages.
</p>
</div>
<hr>
<div class="field">
<label class="label mb-0">Who can send me the first <i class="fa fa-envelope"></i> Message?</label>
<div class="has-text-warning ml-4">
<small><em>
Note: this refers to Direct Messages on the main website
(not inside the chat room).
</em></small>
{{.CurrentUser.GetProfileField "dm_privacy"}}
</div>
<label class="checkbox">
<input type="radio"
name="dm_privacy"
value=""
{{if eq (.CurrentUser.GetProfileField "dm_privacy") ""}}checked{{end}}>
Anybody on the site
</label>
<p class="help">
Almost any member of the site may send you a Direct Message from your profile
page (except for maybe <a href="/faq#shy-faqs" target="_blank">Shy Accounts</a>).
</p>
<label class="checkbox">
<input type="radio"
name="dm_privacy"
value="friends"
{{if eq (.CurrentUser.GetProfileField "dm_privacy") "friends"}}checked{{end}}>
Only people on my Friends list
</label>
<p class="help">
Nobody can slide into your DMs except for friends (and admins if needed). Anybody
may <em>reply</em> to messages that you send to them.
</p>
<label class="checkbox">
<input type="radio"
name="dm_privacy"
value="nobody"
{{if eq (.CurrentUser.GetProfileField "dm_privacy") "nobody"}}checked{{end}}>
Nobody (close my DMs)
</label>
<p class="help">
Nobody can start a Direct Message conversation with you on the main website
(except an admin if necessary). Anybody may <em>reply</em> to messages that you
sent to them first.
</p>
</div>
<!-- TODO: manually opt-in dark mode is hairy, look at
those media queries in bulma-prefers-dark.js!
<div class="field">
<label class="label">Website Theme</label>
<label class="checkbox">
<input type="radio"
name="theme"
value=""
{{if eq ($User.GetProfileField "theme") "" }}checked{{end}}>
Automatic
</label>
<p class="help">
Automatically chooses a theme based on your device settings.
</p>
<label class="checkbox">
<input type="radio"
name="theme"
value="light"
{{if eq ($User.GetProfileField "theme") "light" }}checked{{end}}>
Light
</label>
<label class="checkbox">
<input type="radio"
name="theme"
value="dark"
{{if eq ($User.GetProfileField "theme") "dark" }}checked{{end}}>
Dark
</label>
</div>
-->
<div class="field">
<button type="submit" class="button is-primary">
Save Website Preferences
</button>
</div>
</div>
</div>
</form>
<!-- Account Settings -->
<div id="account">
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="settings">
{{InputCSRF}}
<div class="card mb-5">
<header class="card-header has-background-warning">
<p class="card-header-title has-text-dark-dark">
<i class="fa fa-gear pr-2"></i>
Delete Account
Account Settings
</p>
</header>
<div class="card-content">
<p class="block">
If you would like to delete your account, please click
on the button below.
</p>
<div class="field">
<label class="label" for="old_password">
Current Password
</label>
<input type="password" class="input"
name="old_password"
id="old_password"
placeholder="Current password"
required>
<p class="help">
Enter your current password before making any changes to your
email address or setting a new password.
</p>
</div>
<div class="field">
<label class="label" for="change_email">Change Email</label>
<input type="email" class="input"
id="change_email"
name="change_email"
placeholder="name@domain.com"
value="{{.CurrentUser.Email}}">
</div>
<p class="block">
<a href="/account/delete" class="button is-danger">
Delete My Account
</a>
</p>
<div class="field">
<label class="label">Change Password</label>
<input type="password" class="input mb-2"
name="new_password"
placeholder="New password">
<input type="password" class="input mb-2"
name="new_password2"
placeholder="Confirm new password">
</div>
<div class="field">
<button type="submit" class="button is-primary">
Save Account Settings
</button>
</div>
</div>
</div>
</form>
<!-- Delete Account -->
<div class="card mb-5">
<header class="card-header has-background-danger">
<p class="card-header-title has-text-light">
<i class="fa fa-gear pr-2"></i>
Delete Account
</p>
</header>
<div class="card-content">
<p class="block">
If you would like to delete your account, please click
on the button below.
</p>
<p class="block">
<a href="/account/delete" class="button is-danger">
Delete My Account
</a>
</p>
</div>
</div>
</div>
</div>
</div>
{{end}}
{{end}}
{{define "scripts"}}
<script>
window.addEventListener("DOMContentLoaded", (event) => {
// The tabs
const $profile = document.querySelector("#profile"),
$prefs = document.querySelector("#prefs"),
$account = document.querySelector("#account")
buttons = Array.from(document.getElementsByClassName("nonshy-tab-button"));
// Hide all by default.
$profile.style.display = 'none';
$prefs.style.display = 'none';
$account.style.display = 'none';
// Current tab to select by default.
let $activeTab = $profile;
// Global function to toggle the active tab.
const showTab = (name) => {
name = name.replace(/\.$/, '');
if (!name) name = "profile";
$activeTab.style.display = 'none';
switch (name) {
case "prefs":
$activeTab = $prefs;
break
case "account":
$activeTab = $account;
break
default:
$activeTab = $profile;
}
// Update the is-active classes on all the tabs.
buttons.forEach(tab_ => {
let name_ = tab_.href.split("#").pop(),
parent = tab_.parentElement;
if (name !== name_) {
console.log("button: remove is-active", tab_);
parent.classList.remove("is-active");
} else {
console.log("button %s: ADD is-active", tab_);
parent.classList.add("is-active");
}
});
$activeTab.style.display = 'block';
history.replaceState(undefined, undefined, '#'+name);
};
// Wire the tab buttons up.
buttons.forEach(el => {
let name = el.href.split("#").pop();
el.addEventListener("click", (e) => {
showTab(name);
e.preventDefault();
});
})
showTab(window.location.hash.replace(/^#/, ''));
});
</script>
{{end}}

View File

@ -333,4 +333,4 @@
</body>
</html>
{{end}}
{{end}}