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" ref="filters"
class="filters" class="filters"
> >
<div class="filters-row"> <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 class="filters-row">
<ul class="nolist"> <ul class="nolist">
<li> <li>
<Tooltip class="filter boobs"> <Tooltip class="filter boobs">
@ -195,9 +156,48 @@
</Tooltip> </Tooltip>
</li> </li>
</ul> </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> </nav>
<div class="tiles"> <div class="tiles">
@ -291,6 +291,7 @@ async function fetchActors(scroll) {
async function searchActors(scroll) { async function searchActors(scroll) {
const actors = await this.$store.dispatch('searchActors', { const actors = await this.$store.dispatch('searchActors', {
minLength: 1,
query: this.$route.query.query, query: this.$route.query.query,
limit: this.limit, limit: this.limit,
}); });
@ -419,7 +420,6 @@ export default {
.search { .search {
width: 0; width: 0;
flex-grow: 1; flex-grow: 1;
justify-content: flex-end;
box-sizing: border-box; box-sizing: border-box;
padding: 0 1rem; padding: 0 1rem;
} }
@ -427,7 +427,7 @@ export default {
.filters, .filters,
.filters-row { .filters-row {
display: flex; display: flex;
justify-content: center; justify-content: flex-end;
align-items: center; align-items: center;
} }
@ -435,6 +435,10 @@ export default {
margin: 1rem 0 .5rem 0; margin: 1rem 0 .5rem 0;
} }
.filters-row {
flex-grow: 1;
}
.filters-row, .filters-row,
.filter { .filter {
padding: 0 1rem; padding: 0 1rem;
@ -443,7 +447,7 @@ export default {
.genders { .genders {
display: flex; display: flex;
flex-shrink: 0; flex-shrink: 0;
padding: 0 .5rem 0 0; padding: 0 0 0 .5rem;
} }
.letter, .letter,
@ -686,7 +690,7 @@ export default {
@media(max-width: $breakpoint-kilo) { @media(max-width: $breakpoint-kilo) {
.filters { .filters {
flex-direction: column-reverse; flex-direction: column;
} }
::v-deep(.search) { ::v-deep(.search) {
@ -698,7 +702,7 @@ export default {
@media(max-width: $breakpoint) { @media(max-width: $breakpoint) {
.filters-row { .filters-row {
flex-direction: column; flex-direction: column-reverse;
} }
.genders { .genders {

View File

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

View File

@ -1,22 +1,26 @@
<template> <template>
<div class="tags"> <div class="tags">
<div <div class="content-inner">
v-for="(tags, category) in categories" <SearchBar :placeholder="'Search tags'" />
:key="category"
class="category"
>
<h3 class="heading">{{ category }}</h3>
<div <div
:key="sfw" v-for="(tags, category) in categories"
class="tiles" :key="category"
class="category"
> >
<Tag <h3 class="heading">{{ category }}</h3>
v-for="tag in tags"
:key="`tag-${tag.id}`" <div
:tag="tag" :key="sfw"
:lazy="true" class="tiles"
/> >
<Tag
v-for="tag in tags"
:key="`tag-${tag.id}`"
:tag="tag"
:lazy="true"
/>
</div>
</div> </div>
</div> </div>
@ -26,122 +30,128 @@
<script> <script>
import Tag from './tile.vue'; 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() { function sfw() {
return this.$store.state.ui.sfw; return this.$store.state.ui.sfw;
} }
async function mounted() { async function fetchTags() {
const tagSlugsByCategory = { if (this.$route.query.query) {
popular: [ await this.searchTags();
'anal', return;
'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',
],
};
const tags = await this.$store.dispatch('fetchTags', { const tags = await this.$store.dispatch('fetchTags', {
slugs: Object.values(tagSlugsByCategory).flat(), slugs: Object.values(tagSlugsByCategory).flat(),
@ -153,13 +163,30 @@ async function mounted() {
...acc, ...acc,
[category]: tagSlugs.map(tagSlug => tagsBySlug[tagSlug]), [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'; this.pageTitle = 'Tags';
await this.fetchTags();
} }
export default { export default {
components: { components: {
Tag, Tag,
SearchBar,
}, },
data() { data() {
return { return {
@ -170,7 +197,14 @@ export default {
computed: { computed: {
sfw, sfw,
}, },
watch: {
$route: fetchTags,
},
mounted, mounted,
methods: {
fetchTags,
searchTags,
},
}; };
</script> </script>
@ -178,6 +212,12 @@ export default {
@import 'theme'; @import 'theme';
.tags { .tags {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.content-inner {
padding: 1rem 1rem 0 1rem; padding: 1rem 1rem 0 1rem;
} }
@ -191,7 +231,7 @@ export default {
.heading { .heading {
text-transform: capitalize; text-transform: capitalize;
padding: 0 0 0 .5rem; padding: 0 0 0 .5rem;
margin: 2rem 0 1rem 0; margin: 1.75rem 0 1rem 0;
} }
.category:first-child .heading { .category:first-child .heading {

View File

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

View File

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

View File

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

View File

@ -1396,6 +1396,8 @@ exports.up = knex => Promise.resolve()
.then(() => { // eslint-disable-line arrow-body-style .then(() => { // eslint-disable-line arrow-body-style
// allow vim fold // allow vim fold
return knex.raw(` return knex.raw(`
CREATE EXTENSION pg_trgm;
CREATE FUNCTION current_user_id() RETURNS INTEGER AS $$ 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 */ /* 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; 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 $$ CREATE FUNCTION search_actors(search text, min_length smallint DEFAULT 2) RETURNS SETOF actors AS $$
SELECT * FROM actors SELECT * FROM actors
WHERE length(search) >= min_length 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; $$ LANGUAGE SQL STABLE;
CREATE FUNCTION search_tags(search text, min_length smallint DEFAULT 2, is_primary boolean DEFAULT true) RETURNS SETOF tags AS $$ CREATE FUNCTION search_tags(query text, min_length smallint DEFAULT 2) RETURNS SETOF tags AS $$
SELECT * FROM tags SELECT aliases.* FROM tags
WHERE length(search) >= min_length LEFT JOIN tags AS aliases ON aliases.id = tags.alias_for OR (tags.alias_for IS NULL AND aliases.id = tags.id)
AND name ILIKE ('%' || TRIM(search) || '%') WHERE length(query) >= min_length
AND CASE AND tags.name ILIKE ('%' || TRIM(query) || '%')
WHEN is_primary GROUP BY aliases.id
THEN tags.alias_for IS NULL ORDER BY similarity(aliases.slug, query) DESC, slug ASC
ELSE true
END
$$ LANGUAGE SQL STABLE; $$ LANGUAGE SQL STABLE;
CREATE FUNCTION actors_tags(actor actors, selectable_tags text[]) RETURNS SETOF tags AS $$ 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_sites;
DROP FUNCTION IF EXISTS search_entities; DROP FUNCTION IF EXISTS search_entities;
DROP FUNCTION IF EXISTS search_actors; DROP FUNCTION IF EXISTS search_actors;
DROP FUNCTION IF EXISTS search_movies;
DROP FUNCTION IF EXISTS search_tags; DROP FUNCTION IF EXISTS search_tags;
DROP FUNCTION IF EXISTS get_random_sfw_media_id; DROP FUNCTION IF EXISTS get_random_sfw_media_id;

View File

@ -977,14 +977,14 @@ const tags = [
name: 'DA triple penetration', name: 'DA triple penetration',
slug: 'da-tp', slug: 'da-tp',
priority: 7, 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', group: 'penetration',
}, },
{ {
name: 'DV triple penetration', name: 'DV triple penetration',
slug: 'dv-tp', slug: 'dv-tp',
priority: 7, 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', group: 'penetration',
}, },
{ {