website/web/templates/account/settings.html
Noah Petherbridge 1b3e8cb250 Private Photo Sharing Improvements
* Add a user privacy setting so they can gate who is allowed to share private
  photos with them (for people who dislike unsolicited shares):
  * Anybody (default)
  * Friends only
  * Friends + people whom they have sent a DM to (on the main website)
  * Nobody
* Add gating around whether to display the prompt to unlock your private photos
  while you are viewing somebody's gallery:
  * The current user needs at least one private photo to share.
  * The target user's new privacy preference is taken into consideration.
* The "should show private photo share prompt" logic is also used on the actual
  share page, e.g. for people who manually paste in a username to share with.
  You can not grant access to private photos which don't exist.
* Improve the UI on the private photo shares page.
  * Profile cards to add elements from the Member Directory page, such as a
    Friends and Liked indicator.
  * A count of the user's Private photos is shown, which links directly to
    their private gallery.
* Add "Decline" buttons to the Shared With Me page: so the target of a private
  photo share is able to remove/cancel shares with them.
2024-10-19 12:44:47 -07:00

1898 lines
94 KiB
HTML

{{define "title"}}User Settings{{end}}
{{define "content"}}
<div class="container">
<section class="hero is-link is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">User Settings</h1>
</div>
</div>
</section>
{{ $User := .CurrentUser }}
<div class="columns mt-4">
<div class="column is-one-quarter">
<div class="card">
<div class="card-header has-background-info">
<p class="card-header-title has-text-light">
<i class="fa fa-gear mr-2"></i>
Settings Menu
</p>
</div>
<div class="card-content p-4">
<aside class="menu">
<p class="menu-label">
Settings
</p>
<ul class="menu-list">
<li>
<a href="/settings#profile" class="nonshy-tab-button">
<strong><i class="fa fa-address-card mr-1"></i> My Profile</strong>
<p class="help">Manage your profile page.</p>
</a>
</li>
<li>
<a href="/settings#look" class="nonshy-tab-button">
<strong><i class="fa fa-palette mr-1"></i> Look &amp; Feel</strong>
<p class="help">
Customize your profile with a color scheme.
</p>
</a>
</li>
<li>
<a href="/settings#prefs" class="nonshy-tab-button">
<strong><i class="fa fa-square-check mr-1"></i> Website Preferences</strong>
<p class="help">
Explicit content filter <i class="fa fa-fire"></i>; photo blur.
</p>
</a>
</li>
<li>
<a href="/settings#location" class="nonshy-tab-button">
<strong><i class="fa fa-globe mr-1"></i> Location Settings</strong>
<p class="help">
For the "Who's Nearby?" feature.
</p>
</a>
</li>
<li>
<a href="/settings#privacy" class="nonshy-tab-button">
<strong><i class="fa fa-eye mr-1"></i> Privacy</strong>
<p class="help">
Profile visibility; who can slide into your DMs.
</p>
</a>
</li>
<li>
<a href="/settings#notifications" class="nonshy-tab-button">
<strong><i class="fa fa-bell mr-1"></i> Notifications</strong>
<p class="help">
Control your (on-site) notification preferences.
</p>
</a>
</li>
<li>
<a href="/settings#account" class="nonshy-tab-button">
<strong><i class="fa fa-user mr-1"></i> Account Settings</strong>
<p class="help">
Change password or e-mail; set up Two-Factor Authentication (2FA).
</p>
</a>
</li>
<li>
<a href="/settings#deactivate" class="nonshy-tab-button">
<strong><i class="fa fa-exclamation-triangle mr-1"></i> Deactivate Account</strong>
<p class="help">
Temporarily deactivate or permanently delete my account.
</p>
</a>
</li>
</ul>
<p class="menu-label">
See Also
</p>
<ul class="menu-list">
<li>
<a href="/photo/certification">
<strong><i class="fa fa-certificate mr-1"></i> Certification Photo</strong>
<p class="help">
View and manage your Certification status.
</p>
</a>
</li>
<li>
<a href="/photo/private">
<strong><i class="fa fa-eye mr-1"></i> Private Photos</strong>
<p class="help">
Manage who can see your private photos.
</p>
</a>
</li>
<li>
<a href="/users/blocked">
<strong><i class="fa fa-hand mr-1"></i> Blocked Users</strong>
<p class="help">
View and manage your block list.
</p>
</a>
</li>
<li>
<a href="/notes/me">
<strong><i class="fa fa-pen-to-square mr-1"></i> My User Notes</strong>
<p class="help">
Browse and search private notes you have written about others.
</p>
</a>
</li>
</ul>
</aside>
</div>
</div>
</div>
<div class="column">
<!-- Profile -->
<div class="card" id="profile">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-address-card pr-2"></i>
My Profile
</p>
</header>
<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).
</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>
<p class="help">
Used to show your age on your profile.
{{if not $User.Birthdate.IsZero}}
If you entered a wrong birthdate, you can change it here. Note that all birthdate
changes will notify the admin, so don't mess around or behave dishonestly.
{{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.
<a href="#" onclick="document.querySelector('#pronouns').value='he/him'; return false">he/him</a>;
<a href="#" onclick="document.querySelector('#pronouns').value='she/her'; return false">she/her</a>;
<a href="#" onclick="document.querySelector('#pronouns').value='they/them'; return false">they/them</a>
</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>
{{end}}
</div>
</div>
<div class="field" id="profile/about_me">
<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" id="profile/interests">
<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" id="profile/music_movies">
<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">
<i class="fa fa-save mr-2"></i> Save Profile Settings
</button>
</div>
</div>
</form>
</div>
<!-- Look & Feel -->
<div class="card mb-5" id="look">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-palette pr-2"></i>
Look &amp; Feel
</p>
</header>
<div class="card-content">
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="look">
{{InputCSRF}}
<p class="block">
On this screen, you may customize the visual appearance of your profile
page. You may set a custom color gradient for your header bar and personalize
the colors of your profile cards.
</p>
<!--
-- Website Theme Section
-->
<h2 class="subtitle">Website Theme <span class="tag is-success">New!</span></h2>
<div class="field">
<label class="checkbox">
<input type="radio"
name="website-theme"
value=""
{{if eq (.CurrentUser.GetProfileField "website-theme") ""}}checked{{end}}>
Automatically match my device's theme
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="radio"
name="website-theme"
value="light"
{{if eq (.CurrentUser.GetProfileField "website-theme") "light"}}checked{{end}}>
Always use the light theme
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="radio"
name="website-theme"
value="dark"
{{if eq (.CurrentUser.GetProfileField "website-theme") "dark"}}checked{{end}}>
Always use the dark theme
</label>
<p class="help">
This setting controls how the website theme will appear to you while logged in.
By default, the dark theme is used if your device prefers dark, but you can
manually override it to the Light or Dark themes above.
Remember to click "Save Look & Feel" below to see the change!
</p>
</div>
<hr>
<!--
-- Profile Header Section
-->
<h2 class="subtitle">Profile Header</h2>
<div class="field">
<label class="label">Preview</label>
</div>
<!-- Header preview hero, should be similar to profile page. -->
<div class="hero is-link is-bold mb-4" id="header-hero-preview">
<div class="hero-body p-4">
<div class="container">
<div class="columns is-mobile is-gapless mt-1 mb-6">
<div class="column is-narrow has-text-centered">
<figure class="profile-photo is-inline-block" style="width: auto; height: auto">
{{template "avatar-48x48" .CurrentUser}}
</figure>
</div>
<div class="column mx-2">
<strong>{{.CurrentUser.NameOrUsername}}</strong>
</div>
{{if and .LoggedIn (not .IsPrivate)}}
<div class="column is-narrow">
<div class="box">
<div style="width: 16px;"></div>
</div>
</div>
{{end}}<!-- if .LoggedIn -->
</div>
</div>
</div>
</div>
<div class="field">
<label class="label">Header Gradient</label>
<div class="columns is-mobile">
<div class="column is-narrow">
<input type="color" class="color"
id="hero-color-start"
name="hero-color-start"
{{if ($User.GetProfileField "hero-color-start")}}
value="{{$User.GetProfileField "hero-color-start"}}"
{{else}}
value="{{template "--prof-colorA"}}"
{{end}}
>
</div>
<div class="column is-narrow">
<input type="color" class="color"
id="hero-color-end"
name="hero-color-end"
{{if ($User.GetProfileField "hero-color-end")}}
value="{{$User.GetProfileField "hero-color-end"}}"
{{else}}
value="{{template "--prof-colorB"}}"
{{end}}
>
</div>
<div class="column">
<label class="checkbox">
<input type="checkbox"
id="hero-text-dark"
name="hero-text-dark"
value="true"
{{if eq (.CurrentUser.GetProfileField "hero-text-dark") "true"}}checked{{end}}
>
Dark text
</label>
</div>
</div>
</div>
<hr>
<!--
-- Profile Page Section
-->
<h2 class="subtitle">Profile Page</h2>
<div class="card block" id="profile-card-preview">
<div class="card-header">
<p class="card-header-title">Card Colors</p>
</div>
<div class="card-body">
<table class="table is-fullwidth">
<tr>
<td>
<strong>Lightness</strong>
</td>
<td>
<div class="select is-fullwidth">
<select id="card-lightness" name="card-lightness">
<option value="">Automatic</option>
<option value="light"{{if eq ($User.GetProfileField "card-lightness") "light"}} selected{{end}}>Light theme (black on white)</option>
<option value="dark"{{if eq ($User.GetProfileField "card-lightness") "dark"}} selected{{end}}>Dark theme (white on black)</option>
</select>
</div>
</td>
</tr>
<tr>
<td class="is-narrow">
<strong>Card Title BG</strong>
</td>
<td>
<input type="color"
id="card-title-bg"
name="card-title-bg"
{{if ($User.GetProfileField "card-title-bg")}}
value="{{$User.GetProfileField "card-title-bg"}}"
{{else}}
value="{{template "--prof-card-bg"}}"
{{end}}
>
</td>
</tr>
<tr>
<td>
<strong>Card Title FG</strong>
</td>
<td>
<input type="color"
id="card-title-fg"
name="card-title-fg"
{{if ($User.GetProfileField "card-title-fg")}}
value="{{$User.GetProfileField "card-title-fg"}}"
{{else}}
value="{{template "--prof-card-fg"}}"
{{end}}
>
</td>
</tr>
<tr>
<td>
<strong>Link Color</strong>
</td>
<td>
<input type="color"
id="card-link-color"
name="card-link-color"
{{if ($User.GetProfileField "card-link-color")}}
value="{{$User.GetProfileField "card-link-color"}}"
{{else}}
value="{{template "--prof-link-fg"}}"
{{end}}
>
<a href="#" onclick="return false" class="ml-2">
Example
<i class="fa fa-link"></i>
</a>
</td>
</tr>
</table>
</div>
</div>
<div class="field">
<label class="label">
Reset Styles
</label>
<label class="checkbox">
<input type="checkbox"
name="reset"
value="true">
Reset to default style
</label>
<p class="help">
If you'd like to reset all your page styles to their default, check
this box and click Save below.
</p>
</div>
<div class="field">
<button type="submit" class="button is-primary">
<i class="fa fa-save mr-2"></i> Save Look &amp; Feel
</button>
</div>
</form>
</div>
</div>
<!-- Website Preferences -->
<div class="card mb-5" id="prefs">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-square-check pr-2"></i>
Website Preferences
</p>
</header>
<div class="card-content">
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="preferences">
{{InputCSRF}}
<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>
<label class="checkbox">
<input type="checkbox"
name="blur_explicit"
value="true"
{{if eq (.CurrentUser.GetProfileField "blur_explicit") "true"}}checked{{end}}>
Blur explicit photos by default
</label>
<p class="help">
Explicit photos on Gallery pages will be blurred by default until clicked. Photos
on explicit Forum threads will also be blurred until clicked.
</p>
</div>
<div class="field">
<label class="label">Display Settings</label>
<label class="checkbox">
<input type="checkbox"
name="autoplay_gif"
value="true"
{{if not (eq (.CurrentUser.GetProfileField "autoplay_gif") "false")}}checked{{end}}>
Automatically play animated GIFs
</label>
<p class="help">
Uncheck this box to disable auto-play on GIFs.
</p>
</div>
<div class="field">
<button type="submit" class="button is-primary">
<i class="fa fa-save mr-2"></i> Save Website Preferences
</button>
</div>
</form>
</div>
</div>
<!-- Location Settings -->
<div id="location">
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="location">
{{InputCSRF}}
<div class="card mb-5">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-globe mr-2"></i>
Location Settings
</p>
</header>
<div class="card-content">
<p class="block">
The settings on this page control your location for the <a href="/members?sort=distance"><strong>Who's Nearby</strong></a>
feature of {{PrettyTitle}}. Being discoverable by your location is an <strong>opt-in</strong>
feature and you have your choice of options how you want your location to be found.
</p>
<div class="notification is-info is-light py-2 px-3">
<i class="fa fa-exclamation-triangle"></i>
<strong>Notice:</strong>
Remember to click "Save" after setting your location preference!
</div>
<div class="field">
<label class="label">How do you want your location to be determined?</label>
<label class="checkbox">
<input type="radio"
name="source"
value=""
id="location-option-none"
{{if eq .UserLocation.Source ""}}checked{{end}}>
<i class="fa fa-ban ml-2 mr-1 has-text-danger"></i>
Do not share my location with {{PrettyTitle}}.
</label>
<p class="help mb-4">
This option will opt-out of the Who's Nearby feature and your profile will not be
discoverable by distance to other members. Any location data already stored by
the website will be erased if you choose this option.
</p>
<label class="checkbox">
<input type="radio"
name="source"
value="geoip"
id="location-option-geoip"
{{if eq .UserLocation.Source "geoip"}}checked{{end}}>
<i class="fa fa-network-wired ml-2 mr-1 has-text-info"></i>
Automatically detect my location by my IP address
</label>
<p class="help mb-4">
Coarse location data based on your IP address. Might be accurate to your
city level.
<!-- Do we have GeoIP insights? -->
{{if not .GeoIPInsights.IsZero}}
<br>
<strong>Currently:</strong>
{{.GeoIPInsights}}
{{end}}
</p>
<label class="checkbox">
<input type="radio"
name="source"
value="gps"
id="location-option-gps"
{{if eq .UserLocation.Source "gps"}}checked{{end}}>
<i class="fa fa-location-dot ml-2 mr-1 has-text-success"></i>
Automatically detect my current location by GPS (with your permission)
</label>
<p class="help mb-4">
Your web browser will prompt you to share your current location with {{PrettyTitle}}.
<br>
<strong>Currently:</strong>
<span id="location-status-gps">You have not granted permission.</span>
<a href="#" class="fa fa-arrows-rotate" id="gps-refresh" title="Refresh my location"></a>
</p>
<label class="checkbox">
<input type="radio"
name="source"
value="pin"
id="location-option-pin"
{{if eq .UserLocation.Source "pin"}}checked{{end}}>
<i class="fa fa-map-pin ml-2 mr-1 has-text-info"></i>
Drop a pin on the map myself
</label>
<p class="help mb-4">
Use the map below and drop a pin anywhere you like to set your location.
</p>
</div>
<div class="field">
<label class="label">
Your Current Location (click "Save" when you are satisfied)
</label>
<p class="block">
Your location will be saved as the following in the database:
</p>
<div class="columns is-mobile" style="max-width: 500px">
<div class="column is-half pr-1">
<label class="label mb-0">Latitude:</label>
<input type="text" class="input is-fullwidth"
name="latitude"
id="saveLatitude"
value="{{.UserLocation.Latitude}}"
placeholder="None"
readonly>
</div>
<div class="column is-half pl-1">
<label class="label mb-0">Longitude:</label>
<input type="text" class="input is-fullwidth"
name="longitude"
id="saveLongitude"
value="{{.UserLocation.Longitude}}"
placeholder="None"
readonly>
</div>
</div>
</div>
<div class="field">
<button type="submit" class="button is-success mr-2"
name="intent" value="location">
<i class="fa fa-save mr-2"></i>
Save My Location Settings
</button>
</div>
<h3 class="is-size-3">Map</h3>
<p class="block">
This map shows your current location pin. To click and drop a pin manually,
select the "Drop a pin on the map myself" option above. Otherwise, the map will
center on your GPS location (if available) or your IP address location, depending
on your selection above.
</p>
<div id="map" class="block" style="width: 100%; height: 450px"></div>
<p class="block">
Map tiles provided by <a href="https://www.openstreetmap.org" target="_blank">OpenStreetMap.</a>
</p>
</div>
</div>
</form>
</div>
<!-- Privacy Settings -->
<div class="card mb-5" id="privacy">
<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>
Privacy Settings
</p>
</header>
<div class="card-content">
<form method="POST" action="/settings">
{{InputCSRF}}
<input type="hidden" name="intent" value="privacy">
<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-3">
<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-3">
<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 mb-0">Who can send me the first <i class="fa fa-envelope"></i> Message?</label>
<div class="has-text-info">
<small><em>
Note: this refers to Direct Messages on the main website
(not inside the chat room).
</em></small>
</div>
<label class="checkbox mt-3">
<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 mt-3">
<input type="radio"
name="dm_privacy"
value="friends"
{{if eq (.CurrentUser.GetProfileField "dm_privacy") "friends"}}checked{{end}}>
Only people on my Friends list
<i class="fa fa-user-group has-text-warning ml-2"></i>
</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 mt-3">
<input type="radio"
name="dm_privacy"
value="nobody"
{{if eq (.CurrentUser.GetProfileField "dm_privacy") "nobody"}}checked{{end}}>
Nobody (close my DMs)
<i class="fa fa-hand has-text-danger ml-2"></i>
</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>
<hr>
<div class="field">
<label class="label mb-0">
Who can share their
<span class="has-text-private">
<i class="fa fa-eye"></i> Private Photos
</span>
with me?
<span class="tag is-success">New!</span>
</label>
<p class="help">
This setting can help you to be in control of who else on {{PrettyTitle}} is allowed
to unlock their private photo gallery for you.
</p>
<label class="checkbox mt-3">
<input type="radio"
name="private_photo_gate"
value=""
{{if eq (.CurrentUser.GetProfileField "private_photo_gate") ""}}checked{{end}}>
Anybody on the site
</label>
<p class="help">
Any member of the website is able to share their private photo gallery with you.
</p>
<label class="checkbox mt-3">
<input type="radio"
name="private_photo_gate"
value="friends"
{{if eq (.CurrentUser.GetProfileField "private_photo_gate") "friends"}}checked{{end}}>
Only people on my Friends list
<i class="fa fa-user-group has-text-warning ml-2"></i>
</label>
<p class="help">
Only people who you have accepted as a friend will have the ability to share their private
photo gallery with you.
</p>
<label class="checkbox mt-3">
<input type="radio"
name="private_photo_gate"
value="messaged"
{{if eq (.CurrentUser.GetProfileField "private_photo_gate") "messaged"}}checked{{end}}>
Only my friends and people I have sent a DM to
<i class="fa fa-user-group has-text-warning ml-2"></i>
<i class="fa fa-envelope has-text-link"></i>
</label>
<p class="help">
People on your friend list and people who <strong>you</strong> have sent a Direct Message to
(on the main website - not the chat room) will be able to share their private photos with you.
Note: for example, if somebody sends <em>you</em> an unsolicited DM and you did not respond,
that person can not share their private photos with you.
</p>
<label class="checkbox mt-3">
<input type="radio"
name="private_photo_gate"
value="nobody"
{{if eq (.CurrentUser.GetProfileField "private_photo_gate") "nobody"}}checked{{end}}>
Nobody <i class="fa fa-hand has-text-danger ml-2"></i>
</label>
<p class="help">
Nobody on the website will be allowed to share their private gallery with you. Note: this
will mean that you will have no method to see private photos on the site except those which
had already been shared with you in the past.
</p>
</div>
<hr>
<div class="field">
<button type="submit" class="button is-primary">
<i class="fa fa-save mr-2"></i> Save Privacy Settings
</button>
</div>
</form>
</div>
</div>
<!-- Notification Settings -->
<div id="notifications">
<div class="card mb-5">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-bell pr-2"></i>
Web Push Notifications <span class="tag is-success ml-2">New!</span>
</p>
</header>
<div class="card-content">
<p class="block">
You may opt-in to receive Web Push Notifications for some of your important updates
from {{PrettyTitle}}, such as when you receive a new Direct Message on the main website,
even when you have closed your browser.
</p>
<p class="block">
<strong>Push Notification Permission:</strong>
<strong class="has-text-success" id="push-status-enabled" style="display: none">
<i class="fa fa-check mr-1"></i> Granted
</strong>
<strong class="has-text-danger" id="push-status-disabled" style="display: none">
<i class="fa fa-xmark mr-1"></i> Denied
</strong>
<strong class="has-text-warning" id="push-status-default" style="display: none">
<i class="fa fa-xmark mr-1"></i> Not Granted
</strong>
</p>
<p class="block">
<!-- Button to Grant Permission or Test Notifications -->
<a href="#" id="grant-push-permission"
class="button is-small is-success">
Test Notifications
</a>
<!-- Help error if the permission is denied -->
<span id="push-denied-help" style="display: none">
<i class="fa fa-info-circle has-text-warning mr-1"></i>
You had denied notification permission to this site. Please check in your web browser's
settings (or click in your address bar to the left of the website URL) to reset your
permission setting. Please <a href="/faq#troubleshoot-web-push">see this page</a> for
help in case you want to resolve this.
</span>
</p>
<!-- Have existing subscriptions? -->
{{if .PushNotificationsCount}}
<p class="block">
<strong>Sessions:</strong> you have enabled push notifications on {{.PushNotificationsCount}} web browser{{Pluralize64 .PushNotificationsCount}}. You may
<a href="/v1/web-push/unregister">click here</a> to reset your subscriptions. Devices that you actively use
(and had granted permission on before) may re-subscribe on your next visit.
</p>
{{end}}
<!-- Specific Push Notification Opt-out Form -->
<form method="POST" action="/settings">
{{InputCSRF}}
<input type="hidden" name="intent" value="push_notifications">
<div class="field">
<label class="label">Send a Web Push Notification when...</label>
<label class="checkbox">
<input type="checkbox"
name="notif_optout_push_messages"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_push_messages") "true"}}checked{{end}}>
I receive a Direct Message on the main website
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="notif_optout_push_friends"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_push_friends") "true"}}checked{{end}}>
I receive a new Friend Request
</label>
</div>
<div class="field">
<button type="submit" class="button is-primary">
<i class="fa fa-save mr-2"></i> Save Push Notification Settings
</button>
</div>
</form>
</div>
</div>
<div class="card mb-5">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-bell pr-2"></i>
Notification Settings
</p>
</header>
<div class="card-content">
<p class="block">
On this page you may opt-out of certain kinds of (on-site) notification messages.
{{PrettyTitle}} does not send you any e-mails or push notification -- these on-site
notifications only appear while you are visiting the website (on your
<a href="/me">home/user dashboard page</a>).
</p>
<form method="POST" action="/settings">
{{InputCSRF}}
<input type="hidden" name="intent" value="notifications">
<h2 class="subtitle">New Photo Uploads</h2>
<p class="block">
By default you will be notified when your friends upload a new picture to the site.
Below, you may opt-out of new photo upload notifications.
</p>
<div class="field">
<label class="label">Notify me when...</label>
<label class="checkbox">
<input type="checkbox"
name="notif_optout_friends_photos"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_friends_photos") "true"}}checked{{end}}>
My friends upload a new photo
</label>
<p class="help">
If unchecked, the following two notifications will not be sent either.
</p>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="notif_optout_private_photos"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_private_photos") "true"}}checked{{end}}>
A friend who shared their private photos with me uploads a new private photo
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="notif_optout_explicit_photos"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_explicit_photos") "true"}}checked{{end}}>
Allow notifications for 'explicit' photo uploads by my friends
</label>
<p class="help">
This will also depend on your <a href="/settings#prefs" target="_blank">opt-in to see explicit content</a> -- otherwise
notifications about explicit photo uploads will not be sent to you.
</p>
</div>
<h2 class="subtitle mt-5">Likes &amp; Comments</h2>
<p class="block">
By default you will be notified when somebody 'likes' or comments on your profile page
or photos. You may turn off those notifications with the options below.
</p>
<div class="field">
<label class="label">Notify me when...</label>
<label class="checkbox">
<input type="checkbox"
name="notif_optout_likes"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_likes") "true"}}checked{{end}}>
Somebody 'likes' my profile page, photos, or comments
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="notif_optout_comments"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_comments") "true"}}checked{{end}}>
Somebody leaves a comment on one of my photos
</label>
</div>
<h2 class="subtitle mt-5">Comment Thread Subscriptions</h2>
<p class="block">
Comment threads and forum posts may be 'subscribed' to so that you can be notified about
comments left by other people after you. By default, you will subscribe to comment threads
after you leave your first comment.
</p>
<p class="block">
<strong>Note:</strong> you may unsubscribe from comment threads by using the link at the
top of its page (for example: at the top of a forum thread page or the top of the list of
comments on a photo page). You may also opt <em>in</em> to get notifications on a thread
that you didn't comment on by using the same link at the top of their pages.
</p>
<p class="block">
The options below can control the automatic opt-in for subscriptions when you leave a
comment on a new comment thread.
</p>
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="notif_optout_subscriptions"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_subscriptions") "true"}}checked{{end}}>
Subscribe to notifications for future comments when I leave a comment on something
</label>
</div>
<div class="field">
<label class="label">Unsubscribe from Comment Threads</label>
<label class="checkbox">
<input type="checkbox"
name="unsubscribe_all_threads"
value="true">
Unsubscribe NOW from <strong>all ({{.SubscriptionCount}}) comment threads</strong> that I am currently following.
</label>
<p class="help">
You are currently subscribed to <strong>{{.SubscriptionCount}}</strong> comment thread{{Pluralize64 .SubscriptionCount}}.
You may immediately unsubscribe from all of these threads by checking this box and clicking "Save" below.
</p>
</div>
<h2 class="subtitle mt-5">Miscellaneous</h2>
<div class="field">
<label class="label">Notify me when...</label>
<label class="checkbox">
<input type="checkbox"
name="notif_optout_friend_request_accepted"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_friend_request_accepted") "true"}}checked{{end}}>
Somebody approves my friendship request
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox"
name="notif_optout_private_grant"
value="true"
{{if ne (.CurrentUser.GetProfileField "notif_optout_private_grant") "true"}}checked{{end}}>
Somebody unlocks their private photos for me to see
</label>
</div>
<!-- Read-only box for certification photo response -->
<div class="field">
<label class="checkbox">
<input type="checkbox"
value="true"
checked
disabled>
My certification photo is approved or rejected
</label>
<p class="help">
This notification is important for your account status, is rarely sent out, and can
not be opted-out from.
</p>
</div>
<div class="field">
<button type="submit" class="button is-primary">
<i class="fa fa-save mr-2"></i> Save Settings
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Account Settings -->
<div id="account">
<div class="card mb-5">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-lock pr-2"></i>
Two-Factor Authentication
</p>
</header>
<div class="card-content content">
<p>
To help protect your {{PrettyTitle}} account, you may opt-in to add a
second factor to your login ("Two-Factor Authentication", or 2FA). This
means that in addition to needing "something you know" (your password) to
log in to your account, you can also require "something you have" (an
authenticator device which generates random time-dependent codes).
</p>
<p>
{{PrettyTitle}} offers Two-Factor Authentication using the industry
standard "Time-based One-Time Password" (TOTP) system that is compatible
with Google Authenticator and Authy.
</p>
<p>
Your Two-Factor is currently:
{{if .TwoFactorEnabled}}
<i class="fa fa-check mr-1 has-text-success"></i>
<strong class="has-text-success">Enabled</strong>
{{else}}
<i class="fa fa-xmark mr-1 has-text-danger"></i>
<strong class="has-text-danger">Not Enabled</strong>
{{end}}
</p>
<p>
<a href="/account/two-factor/setup" class="button is-primary">
Manage Two-Factor Authentication
</a>
</p>
</div>
</div>
<div class="card mb-5">
<header class="card-header has-background-link">
<p class="card-header-title has-text-light">
<i class="fa fa-lock pr-2"></i>
Update E-mail, Username or Password
</p>
</header>
<div class="card-content">
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="settings">
{{InputCSRF}}
<div class="block">
You may use the fields below to change the e-mail address you log in with,
change your username on the site, or set a new password. You will need to
confirm your current password first before making these changes to your
account.
</div>
<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" for="change_username">Change Username</label>
<input type="text" class="input"
id="change_username"
name="change_username"
placeholder="{{.CurrentUser.Username}}"
value="{{.CurrentUser.Username}}"
{{if .OnChat}}readonly{{end}}>
<p class="help">
{{if .OnChat}}
<span class="has-text-danger">
<i class="fa fa-exclamation-triangle mr-1"></i>
You are currently logged into the chat room, so your username may not be
updated at this time. To change your username, please log out of the chat
room and wait a minute before reloading this page.
</span>
{{else}}
Usernames are 3 to 32 characters a-z 0-9 . -
{{end}}
</p>
</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">
<i class="fa fa-save mr-2"></i> Save Account Settings
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Deactivate or Delete Account -->
<div id="deactivate">
<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-lock pr-2"></i>
Deactivate My Account
</p>
</header>
<div class="card-content content">
<p>
If you'd like to take a break from {{PrettyTitle}} but think you may want to
come back later, you may <strong>temporarily deactivate your account</strong>
which will mark your profile as hidden from everywhere on the website (as if
it were deleted), but in a way that you can recover your account and reactivate
it again in the future.
</p>
<p>
<a href="/account/deactivate" class="button is-primary">
Temporarily Deactivate My Account
</a>
</p>
</div>
</div>
<!-- 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-exclamation-triangle pr-2"></i>
Permanently Delete My 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">
Permanently Delete My Account
</a>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
{{end}}
{{define "scripts"}}
<link rel="stylesheet" href="/static/js/openlayers-7.5.1/en/latest/ol/ol.css">
<script src="/static/js/openlayers-7.5.1/en/latest/ol/dist/ol.js"></script>
<script>
window.addEventListener("DOMContentLoaded", (event) => {
// The tabs
const $profile = document.querySelector("#profile"),
$look = document.querySelector("#look"),
$prefs = document.querySelector("#prefs"),
$location = document.querySelector("#location"),
$privacy = document.querySelector("#privacy"),
$notifications = document.querySelector("#notifications"),
$account = document.querySelector("#account")
$deactivate = document.querySelector("#deactivate"),
buttons = Array.from(document.getElementsByClassName("nonshy-tab-button"));
// Hide all by default.
$profile.style.display = 'none';
$look.style.display = 'none';
$prefs.style.display = 'none';
$location.style.display = 'none';
$privacy.style.display = 'none';
$notifications.style.display = 'none';
$account.style.display = 'none';
$deactivate.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(/\.$/, '');
let tabName = name.split('/')[0]; // "#profile/about_me"
if (!tabName) name = "profile";
$activeTab.style.display = 'none';
switch (tabName) {
case "look":
$activeTab = $look;
break;
case "prefs":
$activeTab = $prefs;
break;
case "location":
$activeTab = $location;
break;
case "privacy":
$activeTab = $privacy;
break;
case "notifications":
$activeTab = $notifications;
break;
case "account":
$activeTab = $account;
break;
case "deactivate":
$activeTab = $deactivate;
break;
default:
$activeTab = $profile;
}
// Update the is-active classes on all the tabs.
buttons.forEach(tab_ => {
let name_ = tab_.href.split("#").pop();
if (name !== name_) {
tab_.classList.remove("is-active");
} else {
tab_.classList.add("is-active");
}
tab_.addEventListener("click", (e) => {
document.querySelector("#"+name_).scrollIntoView();
});
});
$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);
if (screen.width >= 1024) {
e.preventDefault();
}
window.requestAnimationFrame(() => {
window.scrollTo(0, 0);
});
});
})
// Show the requested tab on first page load.
showTab(window.location.hash.replace(/^#/, ''));
// window.requestAnimationFrame(() => {
// window.scrollTo(0, 0);
// });
});
// Look & Feel tab scripts.
window.addEventListener("DOMContentLoaded", (event) => {
let $headerPreview = document.querySelector("#header-hero-preview"),
$colorA = document.querySelector("#hero-color-start"),
$colorB = document.querySelector("#hero-color-end"),
$darkText = document.querySelector("#hero-text-dark"),
$cardPreview = document.querySelector("#profile-card-preview"),
$cardLightness = document.querySelector("#card-lightness"),
$cardTitleBG = document.querySelector("#card-title-bg"),
$cardTitleFG = document.querySelector("#card-title-fg"),
$cardLinkColor = document.querySelector("#card-link-color");
function updatePreview() {
/* Hero banner preview */
let css = `linear-gradient(141deg, ${$colorA.value}, ${$colorB.value})`;
$headerPreview.style.backgroundImage = css;
if ($darkText.checked) {
$headerPreview.classList.remove("has-text-light");
$headerPreview.classList.add("has-text-dark-dark");
} else {
$headerPreview.classList.remove("has-text-dark-dark");
$headerPreview.classList.add("has-text-light");
}
/* Card style preview */
let $table = $cardPreview.querySelector("table"),
$header = $cardPreview.querySelector("div.card-header"),
$title = $cardPreview.querySelector("p.card-header-title"),
$link = $cardPreview.querySelector("a");
$header.style.backgroundColor = $cardTitleBG.value;
$title.style.color = $cardTitleFG.value;
$table.style.backgroundColor = "transparent";
$link.style.color = $cardLinkColor.value;
if ($cardLightness.value === "light") {
$table.style.backgroundColor = "#fff";
$table.style.color = "#4a4a4a";
} else if ($cardLightness.value === "dark") {
$table.style.backgroundColor = "#4a4a4a";
$table.style.color = "#f5f5f5";
}
/* Update <strong> tags in card style table */
($table.querySelectorAll("strong") || []).forEach(node => {
node.style.color = "";
if ($cardLightness.value === "light") {
node.style.color = "#4a4a4a";
} else if ($cardLightness.value === "dark") {
node.style.color = "#f5f5f5";
}
});
}
updatePreview();
([$colorA, $colorB, $darkText, $cardLightness, $cardTitleBG, $cardTitleFG, $cardLinkColor]).forEach(node => {
node.addEventListener("change", (e) => {
updatePreview();
});
});
});
// Notifications tab scripts.
window.addEventListener("DOMContentLoaded", (event) => {
// Get useful controls from the tab.
const $pushStatusGranted = document.querySelector("#push-status-enabled"),
$pushStatusDenied = document.querySelector("#push-status-disabled"),
$pushStatusDefault = document.querySelector("#push-status-default"),
$pushEnableButton = document.querySelector("#grant-push-permission"),
$pushDeniedHelp = document.querySelector("#push-denied-help");
// Is the Notification API unavailable?
if (typeof(window.Notification) === "undefined" || typeof(PushNotificationSubscribe) === "undefined") {
$pushStatusDefault.innerHTML = `<i class="fa fa-xmark mr-1"></i> Notification API unavailable`;
$pushStatusDefault.style.display = "";
return;
}
// And testing widely for errors...
try {
// Get the current permission status: default, granted, denied.
const showPermission = (permission) => {
$pushStatusGranted.style.display = "none";
$pushStatusDenied.style.display = "none";
$pushStatusDefault.style.display = "none";
switch (permission) {
case "granted":
$pushStatusGranted.style.display = "";
$pushEnableButton.innerHTML = "Test Push Notification";
break;
case "denied":
$pushEnableButton.style.display = "none";
$pushDeniedHelp.style.display = "";
$pushStatusDenied.style.display = "";
break;
default:
$pushStatusDefault.style.display = "";
$pushEnableButton.innerHTML = "Grant Push Notification Permission";
break;
}
};
showPermission(Notification.permission);
$pushEnableButton.addEventListener("click", (e) => {
e.preventDefault();
Notification.requestPermission().then(permission => {
// Update the displayed permission status.
showPermission(Notification.permission);
// If granted, subscribe to push notifications now.
if (permission === "granted") {
// In static/js/web-push.js
PushNotificationSubscribe();
// Test the notification now.
const notification = new Notification(`Hello from ${document.location.hostname}!`, {
body: "This is an example notification from this site.",
icon: "/static/img/favicon-192.png",
});
}
});
});
} catch(err) {
$pushStatusDefault.innerHTML = `<i class="fa fa-xmark mr-1"></i> Error: ${err}`;
$pushStatusDefault.style.display = "block";
}
});
// Location tab scripts.
window.addEventListener("DOMContentLoaded", (event) => {
// Get useful controls from the tab.
const $optionGPS = document.querySelector("#location-option-gps"),
$optionGeoIP = document.querySelector("#location-option-geoip"),
$optionPin = document.querySelector("#location-option-pin"),
$optionNone = document.querySelector("#location-option-none"),
$gpsCurrentStatus = document.querySelector("#location-status-gps"),
$gpsRefreshButton = document.querySelector("#gps-refresh"),
$saveLatitude = document.querySelector("#saveLatitude"),
$saveLongitude = document.querySelector("#saveLongitude");
// Function to massage coordinates for saving in the DB.
const saveCoords = (latitude, longitude) => {
$saveLatitude.value = latitude === null ? "" : latitude.toFixed(2);
$saveLongitude.value = longitude === null ? "" : longitude.toFixed(2);
};
// A lazy-initialized function to handle setting a new pin on the OpenStreetMap widget.
let setMapPin = null; // function(lonLat)
// The user's GeoIP coordinates, if available.
let geoIPInsights = {{ToJSON .GeoIPInsights}},
savedLocation = {{.UserLocation}};
// Default GPS coords to select on the map on page load:
// whatever we get from the backend DB.
let defaultCoords = [12.5, 41.9];
if (savedLocation.Latitude != 0 || savedLocation.Longitude != 0) {
// Prefer the DB saved coords.
defaultCoords = [
savedLocation.Longitude,
savedLocation.Latitude,
];
} else if (geoIPInsights.Latitude != 0 || geoIPInsights.Longitude != 0) {
// Then fall back on GeoIP coords.
defaultCoords = [
geoIPInsights.Longitude,
geoIPInsights.Latitude,
];
}
// Is geolocation not supported?
if (navigator.geolocation == undefined) {
$gpsCurrentStatus.className = "";
$gpsCurrentStatus.classList.add("has-text-danger");
$gpsCurrentStatus.innerHTML = "Geolocation is not supported by your browser.";
}
// Geolocation callback funcs.
const onSuccess = (pos) => {
const crd = pos.coords;
$gpsCurrentStatus.className = "";
$gpsCurrentStatus.classList.add("has-text-success-dark");
$gpsCurrentStatus.innerHTML = `Lat: ${crd.latitude}; Long: ${crd.longitude}; Accuracy: ${crd.accuracy}`;
$gpsRefreshButton.style.display = "";
// Set the form fields.
saveCoords(crd.latitude, crd.longitude);
// Can we drop a pin?
if (setMapPin !== null) {
setMapPin([crd.longitude, crd.latitude]);
}
};
const onError = (err) => {
$gpsCurrentStatus.className = "";
$gpsCurrentStatus.classList.add("has-text-danger");
$gpsCurrentStatus.innerHTML = `${err.message} (code ${err.code})`;
$gpsRefreshButton.style.display = "";
};
// Get or refresh their location.
const getLocation = () => {
if (navigator.geolocation != undefined) {
$gpsRefreshButton.style.display = "none";
$gpsCurrentStatus.className = "";
$gpsCurrentStatus.classList.add("has-text-info");
$gpsCurrentStatus.innerHTML = "Requesting your current location...";
navigator.geolocation.getCurrentPosition(onSuccess, onError, {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0,
});
}
};
// Set an onClick handler for the GPS location tab, to request
// permission from the user's browser.
$optionGPS.addEventListener("click", (e) => {
getLocation();
});
// If the page loaded w/ the GPS option on, ask right away.
if ($optionGPS.checked) {
$optionGPS.click();
}
// Wire the refresh button.
$gpsRefreshButton.addEventListener("click", (e) => {
e.preventDefault();
getLocation();
});
// If the user goes back to GeoIP coords, reinitialize the defaults.
$optionGeoIP.addEventListener("click", (e) => {
// Set the form fields.
// NOTE: defaultCoords are [lon, lat] we want [lat, lon]
if (geoIPInsights.Latitude != 0 || geoIPInsights.Longitude != 0) {
defaultCoords = [
geoIPInsights.Longitude,
geoIPInsights.Latitude,
];
}
saveCoords(defaultCoords[1], defaultCoords[0]);
if (setMapPin !== null) {
setMapPin(defaultCoords);
}
});
// If the user disables their geolocation.
$optionNone.addEventListener("click", (e) => {
saveCoords(null, null);
});
/***
* OpenLayers Map Widget
***/
let vectorSource = new ol.source.Vector(),
vectorLayer = new ol.layer.Vector({
source: vectorSource,
});
let map = new ol.Map({
target: 'map', // the <div id="map"> target
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer
],
// The view allows to specify the center, resolution, and rotation of the map.
view: new ol.View({
center: ol.proj.fromLonLat(defaultCoords),
zoom: 10
})
});
// Define the function to drop a pin.
let onMapClick = (coordinate, { center=true }) => {
vectorSource.clear();
let feature = new ol.Feature(new ol.geom.Point(coordinate));
vectorSource.addFeatures([feature]);
let prettyCoord = ol.coordinate.toStringHDMS(
ol.proj.transform(coordinate, 'EPSG:3857', 'EPSG:4326'),
2,
);
// Center the map on the pin.
if (center) {
map.setView(new ol.View({
center: coordinate,
zoom: 10
}));
}
};
setMapPin = (lonLat) => {
let center = ol.proj.fromLonLat(lonLat);
onMapClick(ol.proj.fromLonLat(lonLat), {center: true});
};
map.on('click', (e) => {
// Do not drop a pin if the option isn't set.
if (!$optionPin.checked) return;
// Save the selected coords to the form.
let center = ol.proj.toLonLat(e.coordinate);
saveCoords(center[1], center[0]);
onMapClick(e.coordinate, {center: false});
});
// Drop the initial pin at the default coords.
setMapPin(defaultCoords);
});
</script>
<style type="text/css">
/* Ugly hack */
div.hero-body figure.profile-photo {
padding: 2px !important;
}
div.hero-body figure.is-inline-block {
margin-bottom: -6px !important;
}
</style>
{{end}}