Added search to tags.

This commit is contained in:
DebaucheryLibrarian 2021-08-22 03:14:02 +02:00
parent 959b5d9d0e
commit eb1f8f86fd
8 changed files with 311 additions and 240 deletions

View File

@ -7,48 +7,9 @@
ref="filters"
class="filters"
>
<div class="filters-row">
<ul class="genders nolist">
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'all', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'all' }"
class="gender-link all"
>all</router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'female', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'female' }"
class="gender-link female"
><Gender gender="female" /></router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'male', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'male' }"
class="gender-link male"
replace
><Gender gender="male" /></router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'trans', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'trans' }"
class="gender-link transsexual"
replace
><Gender gender="transsexual" /></router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'other', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'other' }"
class="gender-link other"
replace
><Icon icon="question5" /></router-link>
</li>
</ul>
<SearchBar :placeholder="`Search ${totalCount} actors`" />
<div class="filters-row">
<ul class="nolist">
<li>
<Tooltip class="filter boobs">
@ -195,9 +156,48 @@
</Tooltip>
</li>
</ul>
</div>
<SearchBar :placeholder="`Search ${totalCount} actors`" />
<ul class="genders nolist">
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'all', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'all' }"
class="gender-link all"
>all</router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'female', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'female' }"
class="gender-link female"
><Gender gender="female" /></router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'male', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'male' }"
class="gender-link male"
replace
><Gender gender="male" /></router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'trans', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'trans' }"
class="gender-link transsexual"
replace
><Gender gender="transsexual" /></router-link>
</li>
<li class="gender">
<router-link
:to="{ name: 'actors', params: { gender: 'other', letter, pageNumber: 1 } }"
:class="{ selected: gender === 'other' }"
class="gender-link other"
replace
><Icon icon="question5" /></router-link>
</li>
</ul>
</div>
</nav>
<div class="tiles">
@ -291,6 +291,7 @@ async function fetchActors(scroll) {
async function searchActors(scroll) {
const actors = await this.$store.dispatch('searchActors', {
minLength: 1,
query: this.$route.query.query,
limit: this.limit,
});
@ -419,7 +420,6 @@ export default {
.search {
width: 0;
flex-grow: 1;
justify-content: flex-end;
box-sizing: border-box;
padding: 0 1rem;
}
@ -427,7 +427,7 @@ export default {
.filters,
.filters-row {
display: flex;
justify-content: center;
justify-content: flex-end;
align-items: center;
}
@ -435,6 +435,10 @@ export default {
margin: 1rem 0 .5rem 0;
}
.filters-row {
flex-grow: 1;
}
.filters-row,
.filter {
padding: 0 1rem;
@ -443,7 +447,7 @@ export default {
.genders {
display: flex;
flex-shrink: 0;
padding: 0 .5rem 0 0;
padding: 0 0 0 .5rem;
}
.letter,
@ -686,7 +690,7 @@ export default {
@media(max-width: $breakpoint-kilo) {
.filters {
flex-direction: column-reverse;
flex-direction: column;
}
::v-deep(.search) {
@ -698,7 +702,7 @@ export default {
@media(max-width: $breakpoint) {
.filters-row {
flex-direction: column;
flex-direction: column-reverse;
}
.genders {

View File

@ -16,7 +16,7 @@
<li class="nav-item">
<router-link
v-slot="{ href, isActive, navigate }"
to="/actors"
:to="{ name: 'actors', params: { pageNumber: 1 } }"
custom
>
<a
@ -31,7 +31,7 @@
<li class="nav-item">
<router-link
v-slot="{ href, isActive, navigate }"
to="/channels"
:to="{ name: 'channels' }"
custom
>
<a
@ -46,7 +46,7 @@
<li class="nav-item">
<router-link
v-slot="{ href, isActive, navigate }"
to="/tags"
:to="{ name: 'tags' }"
custom
>
<a
@ -61,7 +61,7 @@
<li class="nav-item">
<router-link
v-slot="{ href, isActive, navigate }"
to="/movies"
:to="{ name: 'movies', params: { range: 'latest', pageNumber: 1 } }"
custom
>
<a

View File

@ -1,22 +1,26 @@
<template>
<div class="tags">
<div
v-for="(tags, category) in categories"
:key="category"
class="category"
>
<h3 class="heading">{{ category }}</h3>
<div class="content-inner">
<SearchBar :placeholder="'Search tags'" />
<div
:key="sfw"
class="tiles"
v-for="(tags, category) in categories"
:key="category"
class="category"
>
<Tag
v-for="tag in tags"
:key="`tag-${tag.id}`"
:tag="tag"
:lazy="true"
/>
<h3 class="heading">{{ category }}</h3>
<div
:key="sfw"
class="tiles"
>
<Tag
v-for="tag in tags"
:key="`tag-${tag.id}`"
:tag="tag"
:lazy="true"
/>
</div>
</div>
</div>
@ -26,122 +30,128 @@
<script>
import Tag from './tile.vue';
import SearchBar from '../search/bar.vue';
const tagSlugsByCategory = {
popular: [
'anal',
'lesbian',
'interracial',
'mff',
'mfm',
'teen',
'milf',
'blowjob',
'dp',
'gangbang',
'facial',
'creampie',
'squirting',
],
appearance: [
'asian',
'black',
'latina',
'white',
'natural-boobs',
'enhanced-boobs',
'blonde',
'brunette',
'redhead',
'tattoos',
'piercings',
],
oral: [
'blowjob',
'pussy-eating',
'ass-eating',
'69',
'double-blowjob',
'deepthroat',
'facefucking',
'atm',
],
manual: [
'handjob',
'fingering',
'anal-fingering',
'titty-fucking',
'fisting',
'anal-fisting',
'fisting-dp',
],
group: [
'mfm',
'mff',
'orgy',
'gangbang',
'blowbang',
],
cumshot: [
'facial',
'bukkake',
'creampie',
'cum-in-mouth',
'cum-on-boobs',
'cum-on-butt',
'cum-on-pussy',
'anal-creampie',
'oral-creampie',
'fake-cum',
],
toys: [
'toys',
'toy-anal',
'toy-dp',
'double-dildo',
'double-dildo-blowjob',
'double-dildo-kiss',
'double-dildo-anal',
'double-dildo-dp',
],
roleplay: [
'family',
'parody',
'schoolgirl',
'nurse',
'maid',
'nun',
],
fetish: [
'bdsm',
'femdom',
'bondage',
'latex',
'blindfold',
],
extreme: [
'dp',
'airtight',
'dap',
'dvp',
'da-tp',
'dv-tp',
'tap',
'tvp',
],
misc: [
'gaping',
'squirting',
'oil',
'vr',
'bts',
],
};
function sfw() {
return this.$store.state.ui.sfw;
}
async function mounted() {
const tagSlugsByCategory = {
popular: [
'anal',
'lesbian',
'interracial',
'mff',
'mfm',
'teen',
'milf',
'blowjob',
'dp',
'gangbang',
'facial',
'creampie',
'squirting',
],
appearance: [
'asian',
'black',
'latina',
'white',
'natural-boobs',
'enhanced-boobs',
'blonde',
'brunette',
'redhead',
'tattoos',
'piercings',
],
oral: [
'blowjob',
'pussy-eating',
'ass-eating',
'69',
'double-blowjob',
'deepthroat',
'facefucking',
'atm',
],
manual: [
'handjob',
'fingering',
'anal-fingering',
'titty-fucking',
'fisting',
'anal-fisting',
'fisting-dp',
],
group: [
'mfm',
'mff',
'orgy',
'gangbang',
'blowbang',
],
cumshot: [
'facial',
'bukkake',
'creampie',
'cum-in-mouth',
'cum-on-boobs',
'cum-on-butt',
'cum-on-pussy',
'anal-creampie',
'oral-creampie',
'fake-cum',
],
toys: [
'toys',
'toy-anal',
'toy-dp',
'double-dildo',
'double-dildo-blowjob',
'double-dildo-kiss',
'double-dildo-anal',
'double-dildo-dp',
],
roleplay: [
'family',
'parody',
'schoolgirl',
'nurse',
'maid',
'nun',
],
fetish: [
'bdsm',
'femdom',
'bondage',
'latex',
'blindfold',
],
extreme: [
'dp',
'airtight',
'dap',
'dvp',
'da-tp',
'dv-tp',
'tap',
'tvp',
],
misc: [
'gaping',
'squirting',
'oil',
'vr',
'bts',
],
};
async function fetchTags() {
if (this.$route.query.query) {
await this.searchTags();
return;
}
const tags = await this.$store.dispatch('fetchTags', {
slugs: Object.values(tagSlugsByCategory).flat(),
@ -153,13 +163,30 @@ async function mounted() {
...acc,
[category]: tagSlugs.map(tagSlug => tagsBySlug[tagSlug]),
}), {});
}
async function searchTags() {
const tags = await this.$store.dispatch('searchTags', {
minLength: 1,
query: this.$route.query.query,
limit: 20,
});
this.categories = {
results: tags,
};
}
async function mounted() {
this.pageTitle = 'Tags';
await this.fetchTags();
}
export default {
components: {
Tag,
SearchBar,
},
data() {
return {
@ -170,7 +197,14 @@ export default {
computed: {
sfw,
},
watch: {
$route: fetchTags,
},
mounted,
methods: {
fetchTags,
searchTags,
},
};
</script>
@ -178,6 +212,12 @@ export default {
@import 'theme';
.tags {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.content-inner {
padding: 1rem 1rem 0 1rem;
}
@ -191,7 +231,7 @@ export default {
.heading {
text-transform: capitalize;
padding: 0 0 0 .5rem;
margin: 2rem 0 1rem 0;
margin: 1.75rem 0 1rem 0;
}
.category:first-child .heading {

View File

@ -1,55 +1,62 @@
<template>
<div
v-if="tag.poster"
class="tile"
>
<router-link
:to="`/tag/${tag.slug}`"
:title="tag.name"
:class="{ blank: !tag.poster }"
class="poster-link"
>
<img
v-if="!lazy && !sfw"
:src="`/img/${tag.poster.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.lazy})` }"
:title="comment"
:alt="tag.name"
class="poster"
>
<template v-if="tag.poster">
<img
v-if="!lazy && !sfw"
:src="`/img/${tag.poster.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.lazy})` }"
:title="comment"
:alt="tag.name"
class="poster"
>
<img
v-if="!lazy && sfw"
:src="`/img/${tag.poster.sfw.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.sfw.lazy})` }"
:title="tag.poster.sfw.comment"
:alt="tag.name"
class="poster"
>
<img
v-if="!lazy && sfw"
:src="`/img/${tag.poster.sfw.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.sfw.lazy})` }"
:title="tag.poster.sfw.comment"
:alt="tag.name"
class="poster"
>
<img
v-if="lazy && !sfw"
:src="`/img/${tag.poster.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.lazy})` }"
:title="comment"
:alt="tag.name"
loading="lazy"
class="poster"
>
<img
v-if="lazy && !sfw"
:src="`/img/${tag.poster.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.lazy})` }"
:title="comment"
:alt="tag.name"
loading="lazy"
class="poster"
>
<img
v-if="lazy && sfw"
:src="`/img/${tag.poster.sfw.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.sfw.lazy})` }"
:title="tag.poster.sfw.comment"
:alt="tag.name"
loading="lazy"
class="poster"
>
<img
v-if="lazy && sfw"
:src="`/img/${tag.poster.sfw.thumbnail}`"
:style="{ 'background-image': `url(/img/${tag.poster.sfw.lazy})` }"
:title="tag.poster.sfw.comment"
:alt="tag.name"
loading="lazy"
class="poster"
>
<Logo
v-if="!sfw"
:photo="tag.poster"
favicon
<Logo
v-if="!sfw"
:photo="tag.poster"
favicon
/>
</template>
<Icon
v-else
icon="price-tag2"
/>
</router-link>
@ -59,11 +66,6 @@
:title="tag.name"
>{{ tag.name }}</router-link>
</div>
<span
v-else
class="title"
>{{ tag.name }}</span>
</template>
<script>
@ -100,13 +102,16 @@ export default {
<style lang="scss" scoped>
.tile {
display: flex;
flex-direction: column;
box-sizing: border-box;
position: relative;
text-decoration: none;
font-size: 0;
&:hover {
.poster {
.poster,
.blank {
box-shadow: 0 0 2px var(--darken);
}
@ -119,15 +124,30 @@ export default {
.poster {
display: inline-block;
width: 100%;
height: 13.5rem;
object-fit: cover;
background-size: cover;
background-position: center;
box-shadow: 0 0 3px var(--darken-weak);
}
.poster,
.blank {
height: 13.5rem;
}
.poster-link {
display: flex;
align-items: center;
justify-content: center;
position: relative;
flex-grow: 1;
background: var(--shadow-hint);
.icon {
width: 2rem;
height: 2rem;
fill: var(--shadow-weak);
}
}
.title {

View File

@ -437,14 +437,16 @@ function initActorActions(store, router) {
search: $query,
minLength: $minLength
first: $limit
orderBy: NAME_ASC
) {
id
name
slug
age
dateOfBirth
ageFromBirth
ageAtDeath
dateOfBirth
dateOfDeath
dateOfBirth
dateOfDeath
gender
aliasFor: actorByAliasFor {
id

View File

@ -276,7 +276,7 @@ function initTagsActions(store, _router) {
$minLength: Int = 2
) {
tags: searchTags(
search: $query,
query: $query,
first: $limit
minLength: $minLength
) {

View File

@ -1396,6 +1396,8 @@ exports.up = knex => Promise.resolve()
.then(() => { // eslint-disable-line arrow-body-style
// allow vim fold
return knex.raw(`
CREATE EXTENSION pg_trgm;
CREATE FUNCTION current_user_id() RETURNS INTEGER AS $$
/* if the user ID is undefined, the adapter will pass it as a string, which cannot be cast as NULL by ::integer */
SELECT NULLIF(current_setting('user.id', true), '')::integer;
@ -1437,18 +1439,20 @@ exports.up = knex => Promise.resolve()
CREATE FUNCTION search_actors(search text, min_length smallint DEFAULT 2) RETURNS SETOF actors AS $$
SELECT * FROM actors
WHERE length(search) >= min_length
AND name ILIKE ('%' || TRIM(search) || '%')
AND CASE
WHEN length(search) > 1
THEN name ILIKE ('%' || TRIM(search) || '%')
ELSE name ILIKE (TRIM(search) || '%')
END
$$ LANGUAGE SQL STABLE;
CREATE FUNCTION search_tags(search text, min_length smallint DEFAULT 2, is_primary boolean DEFAULT true) RETURNS SETOF tags AS $$
SELECT * FROM tags
WHERE length(search) >= min_length
AND name ILIKE ('%' || TRIM(search) || '%')
AND CASE
WHEN is_primary
THEN tags.alias_for IS NULL
ELSE true
END
CREATE FUNCTION search_tags(query text, min_length smallint DEFAULT 2) RETURNS SETOF tags AS $$
SELECT aliases.* FROM tags
LEFT JOIN tags AS aliases ON aliases.id = tags.alias_for OR (tags.alias_for IS NULL AND aliases.id = tags.id)
WHERE length(query) >= min_length
AND tags.name ILIKE ('%' || TRIM(query) || '%')
GROUP BY aliases.id
ORDER BY similarity(aliases.slug, query) DESC, slug ASC
$$ LANGUAGE SQL STABLE;
CREATE FUNCTION actors_tags(actor actors, selectable_tags text[]) RETURNS SETOF tags AS $$
@ -1822,6 +1826,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
DROP FUNCTION IF EXISTS search_sites;
DROP FUNCTION IF EXISTS search_entities;
DROP FUNCTION IF EXISTS search_actors;
DROP FUNCTION IF EXISTS search_movies;
DROP FUNCTION IF EXISTS search_tags;
DROP FUNCTION IF EXISTS get_random_sfw_media_id;

View File

@ -977,14 +977,14 @@ const tags = [
name: 'DA triple penetration',
slug: 'da-tp',
priority: 7,
description: 'Triple penetration with [two cocks in your ass](/tag/dap), and one in your pussy. Also see [double vaginal triple penetration](/tag/dv-tp).',
description: '[Triple penetration](/tag/triple-penetration) with [two cocks in your ass](/tag/dap), and one in your pussy. Also see [double vaginal triple penetration](/tag/dv-tp).',
group: 'penetration',
},
{
name: 'DV triple penetration',
slug: 'dv-tp',
priority: 7,
description: 'Triple penetration with [two cocks in your pussy](/tag/dvp), and one in [your ass](/tag/anal). Also see [double anal triple penetration](/tag/da-tp).',
description: '[Triple penetration](/tag/triple-penetration) with [two cocks in your pussy](/tag/dvp), and one in [your ass](/tag/anal). Also see [double anal triple penetration](/tag/da-tp).',
group: 'penetration',
},
{