website/web/templates/account/settings.html

1090 lines
51 KiB
HTML

{{define "title"}}User Settings{{end}}
{{define "content"}}
<div class="container">
<section class="hero is-info 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">
<p class="menu-label">
Settings
</p>
<ul class="menu-list block">
<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#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#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 block">
<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>
</ul>
</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">
<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">
<i class="fa fa-save mr-2"></i> Save Profile Settings
</button>
</div>
</div>
</form>
</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-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 mb-0">Who can send me the first <i class="fa fa-envelope"></i> Message?</label>
<div class="has-text-info 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>
<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>
<!-- 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-dark"></i>
<strong class="has-text-success-dark">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 or Password
</p>
</header>
<div class="card-content">
<form method="POST" action="/settings">
<input type="hidden" name="intent" value="settings">
{{InputCSRF}}
<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">
<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"),
$prefs = document.querySelector("#prefs"),
$location = document.querySelector("#location"),
$privacy = document.querySelector("#privacy"),
$account = document.querySelector("#account")
$deactivate = document.querySelector("#deactivate"),
buttons = Array.from(document.getElementsByClassName("nonshy-tab-button"));
// Hide all by default.
$profile.style.display = 'none';
$prefs.style.display = 'none';
$location.style.display = 'none';
$privacy.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(/\.$/, '');
if (!name) name = "profile";
$activeTab.style.display = 'none';
switch (name) {
case "prefs":
$activeTab = $prefs;
break;
case "location":
$activeTab = $location;
break;
case "privacy":
$activeTab = $privacy;
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_) {
console.log("button: remove is-active", tab_);
tab_.classList.remove("is-active");
} else {
console.log("button %s: ADD is-active", tab_);
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);
e.preventDefault();
});
})
// Show the requested tab on first page load.
showTab(window.location.hash.replace(/^#/, ''));
});
// 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,
);
console.log("COORDINATE: " + prettyCoord);
// 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>
{{end}}