website/web/templates/account/search.html
2024-10-19 13:07:17 -07:00

577 lines
28 KiB
HTML

{{define "title"}}People{{end}}
{{define "content"}}
<div class="container">
{{$Root := .}}
<section class="hero is-link is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">
{{if eq .Sort "distance"}}
<i class="fa fa-location-dot mr-2"></i>
Who's Nearby
{{else}}
<i class="fa fa-people-group mr-2"></i>
People
{{end}}
</h1>
<h2 class="subtitle">Member Directory</h2>
</div>
</div>
</section>
<form action="/members" method="GET">
<div class="p-4">
<div class="columns">
<div class="column">
Found {{FormatNumberCommas .Pager.Total}} user{{Pluralize64 .Pager.Total}}
(page {{.Pager.Page}} of {{.Pager.Pages}}).
</div>
</div>
{{if .FriendSearch}}
<div class="notification is-success is-light">
Currently searching within your <i class="fa fa-user-group"></i> Friends list.
</div>
{{else if eq .Sort "distance"}}
<div class="notification is-success is-light content">
<p>
Showing you <i class="fa fa-location-dot mr-1"></i> <strong>Who's Nearby.</strong>
</p>
<!-- Was it a manual city search? -->
{{if .City}}
<!-- If the current user has no location set, lead the way. -->
{{if not .MyLocation.Source}}
<p>
You will need to <a href="/settings#location">set your own location</a> first before you can search for other members by
their location. It's only fair!
</p>
{{else}}
<p>
You are searching for profiles near {{.City}}.
</p>
{{end}}
{{else}}
<!-- Show options to refresh their location -->
<p>
{{if eq .MyLocation.Source "geoip"}}
You had set your location to update automatically by your IP address <small>(currently {{.GeoIPInsights.Short}}).</small>
<a href="/settings#location">Change your location settings?</a>
{{else if eq .MyLocation.Source "gps"}}
You once set your location via your device's GPS coordinates.
<a href="/settings#location" id="gpsRefresh">Refresh to your current location now?</a>
{{else if eq .MyLocation.Source "pin"}}
You had set your location by dropping a pin on a map.
<a href="/settings#location">Update your location?</a>
{{else if eq .MyLocation.Source ""}}
You will need to <a href="/settings#location">set your location</a> first before we can sort people by distance from you.
{{end}}
</p>
{{end}}
</div>
{{else if eq .Certified "shy"}}
<div class="notification is-success is-light content">
<p>
Showing you <i class="fa fa-ghost"></i> <strong>Shy Accounts</strong>. These profiles may be marked as private or
have no photo available to you, as all of theirs are set to 'friends-only' or 'private.'
</p>
<p>
Shy Accounts are limited in the way they can interact with our non-shy members.
<a href="/faq#shy-faqs" target="_blank">Learn more <i class="fa fa-external-link"></i></a>
</p>
</div>
{{end}}
<!-- Restricted search terms -->
{{if .RestrictedSearchError}}
<div class="notification is-danger is-light content">
<p>
<i class="fa fa-hand mr-1"></i>
<strong>Child sexual abuse is illegal</strong>
</p>
<p>
We think that your search might be associated with child sexual abuse. Child sexual abuse or seeking
sexual imagery or contact with children can lead to imprisonment and other severe personal consequences,
and this abuse causes extreme harm to children and searching and viewing such material adds to that harm.
</p>
<p>
It is also against the <a href="/tos#child-exploitation">Terms of Service</a> of this website, and
members who violate this rule will be banned. <strong>This website is actively monitored</strong> to keep on top of this stuff,
and we cooperate enthusiastically with
<a href="https://www.missingkids.org/" title="National Center for Missing and Exploited Children">NCMEC</a>
and relevant law enforcement agencies.
</p>
<p>
This incident has been reported to the website administrator. If you are surprised to see this message and
it was on accident, don't worry -- but <strong>repeated attempts to bypass this search filter</strong> by trying
other related keywords <strong>will be noticed</strong> and may attract extra admin attention to your account.
</p>
</div>
{{end}}
<div class="block">
<div class="card nonshy-collapsible-mobile">
<header class="card-header has-background-link-light">
<p class="card-header-title has-text-dark">
Search Filters
</p>
<button class="card-header-icon" type="button">
<span class="icon">
<i class="fa fa-angle-up"></i>
</span>
</button>
</header>
<div class="card-content">
<div class="columns">
<div class="column pr-1">
<div class="field">
<label class="label" for="certified">Status:</label>
<div class="select is-fullwidth">
<select id="certified" name="certified" id="certified">
<optgroup label="Certification Status">
<option value="true">All certified members</option>
{{if .CurrentUser.IsAdmin}}
<option value="false"{{if eq $Root.Certified "false"}} selected{{end}}>☮ Non-certified only</option>
<option value="all"{{if eq $Root.Certified "all"}} selected{{end}}>☮ Show all users</option>
{{end}}
</optgroup>
<optgroup label="Profile Status">
<option value="shy"{{if eq $Root.Certified "shy"}} selected{{end}}>Shy Accounts</option>
<option value="admin"{{if eq $Root.Certified "admin"}} selected{{end}}>Website administrators</option>
</optgroup>
{{if .CurrentUser.HasAdminScope "admin.user.ban"}}
<optgroup label="Admin Options">
<option value="banned"{{if eq $Root.Certified "banned"}} selected{{end}}>☮ Banned</option>
<option value="disabled"{{if eq $Root.Certified "disabled"}} selected{{end}}>☮ Disabled</option>
</optgroup>
{{end}}
</select>
</div>
</div>
</div>
<div class="column px-1">
<div class="field">
<label class="label" for="name">Name or username:</label>
<input type="text" class="input"
name="name" id="name"
autocomplete="off"
value="{{$Root.EmailOrUsername}}">
</div>
</div>
<div class="column px-1">
<div class="field">
<label class="label" for="search">Profile text:</label>
<input type="text" class="input"
name="search" id="search"
autocomplete="off"
value="{{$Root.Search}}">
<p class="help">
Tip: you can <span class="has-text-success">"quote exact phrases"</span> and
<span class="has-text-success">-exclude</span> words (or
<span class="has-text-success">-"exclude phrases"</span>) from your search.
</p>
</div>
</div>
<div class="column pl-1">
<div class="field">
<label class="label" for="wcs">Location:</label>
<input type="text" class="input"
name="wcs" id="wcs"
autocomplete="off"
value="{{$Root.City}}">
</div>
</div>
</div>
<div class="columns is-centered">
<div class="column pr-1">
<div class="field">
<label class="label">Age:</label>
<div class="columns is-mobile is-gapless">
<div class="column">
<div class="select is-fullwidth">
<select name="age_min">
<option value="">Min</option>
{{range IterRange 18 120}}
<option value="{{.}}"{{if eq $Root.AgeMin .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
<div class="column">
<div class="select is-fullwidth">
<select name="age_max">
<option value="">Max</option>
{{range IterRange 18 120}}
<option value="{{.}}"{{if eq $Root.AgeMax .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
</div>
</div>
<div class="column px-1">
<div class="field">
<label class="label" for="gender">Gender:</label>
<div class="select is-fullwidth">
<select id="gender" name="gender">
<option value=""></option>
{{range .Enum.Gender}}
<option value="{{.}}"{{if eq $Root.Gender .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
<div class="column px-1">
<div class="field">
<label class="label" for="orientation">Orientation:</label>
<div class="select is-fullwidth">
<select id="orientation" name="orientation">
<option value=""></option>
{{range .Enum.Orientation}}
<option value="{{.}}"{{if eq $Root.Orientation .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
<div class="column pl-1">
<div class="field">
<label class="label" for="marital_status">Relationship:</label>
<div class="select is-fullwidth">
<select id="marital_status" name="marital_status">
<option value=""></option>
{{range .Enum.MaritalStatus}}
<option value="{{.}}"{{if eq $Root.MaritalStatus .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
</div>
<div class="columns is-centered">
<div class="column pr-1">
<div class="field">
<label class="label" for="here_for">Here for:</label>
<div class="select is-fullwidth">
<select id="here_for" name="here_for">
<option value=""></option>
{{range .Enum.HereFor}}
<option value="{{.}}"{{if eq $Root.HereFor .}} selected{{end}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
<div class="column px-1">
<div class="field">
<label class="label" for="friends">Miscellanous:</label>
<label class="checkbox">
<input type="checkbox"
name="friends"
id="friends"
value="true"
{{if .FriendSearch}}checked{{end}}>
Show only my friends
</label>
<label class="checkbox">
<input type="checkbox"
name="liked"
id="liked"
value="true"
{{if .LikedSearch}}checked{{end}}>
Show only my "Likes"
</label>
<label class="checkbox">
<input type="checkbox"
name="on_chat"
id="on_chat"
value="true"
{{if .OnChatSearch}}checked{{end}}>
Currently on chat <span class="tag is-success">New!</span>
</label>
</div>
</div>
<div class="column px-1">
<div class="field">
<label class="label" for="sort">Sort by:</label>
<div class="select is-fullwidth">
<select id="sort" name="sort">
<option value="last_login_at desc"{{if eq .Sort "last_login_at desc"}} selected{{end}}>Last login</option>
<option value="created_at desc"{{if eq .Sort "created_at desc"}} selected{{end}}>Signup date</option>
<option value="certified_at desc"{{if eq .Sort "certification_photos.updated_at desc"}} selected{{end}}>Certified date</option>
<option value="username"{{if eq .Sort "username"}} selected{{end}}>Username (a-z)</option>
<option value="username desc"{{if eq .Sort "username desc"}} selected{{end}}>Username (z-a)</option>
<option value="lower(name)"{{if eq .Sort "lower(name)"}} selected{{end}}>Name (a-z)</option>
<option value="lower(name) desc"{{if eq .Sort "lower(name) desc"}} selected{{end}}>Name (z-a)</option>
<option value="distance"{{if eq .Sort "distance"}} selected{{end}}>Distance to me</option>
</select>
</div>
</div>
</div>
<div class="column pl-1 has-text-right">
<a href="/members" class="button">Reset</a>
<button type="submit" class="button is-success">
<span>Search</span>
<span class="icon"><i class="fa fa-search"></i></span>
</button>
</div>
</div>
</div>
</div>
</div>
{{SimplePager .Pager}}
<div class="columns is-multiline">
{{range .Users}}
<div class="column is-half-tablet is-one-third-desktop">
<div class="card">
<div class="card-content">
<div class="media block">
<div class="media-left">
{{template "avatar-64x64" .}}
<!-- Friendship badge -->
{{if $Root.FriendMap.Get .ID}}
<div>
<span class="is-size-7 has-text-warning">
<i class="fa fa-user-group" title="Friends"></i>
Friends
</span>
</div>
{{end}}
<!-- Liked badge -->
{{$LikedStats := $Root.LikedMap.Get .ID}}
{{if $LikedStats.UserLikes}}
<div>
<span class="is-size-7">
<i class="fa fa-heart has-text-danger" title="Friends"></i>
Liked
</span>
</div>
{{end}}
</div>
<div class="media-content">
<p class="title is-4">
<a href="/u/{{.Username}}" class="has-text-dark">
{{if ne .Status "active"}}
<del>{{.NameOrUsername}}</del>
{{else}}
{{.NameOrUsername}}
{{end}}
</a>
{{if eq .Visibility "private"}}
<sup class="fa fa-mask is-size-7" title="Private Profile"></sup>
{{end}}
</p>
<p class="subtitle is-6 mb-2">
<span class="icon"><i class="fa fa-user"></i></span>
<a href="/u/{{.Username}}">{{.Username}}</a>
<!-- Not Certified or Shy Account badge -->
{{if not .Certified}}
<span class="has-text-danger is-size-7">
<i class="fa fa-certificate"></i>
<span>Not Certified!</span>
</span>
{{else if $Root.ShyMap.Get .ID}}
<span class="has-text-danger is-size-7">
<i class="fa fa-ghost"></i>
<span>Shy Account</span>
</span>
{{end}}
<!-- "(banned)" label -->
{{if ne .Status "active"}}<small>({{.Status}})</small>{{end}}
{{if .IsAdmin}}
<span class="tag is-danger is-light p-1" style="font-size: x-small">
<i class="fa fa-peace mr-1"></i>
<span>Admin</span>
</span>
{{end}}
<!-- Photo count pulled to the right -->
<a href="/u/{{.Username}}/photos" class="tag is-info is-light is-pulled-right">
<i class="fa fa-camera mr-2"></i>
{{$Root.PhotoCountMap.Get .ID}}
</a>
</p>
{{if .GetProfileField "city"}}
<p class="subtitle is-6 mb-2">
{{.GetProfileField "city"}}
</p>
{{end}}
<p class="subtitle is-7 mb-2">
{{if or (ne .GetDisplayAge "n/a")}}
<span class="mr-2">{{.GetDisplayAge}}</span>
{{end}}
{{if .GetProfileField "gender"}}
<span class="mr-2">{{.GetProfileField "gender"}}</span>
{{end}}
{{if .GetProfileField "pronouns"}}
<span class="mr-2">{{.GetProfileField "pronouns"}}</span>
{{end}}
{{if .GetProfileField "orientation"}}
<span class="mr-2">{{.GetProfileField "orientation"}}</span>
{{end}}
<!-- Chat room status -->
{{if $Root.UserOnChatMap.Get .Username}}
<div>
<span class="tag is-success is-light">
<i class="fa fa-user mr-2"></i>
Currently on chat!
</span>
</div>
{{end}}
<!-- Show a subfooter based on ordered by -->
{{if eq $Root.Sort "last_login_at desc"}}
<div>
<small>
Last logged in:
<span title="On {{.LastLoginAt.Format "Jan _2 2006 15:04:05 MST"}}">
{{SincePrettyCoarse .LastLoginAt}} ago
</span>
</small>
</div>
{{end}}
{{if or (eq $Root.Sort "created_at desc") (eq $Root.Sort "certification_photos.updated_at desc")}}
<div>
<small>
Member since:
<span title="On {{.CreatedAt.Format "Jan _2 2006 15:04:05 MST"}}">
{{SincePrettyCoarse .CreatedAt}} ago
</span>
</small>
</div>
{{end}}
<!-- Ordered by distance? -->
{{if eq $Root.Sort "distance"}}
<div>
{{$Root.DistanceMap.Get .ID}} away
</div>
{{end}}
</p>
</div>
</div><!-- media-block -->
</div>
</div>
</div>
{{end}}<!-- range .Friends -->
</div>
{{SimplePager .Pager}}
</div>
</form>
</div>
{{end}}
{{define "scripts"}}
<!-- Typeahead search for City -->
<script type="text/javascript" src="/static/js/jquery-3.7.1.min.js"></script>
<script type="text/javascript" src="/static/js/typeahead.bundle.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', (e) => {
let backend = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/v1/world-cities?query=%QUERY',
wildcard: '%QUERY'
},
});
let $searchField = $("#wcs");
$searchField.typeahead({
autoselect: true
}, {
items: 4,
name: 'location',
displayKey: 'Canonical',
source: backend,
});
});
</script>
<style type="text/css">
/*****************************
* Twitter Typeahead Styling *
*****************************/
.twitter-typeahead {
width: 100%;
}
.tt-hint {
color: #999;
}
.tt-menu {
width: 300px;
margin-top: 4px;
padding: 4px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
}
.tt-suggestion {
padding: 3px 20px;
line-height: 24px;
color: #000;
}
.tt-suggestion:hover {
background-color: #ddd;
}
.tt-suggestion p {
margin: 0;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #0097cf;
}
</style>
{{end}}