Compare commits
297 Commits
42791c528e
...
old
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4839a3b94c | ||
|
|
d8b641e461 | ||
|
|
7b3bdadd44 | ||
|
|
5deba6b90f | ||
|
|
a5afffc968 | ||
|
|
a239a5c593 | ||
|
|
e56e7333e3 | ||
|
|
d55e3c37cd | ||
|
|
97b78ea016 | ||
|
|
3e290b74dc | ||
|
|
65141207ae | ||
|
|
17dfeac1af | ||
|
|
4a9c428d69 | ||
|
|
333f252099 | ||
|
|
38232f258a | ||
|
|
582269cfaa | ||
|
|
0b646429fd | ||
|
|
fecef6c1cf | ||
|
|
73e5404c44 | ||
|
|
690d2bb3ed | ||
|
|
1dd935e1e9 | ||
|
|
8386230f33 | ||
|
|
5a68b06137 | ||
|
|
5918364cf5 | ||
|
|
df4d860d35 | ||
|
|
d9f0db6e3c | ||
|
|
7372b560b2 | ||
|
|
9d638c685c | ||
|
|
5da1acc38d | ||
|
|
c7c62e220d | ||
|
|
9edd652a2c | ||
|
|
cde760c1ea | ||
|
|
19c7f958e1 | ||
|
|
c7710afdbf | ||
|
|
ba18a3dadb | ||
|
|
496c29e569 | ||
|
|
98a72a4929 | ||
|
|
c4f0b48932 | ||
|
|
93abbab873 | ||
|
|
f7bbab08bd | ||
|
|
1e4ddf2bbf | ||
|
|
9b7879bff7 | ||
|
|
03d96d4dec | ||
|
|
1dfa034332 | ||
|
|
196a1d33e3 | ||
|
|
3d349c413f | ||
|
|
bd5babd37e | ||
|
|
fffe9080f3 | ||
|
|
5630b16e99 | ||
|
|
4bcf7ef45b | ||
|
|
9abc1d48ac | ||
|
|
ceaf5a3217 | ||
|
|
36d84fb98b | ||
|
|
3d80e9d77b | ||
|
|
1dc38ffacc | ||
|
|
ee210b5c68 | ||
|
|
3aae3cd3a1 | ||
|
|
f1a7f2f905 | ||
|
|
0b7e0319f1 | ||
|
|
10b8abf706 | ||
|
|
b163223ff1 | ||
|
|
b9f3eb85f9 | ||
|
|
285a65f018 | ||
|
|
815d56d334 | ||
|
|
27a9a233e2 | ||
|
|
22864105ac | ||
|
|
7cf47cbf8d | ||
|
|
502c588958 | ||
|
|
81792a7c2f | ||
|
|
784d326584 | ||
|
|
83dc7aa578 | ||
|
|
5a3b27fbf7 | ||
|
|
5ff83a9497 | ||
|
|
07cb39c756 | ||
|
|
fffd54995d | ||
|
|
5302176f0b | ||
|
|
efd7cf8acc | ||
|
|
52218c30f8 | ||
|
|
04c176fa8b | ||
|
|
af320684b4 | ||
|
|
b2ba14ce45 | ||
|
|
d776cc2f71 | ||
|
|
b4bed2f623 | ||
|
|
5c5a11fdca | ||
|
|
a22c62d893 | ||
|
|
90bc2f3724 | ||
|
|
8cdc794a3a | ||
|
|
250618df8b | ||
|
|
b62ae00c54 | ||
|
|
67d0a9e0e0 | ||
| b4425bc3bb | |||
| d77c145de3 | |||
|
|
6753f923d9 | ||
|
|
452f725332 | ||
|
|
7c1f45bcfb | ||
|
|
11ea54f6e8 | ||
|
|
5b623ee46f | ||
|
|
7ceec1c739 | ||
|
|
fc318536aa | ||
|
|
cd8f690ac6 | ||
|
|
240a8e86fd | ||
|
|
c557f0f1f0 | ||
|
|
86f56695e2 | ||
|
|
20da2d1cf6 | ||
|
|
372db86927 | ||
|
|
4c503a3177 | ||
|
|
57d46f5842 | ||
|
|
7f11d0ca91 | ||
|
|
bbd788699e | ||
|
|
e24add98ea | ||
|
|
506971b44b | ||
|
|
43a0bc8a2c | ||
|
|
baebdbc0bb | ||
|
|
1c24cae3b1 | ||
|
|
b9146aee5f | ||
|
|
09bfcb01f5 | ||
|
|
5d80b6dc21 | ||
|
|
140a38c349 | ||
|
|
6760c11881 | ||
|
|
2dcdbf9c37 | ||
|
|
3b958a02ef | ||
|
|
605242b399 | ||
|
|
299f257a01 | ||
|
|
0ad64ef5f4 | ||
|
|
3bfe1e8fdb | ||
|
|
7d992f4129 | ||
|
|
280032937f | ||
|
|
d93670842b | ||
|
|
dc187a9a3a | ||
|
|
5f89c6e14c | ||
|
|
9f10420eb9 | ||
|
|
9568370132 | ||
|
|
1c0c30be11 | ||
|
|
cdb7dcd6e9 | ||
|
|
8e4be06675 | ||
|
|
de7a8c756a | ||
|
|
65e2b72c6a | ||
|
|
523c36ecd4 | ||
|
|
5b5688438c | ||
|
|
4ca07631c7 | ||
|
|
ec4e7bc12a | ||
|
|
26d15c0a6f | ||
|
|
a484396db4 | ||
|
|
681db02784 | ||
|
|
e88554666c | ||
|
|
9f37ec4cff | ||
|
|
abd063a578 | ||
|
|
883e57ce1f | ||
|
|
ad04502a8c | ||
|
|
f65ed16842 | ||
|
|
1918dd4ad5 | ||
|
|
3983d3464f | ||
|
|
61daf5f037 | ||
|
|
eafa144104 | ||
|
|
60c151ff6a | ||
|
|
c97d18fcf9 | ||
|
|
e41f9fa937 | ||
|
|
56a7fb0ad9 | ||
|
|
e29cbc9fea | ||
|
|
08f725a0b6 | ||
|
|
b93a5715cb | ||
|
|
2018d5c298 | ||
|
|
87094a9498 | ||
|
|
53a1c697d0 | ||
|
|
f04eb72891 | ||
|
|
dfeb0c08cf | ||
|
|
9d7183ac69 | ||
|
|
2539e88f47 | ||
|
|
20d0d860d3 | ||
|
|
6b4aa64d74 | ||
|
|
c7b9ec7a4b | ||
|
|
e81864ebde | ||
|
|
0305a22a24 | ||
|
|
a406eb584d | ||
|
|
2be3ea9bbb | ||
|
|
0a8a7ff9a5 | ||
|
|
98c103c625 | ||
|
|
e0908a5f5e | ||
|
|
f0b7678444 | ||
|
|
64e9efe095 | ||
|
|
ab29ab053e | ||
|
|
a5081bc7c9 | ||
|
|
da9c628f9b | ||
|
|
26539b74a5 | ||
|
|
a867817dc1 | ||
|
|
d1e05915b5 | ||
|
|
b764fdec85 | ||
|
|
8322d43b09 | ||
|
|
29b8c5e38e | ||
|
|
83dd233991 | ||
|
|
0864154a0e | ||
|
|
53357d4bd2 | ||
|
|
69bf98edf1 | ||
|
|
a22c4d5679 | ||
|
|
e5ad1648eb | ||
|
|
100a35b4e8 | ||
|
|
6c5d4389fe | ||
|
|
92f9ff4104 | ||
|
|
0e4fd12d70 | ||
|
|
9040285ce5 | ||
|
|
972b15e72d | ||
|
|
c1aea78496 | ||
|
|
49f891ba44 | ||
|
|
239057f1b6 | ||
|
|
167df35d37 | ||
|
|
b1b2ad2111 | ||
|
|
f1ff662be2 | ||
|
|
2418fec9c9 | ||
|
|
aaaa0a6afb | ||
|
|
afe22003e1 | ||
|
|
b6ad2903f3 | ||
|
|
6c298cd639 | ||
|
|
dbff3e9539 | ||
|
|
fcc6b33d07 | ||
|
|
0224c26ca0 | ||
|
|
294f15e694 | ||
|
|
522584711b | ||
|
|
ae40f33283 | ||
|
|
172e01c80e | ||
|
|
901acf1390 | ||
|
|
a316da3083 | ||
|
|
0b46e27497 | ||
|
|
b164c5dad7 | ||
|
|
2d6b285817 | ||
|
|
65c79567d2 | ||
|
|
d3633f31ac | ||
|
|
73a9a11aa6 | ||
|
|
c21bd8b01e | ||
|
|
14f4d24872 | ||
|
|
731abc79ee | ||
|
|
d542889827 | ||
|
|
8c5ef21459 | ||
|
|
7bfca9596d | ||
|
|
7bd858f96b | ||
|
|
b8657cb6e6 | ||
|
|
69a7e8f13d | ||
|
|
23537e0243 | ||
|
|
546f778b1b | ||
|
|
245f69a122 | ||
|
|
c6f3f7a239 | ||
|
|
f0a6e80e5a | ||
|
|
0cbb985945 | ||
|
|
bb384029ac | ||
|
|
85c6b581cb | ||
|
|
dd18644796 | ||
|
|
1628e41d09 | ||
|
|
a77d8f4cea | ||
|
|
0a4152b4b7 | ||
|
|
3fc4dc15df | ||
|
|
4ee0dcef9b | ||
|
|
011482ac9d | ||
|
|
6a8c9d89cb | ||
|
|
e0905ab8fc | ||
|
|
eb1f8f86fd | ||
|
|
959b5d9d0e | ||
|
|
0c19a026ef | ||
|
|
b24973eb19 | ||
|
|
4b18867883 | ||
|
|
5e292a0880 | ||
|
|
f00e37490c | ||
|
|
715e44cf21 | ||
|
|
25d1c1b229 | ||
|
|
094226eeb5 | ||
|
|
b08cb46ae5 | ||
|
|
6b88cf1040 | ||
|
|
aacfd1b29d | ||
|
|
0427e1e276 | ||
|
|
ee33bd8e63 | ||
|
|
25b8bd689e | ||
|
|
b6468b03a7 | ||
|
|
a848d6991b | ||
|
|
65c3053b49 | ||
|
|
1d686d7e40 | ||
|
|
488d1082e4 | ||
|
|
8967907893 | ||
|
|
e527a67dc1 | ||
|
|
6847ef690c | ||
|
|
96a2125248 | ||
|
|
c5e4310a6b | ||
|
|
23b41fc4f3 | ||
|
|
4fb41a4c35 | ||
|
|
12c0e8e828 | ||
|
|
dce27e985e | ||
|
|
0a343dfa98 | ||
|
|
729ca0f968 | ||
|
|
385dfb9f75 | ||
|
|
afbae24f43 | ||
|
|
eb7009832a | ||
|
|
d1480da076 | ||
|
|
10a2731caf | ||
|
|
ab1329dd67 | ||
|
|
e9a0700742 | ||
|
|
3f473589ad | ||
|
|
7a44c7aaaa | ||
|
|
bed329cd8c | ||
|
|
011bb4efa3 | ||
|
|
c979173422 |
@@ -2,7 +2,7 @@
|
|||||||
"root": true,
|
"root": true,
|
||||||
"extends": ["airbnb-base", "plugin:vue/recommended"],
|
"extends": ["airbnb-base", "plugin:vue/recommended"],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"parser": "babel-eslint",
|
"parser": "@babel/eslint-parser",
|
||||||
"ecmaVersion": 2019,
|
"ecmaVersion": 2019,
|
||||||
"sourceType": "module"
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
@@ -17,10 +17,11 @@
|
|||||||
"vue/html-indent": ["error", "tab"],
|
"vue/html-indent": ["error", "tab"],
|
||||||
"vue/multiline-html-element-content-newline": 0,
|
"vue/multiline-html-element-content-newline": 0,
|
||||||
"vue/singleline-html-element-content-newline": 0,
|
"vue/singleline-html-element-content-newline": 0,
|
||||||
|
"vue/multi-word-component-names": 0,
|
||||||
"no-param-reassign": ["error", {
|
"no-param-reassign": ["error", {
|
||||||
"props": true,
|
"props": true,
|
||||||
"ignorePropertyModificationsFor": ["state", "acc"]
|
"ignorePropertyModificationsFor": ["state", "acc"]
|
||||||
}],
|
}]
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"CONFIG": true
|
"CONFIG": true
|
||||||
|
|||||||
3
.gitignore
vendored
@@ -2,9 +2,12 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
log/
|
log/
|
||||||
media/
|
media/
|
||||||
|
html/
|
||||||
public/js/*
|
public/js/*
|
||||||
public/css/*
|
public/css/*
|
||||||
config/*
|
config/*
|
||||||
!config/default.js
|
!config/default.js
|
||||||
assets/js/config/
|
assets/js/config/
|
||||||
!assets/js/config/default.js
|
!assets/js/config/default.js
|
||||||
|
*.heapprofile
|
||||||
|
*.heapsnapshot
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="actor-header">
|
<div class="actor-header">
|
||||||
<h2 class="header-name">
|
<h2 class="header-name">
|
||||||
<span v-if="actor.entity">{{ actor.name }} ({{ actor.entity.name }})</span>
|
<span v-if="actor.entity">{{ actor.name }} ({{ actor.entity.name }})</span>
|
||||||
<span v-else="">{{ actor.name }}</span>
|
<span v-else>{{ actor.name }}</span>
|
||||||
|
|
||||||
<Gender
|
<Gender
|
||||||
:gender="actor.gender"
|
:gender="actor.gender"
|
||||||
@@ -361,7 +361,10 @@
|
|||||||
:available-actors="actor.actors"
|
:available-actors="actor.actors"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Releases :releases="releases" />
|
<Releases
|
||||||
|
:releases="releases"
|
||||||
|
:done="done"
|
||||||
|
/>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
:items-total="totalCount"
|
:items-total="totalCount"
|
||||||
@@ -389,6 +392,8 @@ import Social from './social.vue';
|
|||||||
import StashButton from '../stashes/button.vue';
|
import StashButton from '../stashes/button.vue';
|
||||||
|
|
||||||
async function fetchActor(scroll = true) {
|
async function fetchActor(scroll = true) {
|
||||||
|
this.done = false;
|
||||||
|
|
||||||
const { actor, releases, totalCount } = await this.$store.dispatch('fetchActorById', {
|
const { actor, releases, totalCount } = await this.$store.dispatch('fetchActorById', {
|
||||||
actorId: Number(this.$route.params.actorId),
|
actorId: Number(this.$route.params.actorId),
|
||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
@@ -400,6 +405,7 @@ async function fetchActor(scroll = true) {
|
|||||||
this.releases = releases;
|
this.releases = releases;
|
||||||
this.totalCount = totalCount;
|
this.totalCount = totalCount;
|
||||||
this.stashedBy = actor.stashes;
|
this.stashedBy = actor.stashes;
|
||||||
|
this.done = true;
|
||||||
|
|
||||||
if (this.$refs.filter && scroll) {
|
if (this.$refs.filter && scroll) {
|
||||||
this.$refs.filter.$el.scrollIntoView();
|
this.$refs.filter.$el.scrollIntoView();
|
||||||
@@ -463,6 +469,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
actor: null,
|
actor: null,
|
||||||
releases: null,
|
releases: null,
|
||||||
|
done: false,
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
|
|||||||
@@ -1,216 +1,246 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="actors">
|
<div
|
||||||
|
ref="content"
|
||||||
|
class="actors"
|
||||||
|
>
|
||||||
<nav
|
<nav
|
||||||
ref="filter"
|
ref="filters"
|
||||||
class="filter"
|
class="filters"
|
||||||
>
|
>
|
||||||
<ul class="genders nolist">
|
<div class="filters-row">
|
||||||
<li class="gender">
|
<ul class="genders nolist">
|
||||||
<router-link
|
<li class="gender">
|
||||||
:to="{ name: 'actors', params: { gender: 'all', letter, pageNumber: 1 } }"
|
<router-link
|
||||||
:class="{ selected: gender === 'all' }"
|
:to="{ name: 'actors', params: { gender: 'all', pageNumber: 1 }, query: $route.query }"
|
||||||
class="gender-link all"
|
:class="{ selected: gender === 'all' }"
|
||||||
>all</router-link>
|
class="gender-link all"
|
||||||
</li>
|
>all</router-link>
|
||||||
<li class="gender">
|
</li>
|
||||||
<router-link
|
<li class="gender">
|
||||||
:to="{ name: 'actors', params: { gender: 'female', letter, pageNumber: 1 } }"
|
<router-link
|
||||||
:class="{ selected: gender === 'female' }"
|
:to="{ name: 'actors', params: { gender: 'female', pageNumber: 1 }, query: $route.query }"
|
||||||
class="gender-link female"
|
:class="{ selected: gender === 'female' }"
|
||||||
><Gender gender="female" /></router-link>
|
class="gender-link female"
|
||||||
</li>
|
><Gender gender="female" /></router-link>
|
||||||
<li class="gender">
|
</li>
|
||||||
<router-link
|
<li class="gender">
|
||||||
:to="{ name: 'actors', params: { gender: 'male', letter, pageNumber: 1 } }"
|
<router-link
|
||||||
:class="{ selected: gender === 'male' }"
|
:to="{ name: 'actors', params: { gender: 'male', pageNumber: 1 }, query: $route.query }"
|
||||||
class="gender-link male"
|
:class="{ selected: gender === 'male' }"
|
||||||
replace
|
class="gender-link male"
|
||||||
><Gender gender="male" /></router-link>
|
replace
|
||||||
</li>
|
><Gender gender="male" /></router-link>
|
||||||
<li class="gender">
|
</li>
|
||||||
<router-link
|
<li class="gender">
|
||||||
:to="{ name: 'actors', params: { gender: 'trans', letter, pageNumber: 1 } }"
|
<router-link
|
||||||
:class="{ selected: gender === 'trans' }"
|
:to="{ name: 'actors', params: { gender: 'trans', pageNumber: 1 }, query: $route.query }"
|
||||||
class="gender-link transsexual"
|
:class="{ selected: gender === 'trans' }"
|
||||||
replace
|
class="gender-link transsexual"
|
||||||
><Gender gender="transsexual" /></router-link>
|
replace
|
||||||
</li>
|
><Gender gender="transsexual" /></router-link>
|
||||||
<li class="gender">
|
</li>
|
||||||
<router-link
|
<li class="gender">
|
||||||
:to="{ name: 'actors', params: { gender: 'other', letter, pageNumber: 1 } }"
|
<router-link
|
||||||
:class="{ selected: gender === 'other' }"
|
:to="{ name: 'actors', params: { gender: 'other', pageNumber: 1 }, query: $route.query }"
|
||||||
class="gender-link other"
|
:class="{ selected: gender === 'other' }"
|
||||||
replace
|
class="gender-link other"
|
||||||
><Icon icon="question5" /></router-link>
|
replace
|
||||||
</li>
|
><Icon icon="question5" /></router-link>
|
||||||
</ul>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<ul class="letters nolist">
|
<ul class="filters-attributes nolist">
|
||||||
<li
|
<li>
|
||||||
v-for="letterX in letters"
|
<Tooltip class="filter boobs">
|
||||||
:key="letterX"
|
<span
|
||||||
class="letter"
|
class="filter-trigger"
|
||||||
>
|
:class="{ enabled: ageRequired }"
|
||||||
<router-link
|
><Icon icon="vcard" />Age</span>
|
||||||
:to="{ name: 'actors', params: { gender, letter: letterX, pageNumber: 1 } }"
|
|
||||||
:class="{ selected: letterX === letter }"
|
|
||||||
class="letter-link"
|
|
||||||
replace
|
|
||||||
>{{ letterX || 'All' }}</router-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<nav class="filter">
|
<template v-slot:tooltip>
|
||||||
<ul class="nolist">
|
<RangeFilter
|
||||||
<li>
|
label="age"
|
||||||
<Tooltip class="filter boobs">
|
:min="18"
|
||||||
<span
|
:max="100"
|
||||||
class="filter-trigger"
|
:value="age"
|
||||||
:class="{ enabled: ageRequired }"
|
:disabled="!ageRequired"
|
||||||
><Icon icon="vcard" />Age</span>
|
@enable="(checked) => updateValue('ageRequired', checked, true)"
|
||||||
|
@input="(range) => updateValue('age', range, false)"
|
||||||
<template v-slot:tooltip>
|
@change="(range) => updateValue('age', range, true)"
|
||||||
<RangeFilter
|
|
||||||
label="age"
|
|
||||||
:min="18"
|
|
||||||
:max="100"
|
|
||||||
:value="age"
|
|
||||||
:disabled="!ageRequired"
|
|
||||||
@enable="(checked) => updateValue('ageRequired', checked, true)"
|
|
||||||
@input="(range) => updateValue('age', range, false)"
|
|
||||||
@change="(range) => updateValue('age', range, true)"
|
|
||||||
>
|
|
||||||
<template v-slot:start><Icon icon="leaf" /></template>
|
|
||||||
<template v-slot:end><Icon icon="tree3" /></template>
|
|
||||||
</RangeFilter>
|
|
||||||
|
|
||||||
<div class="filter-section">
|
|
||||||
<label class="filter-label">
|
|
||||||
<span class="label">
|
|
||||||
<Checkbox
|
|
||||||
:checked="dobRequired"
|
|
||||||
class="checkbox"
|
|
||||||
@change="(checked) => updateValue('dobRequired', checked, true)"
|
|
||||||
/>Date of birth
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="input-container"
|
|
||||||
@click="() => updateValue('dobRequired', true, true)"
|
|
||||||
>
|
>
|
||||||
<input
|
<template v-slot:start><Icon icon="leaf" /></template>
|
||||||
v-model="dob"
|
<template v-slot:end><Icon icon="tree3" /></template>
|
||||||
:disabled="!dobRequired"
|
</RangeFilter>
|
||||||
type="date"
|
|
||||||
class="input"
|
<div class="filter-section">
|
||||||
@change="updateFilters"
|
<label class="filter-label">
|
||||||
|
<span class="label">
|
||||||
|
<Checkbox
|
||||||
|
:checked="dobRequired"
|
||||||
|
class="checkbox"
|
||||||
|
@change="(checked) => updateValue('dobRequired', checked, true)"
|
||||||
|
/>Date of birth
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="input-container"
|
||||||
|
@click="() => updateValue('dobRequired', true, true)"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
v-model="dob"
|
||||||
|
:disabled="!dobRequired"
|
||||||
|
type="date"
|
||||||
|
class="input"
|
||||||
|
@change="updateFilters"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</Tooltip>
|
||||||
</Tooltip>
|
</li>
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<Tooltip class="filter">
|
<Tooltip class="filter">
|
||||||
<span
|
<span
|
||||||
class="filter-trigger boobs"
|
class="filter-trigger boobs"
|
||||||
:class="{ enabled: boobSizeRequired || naturalBoobs !== 1 }"
|
:class="{ enabled: boobSizeRequired || naturalBoobs !== 1 }"
|
||||||
><Icon icon="boobs" />Boobs</span>
|
><Icon icon="boobs" />Boobs</span>
|
||||||
|
|
||||||
<template v-slot:tooltip>
|
<template v-slot:tooltip>
|
||||||
<RangeFilter
|
<RangeFilter
|
||||||
label="size"
|
label="size"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="boobSizes.length - 1"
|
:max="boobSizes.length - 1"
|
||||||
:value="boobSize"
|
:value="boobSize"
|
||||||
:values="boobSizes"
|
:values="boobSizes"
|
||||||
:disabled="!boobSizeRequired"
|
:disabled="!boobSizeRequired"
|
||||||
@enable="(checked) => updateValue('boobSizeRequired', checked, true)"
|
@enable="(checked) => updateValue('boobSizeRequired', checked, true)"
|
||||||
@input="(range) => updateValue('boobSize', range, false)"
|
@input="(range) => updateValue('boobSize', range, false)"
|
||||||
@change="(range) => updateValue('boobSize', range, true)"
|
@change="(range) => updateValue('boobSize', range, true)"
|
||||||
>
|
|
||||||
<template v-slot:start><Icon icon="boobs-small" /></template>
|
|
||||||
<template v-slot:end><Icon icon="boobs-big" /></template>
|
|
||||||
</RangeFilter>
|
|
||||||
|
|
||||||
<div class="filter-section">
|
|
||||||
<span class="filter-label">Enhanced</span>
|
|
||||||
|
|
||||||
<span
|
|
||||||
:class="{ [['off', 'default', 'on'][naturalBoobs]]: true }"
|
|
||||||
class="toggle-container noclick"
|
|
||||||
>
|
>
|
||||||
<span
|
<template v-slot:start><Icon icon="boobs-small" /></template>
|
||||||
class="toggle-label off"
|
<template v-slot:end><Icon icon="boobs-big" /></template>
|
||||||
@click="updateValue('naturalBoobs', 0)"
|
</RangeFilter>
|
||||||
><Icon icon="leaf" /></span>
|
|
||||||
|
|
||||||
<input
|
<div class="filter-section">
|
||||||
v-model.number="naturalBoobs"
|
<span class="filter-label">Enhanced</span>
|
||||||
class="toggle"
|
|
||||||
type="range"
|
<span
|
||||||
min="0"
|
:class="{ [['off', 'default', 'on'][naturalBoobs]]: true }"
|
||||||
max="2"
|
class="toggle-container noclick"
|
||||||
@change="updateFilters"
|
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
class="toggle-label off"
|
||||||
|
@click="updateValue('naturalBoobs', 0)"
|
||||||
|
><Icon icon="leaf" /></span>
|
||||||
|
|
||||||
<span
|
<input
|
||||||
class="toggle-label on"
|
v-model.number="naturalBoobs"
|
||||||
@click="updateValue('naturalBoobs', 2)"
|
class="toggle"
|
||||||
><Icon icon="magic-wand2" /></span>
|
type="range"
|
||||||
</span>
|
min="0"
|
||||||
</div>
|
max="2"
|
||||||
</template>
|
@change="updateFilters"
|
||||||
</Tooltip>
|
>
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
<span
|
||||||
<Tooltip class="filter boobs">
|
class="toggle-label on"
|
||||||
<span
|
@click="updateValue('naturalBoobs', 2)"
|
||||||
class="filter-trigger"
|
><Icon icon="magic-wand2" /></span>
|
||||||
:class="{ enabled: heightRequired || weightRequired }"
|
</span>
|
||||||
><Icon icon="rulers" />Physique</span>
|
</div>
|
||||||
|
</template>
|
||||||
|
</Tooltip>
|
||||||
|
</li>
|
||||||
|
|
||||||
<template v-slot:tooltip>
|
<li>
|
||||||
<RangeFilter
|
<Tooltip class="filter boobs">
|
||||||
label="height"
|
<span
|
||||||
:min="50"
|
class="filter-trigger"
|
||||||
:max="220"
|
:class="{ enabled: heightRequired || weightRequired }"
|
||||||
:value="height"
|
><Icon icon="rulers" />Physique</span>
|
||||||
:disabled="!heightRequired"
|
|
||||||
unit="cm"
|
|
||||||
@enable="(checked) => updateValue('heightRequired', checked, true)"
|
|
||||||
@input="(range) => updateValue('height', range, false)"
|
|
||||||
@change="(range) => updateValue('height', range, true)"
|
|
||||||
>
|
|
||||||
<template v-slot:start><Icon icon="height-short" /></template>
|
|
||||||
<template v-slot:end><Icon icon="height" /></template>
|
|
||||||
</RangeFilter>
|
|
||||||
|
|
||||||
<RangeFilter
|
<template v-slot:tooltip>
|
||||||
label="weight"
|
<RangeFilter
|
||||||
:min="30"
|
label="height"
|
||||||
:max="200"
|
:min="50"
|
||||||
:value="weight"
|
:max="220"
|
||||||
:disabled="!weightRequired"
|
:value="height"
|
||||||
unit="kg"
|
:disabled="!heightRequired"
|
||||||
@enable="(checked) => updateValue('weightRequired', checked, true)"
|
unit="cm"
|
||||||
@input="(range) => updateValue('weight', range, false)"
|
@enable="(checked) => updateValue('heightRequired', checked, true)"
|
||||||
@change="(range) => updateValue('weight', range, true)"
|
@input="(range) => updateValue('height', range, false)"
|
||||||
>
|
@change="(range) => updateValue('height', range, true)"
|
||||||
<template v-slot:start><Icon icon="meter-slow" /></template>
|
>
|
||||||
<template v-slot:end><Icon icon="meter-fast" /></template>
|
<template v-slot:start><Icon icon="height-short" /></template>
|
||||||
</RangeFilter>
|
<template v-slot:end><Icon icon="height" /></template>
|
||||||
</template>
|
</RangeFilter>
|
||||||
</Tooltip>
|
|
||||||
</li>
|
<RangeFilter
|
||||||
</ul>
|
label="weight"
|
||||||
|
:min="30"
|
||||||
|
:max="200"
|
||||||
|
:value="weight"
|
||||||
|
:disabled="!weightRequired"
|
||||||
|
unit="kg"
|
||||||
|
@enable="(checked) => updateValue('weightRequired', checked, true)"
|
||||||
|
@input="(range) => updateValue('weight', range, false)"
|
||||||
|
@change="(range) => updateValue('weight', range, true)"
|
||||||
|
>
|
||||||
|
<template v-slot:start><Icon icon="meter-slow" /></template>
|
||||||
|
<template v-slot:end><Icon icon="meter-fast" /></template>
|
||||||
|
</RangeFilter>
|
||||||
|
</template>
|
||||||
|
</Tooltip>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<Tooltip class="filter">
|
||||||
|
<span
|
||||||
|
:class="{ enabled: country }"
|
||||||
|
class="filter-trigger"
|
||||||
|
><img
|
||||||
|
v-if="$route.query.c"
|
||||||
|
:src="`/img/flags/${$route.query.c.toLowerCase()}.svg`"
|
||||||
|
class="flag"
|
||||||
|
><Icon
|
||||||
|
v-else
|
||||||
|
icon="earth2"
|
||||||
|
/>Country</span>
|
||||||
|
|
||||||
|
<template v-slot:tooltip>
|
||||||
|
<input
|
||||||
|
v-model="countryQuery"
|
||||||
|
placeholder="Search"
|
||||||
|
class="input"
|
||||||
|
>
|
||||||
|
|
||||||
|
<Countries
|
||||||
|
v-if="!countryQuery"
|
||||||
|
:countries="topCountries"
|
||||||
|
:selected-country="country"
|
||||||
|
:update-value="updateValue"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Countries
|
||||||
|
:countries="filteredCountries"
|
||||||
|
:selected-country="country"
|
||||||
|
:update-value="updateValue"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Tooltip>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SearchBar :placeholder="`Search ${totalCount} actors`" />
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="tiles">
|
<div
|
||||||
|
ref="tiles"
|
||||||
|
class="tiles"
|
||||||
|
>
|
||||||
<Actor
|
<Actor
|
||||||
v-for="actor in actors"
|
v-for="actor in actors"
|
||||||
:key="`actor-${actor.id}`"
|
:key="`actor-${actor.id}`"
|
||||||
@@ -235,11 +265,14 @@ import dayjs from 'dayjs';
|
|||||||
import Actor from './tile.vue';
|
import Actor from './tile.vue';
|
||||||
import Gender from './gender.vue';
|
import Gender from './gender.vue';
|
||||||
import Checkbox from '../form/checkbox.vue';
|
import Checkbox from '../form/checkbox.vue';
|
||||||
|
import Countries from './countries.vue';
|
||||||
import RangeFilter from './filter-range.vue';
|
import RangeFilter from './filter-range.vue';
|
||||||
|
import SearchBar from '../search/bar.vue';
|
||||||
import Pagination from '../pagination/pagination.vue';
|
import Pagination from '../pagination/pagination.vue';
|
||||||
|
|
||||||
const toggleValues = [true, null, false];
|
const toggleValues = [true, null, false];
|
||||||
const boobSizes = 'ABCDEFGHZ'.split('');
|
const boobSizes = 'ABCDEFGHIJKZ'.split('');
|
||||||
|
const topCountries = ['AU', 'BR', 'CZ', 'DE', 'JP', 'RU', 'GB', 'US'];
|
||||||
|
|
||||||
function updateFilters() {
|
function updateFilters() {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
@@ -247,15 +280,16 @@ function updateFilters() {
|
|||||||
params: {
|
params: {
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
gender: this.gender,
|
gender: this.gender,
|
||||||
letter: this.letter,
|
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
nb: this.naturalBoobs !== 1 ? this.naturalBoobs : undefined,
|
nb: this.naturalBoobs !== 1 ? this.naturalBoobs : undefined,
|
||||||
bs: this.boobSizeRequired ? this.boobSize.join(',') : undefined,
|
bs: this.boobSizeRequired ? this.boobSize.join(',') : undefined,
|
||||||
h: this.heightRequired ? this.height.join(',') : undefined,
|
h: this.heightRequired ? this.height.join(',') : undefined,
|
||||||
w: this.weightRequired ? this.weight.join(',') : undefined,
|
w: this.weightRequired ? this.weight.join(',') : undefined,
|
||||||
|
c: this.country ? this.country : undefined,
|
||||||
age: this.ageRequired ? this.age.join(',') : undefined,
|
age: this.ageRequired ? this.age.join(',') : undefined,
|
||||||
dob: this.dobRequired ? this.dob : undefined,
|
dob: this.dobRequired ? this.dob : undefined,
|
||||||
|
query: this.$route.query.query,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -271,29 +305,39 @@ function updateValue(prop, value, load = true) {
|
|||||||
async function fetchActors(scroll) {
|
async function fetchActors(scroll) {
|
||||||
const curatedGender = this.gender.replace('trans', 'transsexual');
|
const curatedGender = this.gender.replace('trans', 'transsexual');
|
||||||
|
|
||||||
const { actors, totalCount } = await this.$store.dispatch('fetchActors', {
|
const { actors, countries, totalCount } = await this.$store.dispatch('fetchActors', {
|
||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
pageNumber: Number(this.$route.params.pageNumber) || 1,
|
pageNumber: Number(this.$route.params.pageNumber) || 1,
|
||||||
letter: this.letter.replace('all', ''),
|
query: this.$route.query.query,
|
||||||
gender: curatedGender === 'other' ? null : curatedGender,
|
gender: curatedGender === 'other' ? null : curatedGender,
|
||||||
age: this.ageRequired && this.age,
|
age: this.ageRequired && this.age,
|
||||||
dob: this.dobRequired && this.dob,
|
dob: this.dobRequired && this.dob,
|
||||||
boobSize: this.boobSizeRequired && this.boobSize,
|
boobSize: this.boobSizeRequired && this.boobSize,
|
||||||
|
country: this.country,
|
||||||
naturalBoobs: toggleValues[this.naturalBoobs] ?? null,
|
naturalBoobs: toggleValues[this.naturalBoobs] ?? null,
|
||||||
height: this.heightRequired && this.height,
|
height: this.heightRequired && this.height,
|
||||||
weight: this.weightRequired && this.weight,
|
weight: this.weightRequired && this.weight,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const countriesByAlpha2 = countries.reduce((acc, country) => ({ ...acc, [country.alpha2]: country }), {});
|
||||||
|
|
||||||
this.actors = actors;
|
this.actors = actors;
|
||||||
this.totalCount = totalCount;
|
this.totalCount = totalCount;
|
||||||
|
|
||||||
|
this.countries = countries;
|
||||||
|
this.topCountries = [...(this.country && !topCountries.includes(this.country) ? [this.country] : []), ...topCountries].map(alpha2 => countriesByAlpha2[alpha2]);
|
||||||
|
|
||||||
if (scroll) {
|
if (scroll) {
|
||||||
this.$refs.filter?.scrollIntoView();
|
this.$refs.tiles?.scrollIntoView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function letter() {
|
function filteredCountries() {
|
||||||
return this.$route.params.letter || 'all';
|
const countryQueryExpression = new RegExp(this.countryQuery, 'i');
|
||||||
|
|
||||||
|
return this.countryQuery?.length > 0
|
||||||
|
? this.countries.filter(country => countryQueryExpression.test(country.name) || countryQueryExpression.test(country.alpha2))
|
||||||
|
: this.countries;
|
||||||
}
|
}
|
||||||
|
|
||||||
function gender() {
|
function gender() {
|
||||||
@@ -302,8 +346,7 @@ function gender() {
|
|||||||
|
|
||||||
async function route(to, from) {
|
async function route(to, from) {
|
||||||
const scroll = to.params.pageNumber !== from.params.pageNumber
|
const scroll = to.params.pageNumber !== from.params.pageNumber
|
||||||
|| to.params.gender !== from.params.gender
|
|| to.params.gender !== from.params.gender;
|
||||||
|| to.params.letter !== from.params.letter;
|
|
||||||
|
|
||||||
await this.fetchActors(scroll);
|
await this.fetchActors(scroll);
|
||||||
}
|
}
|
||||||
@@ -318,17 +361,21 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
Actor,
|
Actor,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
Countries,
|
||||||
Gender,
|
Gender,
|
||||||
RangeFilter,
|
RangeFilter,
|
||||||
|
SearchBar,
|
||||||
Pagination,
|
Pagination,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actors: [],
|
actors: [],
|
||||||
|
countries: [],
|
||||||
|
topCountries: [],
|
||||||
|
countryQuery: null,
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
letters: ['all'].concat(Array.from({ length: 26 }, (value, index) => String.fromCharCode(index + 97).toUpperCase())),
|
|
||||||
age: this.$route.query.age?.split(',') || [18, 100],
|
age: this.$route.query.age?.split(',') || [18, 100],
|
||||||
ageRequired: !!this.$route.query.age,
|
ageRequired: !!this.$route.query.age,
|
||||||
dob: this.$route.query.dob || dayjs().subtract(21, 'years').format('YYYY-MM-DD'),
|
dob: this.$route.query.dob || dayjs().subtract(21, 'years').format('YYYY-MM-DD'),
|
||||||
@@ -336,6 +383,7 @@ export default {
|
|||||||
boobSizes,
|
boobSizes,
|
||||||
boobSize: this.$route.query.bs?.split(',') || ['A', 'Z'],
|
boobSize: this.$route.query.bs?.split(',') || ['A', 'Z'],
|
||||||
boobSizeRequired: !!this.$route.query.bs,
|
boobSizeRequired: !!this.$route.query.bs,
|
||||||
|
country: this.$route.query.c || null,
|
||||||
naturalBoobs: Number(this.$route.query.nb) || 1,
|
naturalBoobs: Number(this.$route.query.nb) || 1,
|
||||||
height: this.$route.query.h?.split(',').map(Number) || [50, 220],
|
height: this.$route.query.h?.split(',').map(Number) || [50, 220],
|
||||||
heightRequired: !!this.$route.query.h,
|
heightRequired: !!this.$route.query.h,
|
||||||
@@ -344,8 +392,8 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
letter,
|
|
||||||
gender,
|
gender,
|
||||||
|
filteredCountries,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route: route,
|
$route: route,
|
||||||
@@ -393,42 +441,53 @@ export default {
|
|||||||
.actors {
|
.actors {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiles {
|
.tiles {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
|
||||||
|
grid-template-rows: min-content;
|
||||||
grid-gap: .5rem;
|
grid-gap: .5rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter {
|
.search {
|
||||||
display: flex;
|
width: 0;
|
||||||
justify-content: center;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
flex-grow: 1;
|
||||||
padding: 0 1rem;
|
box-sizing: border-box;
|
||||||
margin: 0 0 1rem 0;
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
.filters,
|
||||||
margin: 1rem 0;
|
.filters-row {
|
||||||
}
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 1rem 0 .5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-row,
|
||||||
|
.filter {
|
||||||
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genders {
|
.genders {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 0 .5rem 0 0;
|
padding: 0 .5rem 0 0;
|
||||||
border-right: solid 1px var(--shadow-hint);
|
|
||||||
margin: 0 1rem 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.letter,
|
|
||||||
.gender {
|
.gender {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.letter-link,
|
|
||||||
.gender-link {
|
.gender-link {
|
||||||
width: 2.5rem;
|
width: 2.5rem;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
@@ -478,7 +537,8 @@ export default {
|
|||||||
color: var(--shadow);
|
color: var(--shadow);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
.icon {
|
.icon,
|
||||||
|
.flag {
|
||||||
fill: var(--shadow);
|
fill: var(--shadow);
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
@@ -661,10 +721,45 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint) {
|
@media(max-width: $breakpoint-mega) {
|
||||||
.genders {
|
.filters {
|
||||||
flex-direction: column;
|
flex-direction: column-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep(.search) {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-kilo) {
|
||||||
|
.filters {
|
||||||
|
margin: 1rem 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-row {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
padding: 0 1rem 1rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-attributes {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.genders {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiles {
|
||||||
|
padding: .5rem 1rem 1rem 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint-micro) {
|
@media(max-width: $breakpoint-micro) {
|
||||||
|
|||||||
88
assets/components/actors/countries.vue
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<ul class="countries nolist">
|
||||||
|
<li
|
||||||
|
v-for="country in countries"
|
||||||
|
:key="country.alpha2"
|
||||||
|
:title="country.name"
|
||||||
|
:class="{ selected: selectedCountry === country.alpha2 }"
|
||||||
|
class="country"
|
||||||
|
@click="updateValue('country', country.alpha2, true)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="`/img/flags/${country.alpha2.toLowerCase()}.svg`"
|
||||||
|
class="flag"
|
||||||
|
>
|
||||||
|
|
||||||
|
<span class="country-name">{{ country.alias || country.name }}</span>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
v-if="selectedCountry === country.alpha2"
|
||||||
|
icon="cross2"
|
||||||
|
@click.native.stop="updateValue('country', null, true)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
countries: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
selectedCountry: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
updateValue: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.countries:not(:last-child) {
|
||||||
|
border-bottom: solid 1px var(--shadow-hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.country {
|
||||||
|
width: 15rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.flag {
|
||||||
|
width: 1.25rem;
|
||||||
|
padding: .25rem .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
padding: .25rem .5rem;;
|
||||||
|
fill: var(--shadow);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
fill: var(--shadow-strong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--shadow-hint);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.country-name {
|
||||||
|
flex-grow: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding: .25rem .5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
v-if="actor"
|
v-if="actor"
|
||||||
class="actor"
|
class="actor"
|
||||||
>
|
>
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="{ name: 'actor', params: { actorId: actor.id, actorSlug: actor.slug } }"
|
:to="{ name: 'actor', params: { actorId: actor.id, actorSlug: actor.slug } }"
|
||||||
class="link"
|
class="link"
|
||||||
>
|
>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
class="name"
|
class="name"
|
||||||
>{{ actor.name }}</span>
|
>{{ actor.name }}</span>
|
||||||
|
|
||||||
<router-link
|
<RouterLink
|
||||||
v-if="actor.entity"
|
v-if="actor.entity"
|
||||||
v-tooltip="actor.entity.name"
|
v-tooltip="actor.entity.name"
|
||||||
:to="{ name: actor.entity.type, params: { entitySlug: actor.entity.slug, range: 'new', pageNumber: 1 } }"
|
:to="{ name: actor.entity.type, params: { entitySlug: actor.entity.slug, range: 'new', pageNumber: 1 } }"
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
:src="`/img/logos/${actor.entity.slug}/favicon_dark.png`"
|
:src="`/img/logos/${actor.entity.slug}/favicon_dark.png`"
|
||||||
class="favicon-icon"
|
class="favicon-icon"
|
||||||
>
|
>
|
||||||
</router-link>
|
</RouterLink>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-if="alias"
|
v-if="alias"
|
||||||
@@ -60,19 +60,26 @@
|
|||||||
></span>
|
></span>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="favorited"
|
v-show="(!stash || stash.primary) && favorited"
|
||||||
icon="heart7"
|
icon="heart7"
|
||||||
class="stash stashed"
|
class="stash stashed"
|
||||||
@click.prevent.native="unstashActor"
|
@click.prevent.native="unstashActor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="favorited === false"
|
v-show="(!stash || stash.primary) && favorited === false"
|
||||||
icon="heart8"
|
icon="heart8"
|
||||||
class="stash unstashed"
|
class="stash unstashed"
|
||||||
@click.prevent.native="stashActor"
|
@click.prevent.native="stashActor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
v-show="stash && !stash.primary"
|
||||||
|
icon="cross2"
|
||||||
|
class="stash unstash"
|
||||||
|
@click.prevent.native="unstashActor"
|
||||||
|
/>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="details"
|
class="details"
|
||||||
>
|
>
|
||||||
@@ -122,7 +129,7 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -145,12 +152,14 @@ async function stashActor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function unstashActor() {
|
async function unstashActor() {
|
||||||
this.favorited = false;
|
if (!this.stash || this.stash.primary) {
|
||||||
|
this.favorited = false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('unstashActor', {
|
await this.$store.dispatch('unstashActor', {
|
||||||
actorId: this.actor.id,
|
actorId: this.actor.id,
|
||||||
stashId: this.$store.getters.favorites.id,
|
stashId: this.stash?.id || this.$store.getters.favorites.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('stash', false);
|
this.$emit('stash', false);
|
||||||
@@ -176,6 +185,10 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
stash: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['stash'],
|
emits: ['stash'],
|
||||||
data() {
|
data() {
|
||||||
@@ -201,7 +214,7 @@ export default {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 0 3px var(--darken-weak);
|
box-shadow: 0 0 3px var(--darken-weak);
|
||||||
background: var(--background);
|
background: var(--info);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
@@ -209,6 +222,11 @@ export default {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-bottom: 150%;
|
padding-bottom: 150%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover .unstashed,
|
||||||
|
&:hover .unstash {
|
||||||
|
fill: var(--lighten);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
@@ -219,12 +237,8 @@ export default {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
color: var(--text);
|
color: var(--text-light);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.handle {
|
.handle {
|
||||||
@@ -235,7 +249,8 @@ export default {
|
|||||||
box-shadow: 0 0 3px var(--shadow);
|
box-shadow: 0 0 3px var(--shadow);
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
padding: .5rem;
|
padding: .25rem .5rem;
|
||||||
|
font-size: .9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alias {
|
.alias {
|
||||||
@@ -298,8 +313,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stash {
|
.stash {
|
||||||
width: 1.25rem;
|
width: 1.5rem;
|
||||||
height: 1.25rem;
|
height: 1.5rem;
|
||||||
padding: .25rem .25rem .5rem .5rem;
|
padding: .25rem .25rem .5rem .5rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -307,10 +322,15 @@ export default {
|
|||||||
fill: var(--lighten-weak);
|
fill: var(--lighten-weak);
|
||||||
filter: drop-shadow(0 0 2px var(--darken));
|
filter: drop-shadow(0 0 2px var(--darken));
|
||||||
|
|
||||||
&:hover,
|
&:hover.unstashed,
|
||||||
&.stashed {
|
&.stashed {
|
||||||
fill: var(--primary);
|
fill: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover.unstash {
|
||||||
|
fill: var(--text-light);
|
||||||
|
filter: drop-shadow(0 0 2px var(--darken-weak));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
|
|||||||
@@ -11,43 +11,56 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="album-body">
|
||||||
class="album-items"
|
|
||||||
:class="{ portrait }"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-for="item in albumItems"
|
v-if="entity || tag"
|
||||||
:key="item.id"
|
class="campaign-container"
|
||||||
class="item-container"
|
|
||||||
>
|
>
|
||||||
<a
|
<Campaign
|
||||||
:href="getPath(item, null, { local })"
|
:entity="entity"
|
||||||
class="item-link"
|
:tag="tag"
|
||||||
target="_blank"
|
:min-ratio="3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="album-items"
|
||||||
|
:class="{ portrait }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="item in albumItems"
|
||||||
|
:key="item.id"
|
||||||
|
class="item-container"
|
||||||
>
|
>
|
||||||
<img
|
<a
|
||||||
:src="getPath(item, 'thumbnail', { local })"
|
:href="getPath(item, null, { local })"
|
||||||
:style="{ 'background-image': getBgPath(item, 'lazy', { local }) }"
|
class="item-link"
|
||||||
:width="item.thumbnailWidth"
|
target="_blank"
|
||||||
:height="item.thumbnailHeight"
|
|
||||||
:title="item.title"
|
|
||||||
loading="lazy"
|
|
||||||
class="item image"
|
|
||||||
>
|
>
|
||||||
|
<img
|
||||||
|
:src="getPath(item, 'thumbnail', { local })"
|
||||||
|
:style="{ 'background-image': getBgPath(item, 'lazy', { local }) }"
|
||||||
|
:width="item.thumbnailWidth"
|
||||||
|
:height="item.thumbnailHeight"
|
||||||
|
:title="item.title"
|
||||||
|
loading="lazy"
|
||||||
|
class="item image"
|
||||||
|
>
|
||||||
|
|
||||||
<Logo :photo="item" />
|
<Logo :photo="item" />
|
||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
v-if="comments && item.title && item.entity"
|
v-if="comments && item.title && item.entity"
|
||||||
:to="`/${item.entity.type}/${item.entity.slug}`"
|
:to="`/${item.entity.type}/${item.entity.slug}`"
|
||||||
class="item-comment"
|
class="item-comment"
|
||||||
>{{ item.title }} for {{ item.entity.name }}</router-link>
|
>{{ item.title }} for {{ item.entity.name }}</router-link>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-else-if="comments && item.title"
|
v-else-if="comments && item.title"
|
||||||
class="item-comment"
|
class="item-comment"
|
||||||
>{{ item.title }}</span>
|
>{{ item.title }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,6 +69,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Logo from './logo.vue';
|
import Logo from './logo.vue';
|
||||||
|
import Campaign from '../campaigns/campaign.vue';
|
||||||
|
|
||||||
function albumItems() {
|
function albumItems() {
|
||||||
return this.items
|
return this.items
|
||||||
@@ -69,6 +83,7 @@ function albumItems() {
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Logo,
|
Logo,
|
||||||
|
Campaign,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
items: {
|
items: {
|
||||||
@@ -91,6 +106,14 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
entity: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
albumItems,
|
albumItems,
|
||||||
@@ -146,15 +169,30 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.album-body {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campaign-container {
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.album-items {
|
.album-items {
|
||||||
|
height: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr));
|
||||||
grid-gap: 0 1rem;
|
grid-gap: 0 1rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: auto 0;
|
margin: auto 0;
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
&.portrait {
|
&.portrait {
|
||||||
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<template v-slot:tooltip>
|
<template #tooltip>
|
||||||
<Search
|
<Search
|
||||||
content="actors"
|
content="actors"
|
||||||
@select="actor => addActor(actor)"
|
@select="actor => addActor(actor)"
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<template v-slot:tooltip>
|
<template #tooltip>
|
||||||
<Search
|
<Search
|
||||||
content="tags"
|
content="tags"
|
||||||
:defaults="['anal', 'blowbang', 'mfm', 'dp', 'gangbang', 'airtight']"
|
:defaults="['anal', 'blowbang', 'mfm', 'dp', 'gangbang', 'airtight']"
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-slot:tooltip>
|
<template #tooltip>
|
||||||
<Search
|
<Search
|
||||||
label="Search channels"
|
label="Search channels"
|
||||||
content="entities"
|
content="entities"
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<template v-slot:tooltip>
|
<template #tooltip>
|
||||||
<Search
|
<Search
|
||||||
content="stashes"
|
content="stashes"
|
||||||
@select="stash => addStash(stash)"
|
@select="stash => addStash(stash)"
|
||||||
@@ -194,6 +194,7 @@
|
|||||||
|
|
||||||
<div class="dialog-actions right">
|
<div class="dialog-actions right">
|
||||||
<button
|
<button
|
||||||
|
:disabled="actors.length === 0 && tags.length === 0 && !entity"
|
||||||
type="submit"
|
type="submit"
|
||||||
class="button button-primary"
|
class="button button-primary"
|
||||||
>Add alert</button>
|
>Add alert</button>
|
||||||
@@ -210,19 +211,19 @@ import Search from './search.vue';
|
|||||||
|
|
||||||
async function addAlert() {
|
async function addAlert() {
|
||||||
await this.$store.dispatch('addAlert', {
|
await this.$store.dispatch('addAlert', {
|
||||||
actors: this.actors.map(actor => actor.id),
|
actors: this.actors.map((actor) => actor.id),
|
||||||
tags: this.tags.map(tag => tag.id),
|
tags: this.tags.map((tag) => tag.id),
|
||||||
entity: this.entity?.id,
|
entity: this.entity?.id,
|
||||||
notify: this.notify,
|
notify: this.notify,
|
||||||
email: this.email,
|
email: this.email,
|
||||||
stashes: this.stashes.map(stash => stash.id),
|
stashes: this.stashes.map((stash) => stash.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('close', true);
|
this.$emit('close', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addActor(actor) {
|
function addActor(actor) {
|
||||||
if (!this.actors.some(selectedActor => selectedActor.id === actor.id)) {
|
if (!this.actors.some((selectedActor) => selectedActor.id === actor.id)) {
|
||||||
this.actors = this.actors.concat(actor);
|
this.actors = this.actors.concat(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +236,7 @@ function addEntity(entity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addTag(tag) {
|
function addTag(tag) {
|
||||||
if (!this.tags.some(selectedTag => selectedTag.id === tag.id)) {
|
if (!this.tags.some((selectedTag) => selectedTag.id === tag.id)) {
|
||||||
this.tags = this.tags.concat(tag);
|
this.tags = this.tags.concat(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +244,7 @@ function addTag(tag) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function removeActor(actor) {
|
function removeActor(actor) {
|
||||||
this.actors = this.actors.filter(listedActor => listedActor.id !== actor.id);
|
this.actors = this.actors.filter((listedActor) => listedActor.id !== actor.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeEntity() {
|
function removeEntity() {
|
||||||
@@ -251,11 +252,11 @@ function removeEntity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function removeTag(tag) {
|
function removeTag(tag) {
|
||||||
this.tags = this.tags.filter(listedTag => listedTag.id !== tag.id);
|
this.tags = this.tags.filter((listedTag) => listedTag.id !== tag.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addStash(stash) {
|
function addStash(stash) {
|
||||||
if (!this.stashes.some(selectedStash => selectedStash.id === stash.id)) {
|
if (!this.stashes.some((selectedStash) => selectedStash.id === stash.id)) {
|
||||||
this.stashes = this.stashes.concat(stash);
|
this.stashes = this.stashes.concat(stash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +264,7 @@ function addStash(stash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function removeStash(stash) {
|
function removeStash(stash) {
|
||||||
this.stashes = this.stashes.filter(listedStash => listedStash.id !== stash.id);
|
this.stashes = this.stashes.filter((listedStash) => listedStash.id !== stash.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -273,6 +274,7 @@ export default {
|
|||||||
Entity,
|
Entity,
|
||||||
Search,
|
Search,
|
||||||
},
|
},
|
||||||
|
emits: ['close'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actors: [],
|
actors: [],
|
||||||
@@ -284,7 +286,6 @@ export default {
|
|||||||
availableStashes: this.$store.state.auth.user.stashes,
|
availableStashes: this.$store.state.auth.user.stashes,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ['close'],
|
|
||||||
methods: {
|
methods: {
|
||||||
addActor,
|
addActor,
|
||||||
addAlert,
|
addAlert,
|
||||||
|
|||||||
@@ -43,11 +43,12 @@
|
|||||||
<script>
|
<script>
|
||||||
async function search() {
|
async function search() {
|
||||||
if (this.content === 'actors') {
|
if (this.content === 'actors') {
|
||||||
this.results = await this.$store.dispatch('searchActors', {
|
const { actors } = await this.$store.dispatch('fetchActors', {
|
||||||
query: this.query,
|
query: this.query,
|
||||||
minLength: 1,
|
|
||||||
limit: 10,
|
limit: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.results = actors;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.content === 'entities') {
|
if (this.content === 'entities') {
|
||||||
|
|||||||
@@ -35,10 +35,11 @@
|
|||||||
class="button button-primary"
|
class="button button-primary"
|
||||||
>Log in</button>
|
>Log in</button>
|
||||||
|
|
||||||
<router-link
|
<RouterLink
|
||||||
|
v-if="$store.state.auth.signup"
|
||||||
:to="{ name: 'signup', query: { ref: $route.query.ref } }"
|
:to="{ name: 'signup', query: { ref: $route.query.ref } }"
|
||||||
class="link link-external signup"
|
class="link link-external signup"
|
||||||
>Sign up</router-link>
|
>Sign up</RouterLink>
|
||||||
</template>
|
</template>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
@@ -65,7 +66,7 @@ async function login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mounted() {
|
function mounted() {
|
||||||
if (!this.$store.state.auth.enabled) {
|
if (!this.$store.state.auth.login) {
|
||||||
this.$router.replace({ name: 'not-found' });
|
this.$router.replace({ name: 'not-found' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,11 @@
|
|||||||
class="button button-primary"
|
class="button button-primary"
|
||||||
>Sign up</button>
|
>Sign up</button>
|
||||||
|
|
||||||
<router-link
|
<RouterLink
|
||||||
|
v-if="$store.state.auth.login"
|
||||||
:to="{ name: 'login', query: { ref: $route.query.ref } }"
|
:to="{ name: 'login', query: { ref: $route.query.ref } }"
|
||||||
class="link link-external login"
|
class="link link-external login"
|
||||||
>Log in</router-link>
|
>Log in</RouterLink>
|
||||||
</template>
|
</template>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
@@ -74,7 +75,7 @@ async function signup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mounted() {
|
function mounted() {
|
||||||
if (!this.$store.state.auth.enabled) {
|
if (!this.$store.state.auth.signup) {
|
||||||
this.$router.replace({ name: 'not-found' });
|
this.$router.replace({ name: 'not-found' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
152
assets/components/campaigns/campaign.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<a
|
||||||
|
v-if="campaign"
|
||||||
|
:href="campaign.url || campaign.affiliate?.url"
|
||||||
|
target="_blank"
|
||||||
|
class="campaign"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-if="campaign.banner.entity.type === 'network' || !campaign.banner.entity.parent"
|
||||||
|
:src="`/img/banners/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`"
|
||||||
|
:width="campaign.banner.width"
|
||||||
|
:height="campaign.banner.height"
|
||||||
|
class="campaign-banner"
|
||||||
|
>
|
||||||
|
|
||||||
|
<img
|
||||||
|
v-if="campaign.banner.entity.type === 'channel' && campaign.banner.entity.parent?.type === 'network'"
|
||||||
|
:src="`/img/banners/${campaign.banner.entity.parent.slug}/${campaign.banner.entity.slug}/${campaign.banner.id}.${campaign.banner.type || 'jpg'}`"
|
||||||
|
:width="campaign.banner.width"
|
||||||
|
:height="campaign.banner.height"
|
||||||
|
class="campaign-banner"
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function ratioFilter(banner) {
|
||||||
|
if (!banner) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.minHeight && banner.height < this.minHeight) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.maxHeight && banner.height > this.minHeight) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.minRatio && banner.ratio < this.minRatio) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.maxRatio && banner.ratio > this.maxRatio) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entityCampaign() {
|
||||||
|
const bannerCampaigns = this.entity.campaigns
|
||||||
|
.concat(this.entity.children?.flatMap(child => child.campaigns))
|
||||||
|
.concat(this.entity.parent?.campaigns)
|
||||||
|
.filter(campaignX => campaignX && this.ratioFilter(campaignX.banner));
|
||||||
|
|
||||||
|
if (bannerCampaigns.length > 0) {
|
||||||
|
const randomCampaign = bannerCampaigns[Math.floor(Math.random() * bannerCampaigns.length)];
|
||||||
|
|
||||||
|
this.$emit('campaign', randomCampaign);
|
||||||
|
return randomCampaign;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('campaign', null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tagCampaign() {
|
||||||
|
const campaignBanners = this.tag.banners.filter(banner => banner.campaigns.length > 0 && this.ratioFilter(banner));
|
||||||
|
const banner = campaignBanners[Math.floor(Math.random() * campaignBanners.length)];
|
||||||
|
|
||||||
|
if (banner?.campaigns.length > 0) {
|
||||||
|
const randomCampaign = {
|
||||||
|
...banner.campaigns[Math.floor(Math.random() * banner.campaigns.length)],
|
||||||
|
banner,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$emit('campaign', randomCampaign);
|
||||||
|
return randomCampaign;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('campaign', null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function campaign() {
|
||||||
|
if (this.entity) {
|
||||||
|
return this.entityCampaign();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.tag) {
|
||||||
|
return this.tagCampaign();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('campaign', null); // allow parent to toggle campaigns depending on availability
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
entity: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
minHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
maxHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
minRatio: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
maxRatio: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['campaign'],
|
||||||
|
computed: {
|
||||||
|
campaign,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
entityCampaign,
|
||||||
|
ratioFilter,
|
||||||
|
tagCampaign,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.campaign {
|
||||||
|
height: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campaign-banner {
|
||||||
|
height: auto;
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
54
assets/components/container/container.vue
Normal file → Executable file
@@ -8,35 +8,40 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<transition name="slide">
|
<transition name="slide">
|
||||||
<Sidebar
|
<Sidebar v-if="showSidebar" />
|
||||||
v-if="showSidebar"
|
|
||||||
@toggle-sidebar="(state) => toggleSidebar(state)"
|
|
||||||
@show-filters="(state) => toggleFilters(state)"
|
|
||||||
/>
|
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
<Header
|
<Header />
|
||||||
@toggle-sidebar="(state) => toggleSidebar(state)"
|
|
||||||
@show-filters="(state) => toggleFilters(state)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p
|
<p
|
||||||
v-if="config.showDisclaimer"
|
v-if="config.showDisclaimer"
|
||||||
class="disclaimer"
|
class="disclaimer"
|
||||||
>{{ config.disclaimer }}</p>
|
v-html="config.disclaimer"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p
|
||||||
|
v-if="config.showAnnouncement"
|
||||||
|
class="announcement"
|
||||||
|
v-html="config.announcement"
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref="content"
|
ref="content"
|
||||||
class="content"
|
class="content"
|
||||||
@scroll="scroll"
|
@scroll="scroll"
|
||||||
>
|
>
|
||||||
<router-view @scroll="scrollToTop" />
|
<RouterView @scroll="scrollToTop" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Filters
|
<Filters
|
||||||
v-if="showFilters"
|
v-if="showFilters"
|
||||||
@close="toggleFilters(false)"
|
@close="toggleFilters(false)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Settings
|
||||||
|
v-if="showSettings"
|
||||||
|
@close="toggleSettings(false)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -45,6 +50,7 @@ import Warning from './warning.vue';
|
|||||||
import Header from '../header/header.vue';
|
import Header from '../header/header.vue';
|
||||||
import Sidebar from '../sidebar/sidebar.vue';
|
import Sidebar from '../sidebar/sidebar.vue';
|
||||||
import Filters from '../filters/filters.vue';
|
import Filters from '../filters/filters.vue';
|
||||||
|
import Settings from '../settings/settings.vue';
|
||||||
|
|
||||||
function toggleSidebar(state) {
|
function toggleSidebar(state) {
|
||||||
this.showSidebar = typeof state === 'boolean' ? state : !this.showSidebar;
|
this.showSidebar = typeof state === 'boolean' ? state : !this.showSidebar;
|
||||||
@@ -55,6 +61,11 @@ function toggleFilters(state) {
|
|||||||
this.showSidebar = false;
|
this.showSidebar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSettings(state) {
|
||||||
|
this.showSettings = state;
|
||||||
|
this.showSidebar = false;
|
||||||
|
}
|
||||||
|
|
||||||
async function setConsent(consent, includeQueer) {
|
async function setConsent(consent, includeQueer) {
|
||||||
if (consent) {
|
if (consent) {
|
||||||
this.showWarning = false;
|
this.showWarning = false;
|
||||||
@@ -62,7 +73,7 @@ async function setConsent(consent, includeQueer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (includeQueer) {
|
if (includeQueer) {
|
||||||
this.$store.dispatch('setTagFilter', this.$store.state.ui.tagFilter.filter(tag => !['gay', 'bisexual', 'transsexual'].includes(tag)));
|
this.$store.dispatch('setTagFilter', this.$store.state.ui.tagFilter.filter((tag) => !['gay', 'bisexual', 'transsexual'].includes(tag)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +99,9 @@ function scrollToTop() {
|
|||||||
function mounted() {
|
function mounted() {
|
||||||
document.addEventListener('click', this.blur);
|
document.addEventListener('click', this.blur);
|
||||||
window.addEventListener('resize', this.resize);
|
window.addEventListener('resize', this.resize);
|
||||||
|
|
||||||
|
this.events.on('toggleSettings', this.toggleSettings);
|
||||||
|
this.events.on('toggleSidebar', this.toggleSidebar);
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeUnmount() {
|
function beforeUnmount() {
|
||||||
@@ -101,12 +115,15 @@ export default {
|
|||||||
Sidebar,
|
Sidebar,
|
||||||
Warning,
|
Warning,
|
||||||
Filters,
|
Filters,
|
||||||
|
Settings,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showSidebar: false,
|
showSidebar: false,
|
||||||
showWarning: localStorage.getItem('consent') !== window.env.sessionId,
|
showWarning: localStorage.getItem('consent') !== window.env.sessionId,
|
||||||
showFilters: false,
|
showFilters: false,
|
||||||
|
showSettings: false,
|
||||||
|
selected: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted,
|
mounted,
|
||||||
@@ -114,6 +131,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
toggleFilters,
|
toggleFilters,
|
||||||
|
toggleSettings,
|
||||||
setConsent,
|
setConsent,
|
||||||
blur,
|
blur,
|
||||||
resize,
|
resize,
|
||||||
@@ -183,13 +201,21 @@ export default {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.disclaimer {
|
.disclaimer,
|
||||||
|
.announcement {
|
||||||
padding: .5rem 1rem;
|
padding: .5rem 1rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
background: var(--warn);
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
box-shadow: inset 0 0 3px var(--darken-weak);
|
box-shadow: inset 0 0 3px var(--darken-weak);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disclaimer {
|
||||||
|
background: var(--warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement {
|
||||||
|
background: var(--notice);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -67,12 +67,13 @@ export default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background: var(--darken-censor);
|
background: var(--background-censor);
|
||||||
|
backdrop-filter: blur(.25rem);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
color: var(--text-light);
|
color: var(--text);
|
||||||
width: 50rem;
|
width: 50rem;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -87,8 +88,8 @@ export default {
|
|||||||
.title {
|
.title {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
margin: 1rem 0 0 0;
|
margin: 1rem 0;
|
||||||
color: var(--text-light);
|
color: var(--text);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ export default {
|
|||||||
.rules {
|
.rules {
|
||||||
margin: 0 0 0 1rem;
|
margin: 0 0 0 1rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
text-shadow: 0 0 3px var(--darken-extreme);
|
text-shadow: 0 0 3px var(--highlight-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule {
|
.rule {
|
||||||
@@ -134,16 +135,16 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border: none;
|
border: none;
|
||||||
|
border-radius: .25rem;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 1rem;
|
|
||||||
color: var(--lighten-strong);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: border .5s ease;
|
transition: border-radius .2s ease;
|
||||||
|
|
||||||
&.leave {
|
&.leave {
|
||||||
|
color: var(--shadow-strong);
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
||||||
@@ -151,7 +152,11 @@ export default {
|
|||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
margin: 0 1rem 0 0;
|
margin: 0 1rem 0 0;
|
||||||
fill: var(--lighten);
|
fill: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +166,7 @@ export default {
|
|||||||
|
|
||||||
&.straight,
|
&.straight,
|
||||||
&.queer {
|
&.queer {
|
||||||
|
color: var(--lighten-strong);
|
||||||
background: var(--darken-censor);
|
background: var(--darken-censor);
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
@@ -169,10 +175,18 @@ export default {
|
|||||||
height: calc(100% + .25rem);
|
height: calc(100% + .25rem);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
border-radius: 1.1rem;
|
|
||||||
filter: blur(.25rem);
|
filter: blur(.25rem);
|
||||||
transition: filter .2s ease;
|
transition: filter .2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--text-light);
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
.button-sub {
|
||||||
|
color: var(--text-light);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.straight:before {
|
&.straight:before {
|
||||||
@@ -188,14 +202,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--text-light);
|
|
||||||
|
|
||||||
.button-sub {
|
|
||||||
color: var(--lighten-strong);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
fill: var(--text-light);
|
fill: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.straight:before,
|
&.straight:before,
|
||||||
@@ -225,7 +233,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preferences {
|
.preferences {
|
||||||
color: var(--lighten);
|
color: var(--shadow);
|
||||||
display: block;
|
display: block;
|
||||||
padding: .5rem 0 1rem 0;
|
padding: .5rem 0 1rem 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -234,11 +242,11 @@ export default {
|
|||||||
|
|
||||||
@media(max-width: $breakpoint) {
|
@media(max-width: $breakpoint) {
|
||||||
.title {
|
.title {
|
||||||
font-size: 1.5rem;
|
font-size: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
width: 5rem;
|
width: 5.75rem;
|
||||||
margin: 0 .5rem 0 0;
|
margin: 0 .5rem 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ export default {
|
|||||||
::v-deep(.dialog-body) {
|
::v-deep(.dialog-body) {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep(.dialog-section:not(:last-child)) {
|
::v-deep(.dialog-section:not(:last-child)) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
>
|
>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<a
|
<a
|
||||||
:href="entity.url"
|
:href="entityUrl"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
class="link link-child"
|
class="link link-child"
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
>{{ tag.name }}</li>
|
>{{ tag.name }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<router-link
|
<RouterLink
|
||||||
v-if="entity.parent"
|
v-if="entity.parent"
|
||||||
:to="`/${entity.parent.type}/${entity.parent.slug}`"
|
:to="`/${entity.parent.type}/${entity.parent.slug}`"
|
||||||
class="link link-parent"
|
class="link link-parent"
|
||||||
@@ -72,7 +72,9 @@
|
|||||||
v-else
|
v-else
|
||||||
class="name parent-name"
|
class="name parent-name"
|
||||||
>{{ entity.parent.name }}</h3>
|
>{{ entity.parent.name }}</h3>
|
||||||
</router-link>
|
|
||||||
|
<Icon icon="device_hub" />
|
||||||
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -94,6 +96,13 @@
|
|||||||
/>
|
/>
|
||||||
</Scroll>
|
</Scroll>
|
||||||
|
|
||||||
|
<div class="campaign-container">
|
||||||
|
<Campaign
|
||||||
|
:entity="entity"
|
||||||
|
:min-ratio="3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<FilterBar
|
<FilterBar
|
||||||
ref="filter"
|
ref="filter"
|
||||||
:fetch-releases="fetchEntity"
|
:fetch-releases="fetchEntity"
|
||||||
@@ -102,7 +111,10 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="releases">
|
<div class="releases">
|
||||||
<Releases :releases="entity.releases" />
|
<Releases
|
||||||
|
:releases="entity.releases"
|
||||||
|
:done="done"
|
||||||
|
/>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
:items-total="totalCount"
|
:items-total="totalCount"
|
||||||
@@ -122,8 +134,12 @@ import Pagination from '../pagination/pagination.vue';
|
|||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
import Children from './children.vue';
|
import Children from './children.vue';
|
||||||
import Scroll from '../scroll/scroll.vue';
|
import Scroll from '../scroll/scroll.vue';
|
||||||
|
import Campaign from '../campaigns/campaign.vue';
|
||||||
|
|
||||||
async function fetchEntity(scroll = true) {
|
async function fetchEntity(scroll = true) {
|
||||||
|
this.done = false;
|
||||||
|
this.entityUrl = null;
|
||||||
|
|
||||||
const { entity, totalCount } = await this.$store.dispatch('fetchEntityBySlugAndType', {
|
const { entity, totalCount } = await this.$store.dispatch('fetchEntityBySlugAndType', {
|
||||||
entitySlug: this.$route.params.entitySlug,
|
entitySlug: this.$route.params.entitySlug,
|
||||||
entityType: this.$route.name,
|
entityType: this.$route.name,
|
||||||
@@ -137,6 +153,17 @@ async function fetchEntity(scroll = true) {
|
|||||||
|
|
||||||
this.pageTitle = entity.name;
|
this.pageTitle = entity.name;
|
||||||
|
|
||||||
|
const campaign = entity.campaigns.find((campaignX) => !campaignX.banner)
|
||||||
|
|| entity.parent?.campaigns.find((campaignX) => !campaignX.banner);
|
||||||
|
|
||||||
|
const affiliateParams = new URLSearchParams({
|
||||||
|
...(entity.url && Object.fromEntries(new URL(entity.url).searchParams)), // preserve any query in entity URL, e.g. ?siteId=5
|
||||||
|
...(campaign?.affiliate?.parameters && Object.fromEntries(new URLSearchParams(campaign.affiliate.parameters))), // append affiliate parameters
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
this.entityUrl = campaign?.url || campaign?.affiliate?.url || `${entity.url}${campaign?.affiliate?.parameters ? `?${affiliateParams}` : ''}`;
|
||||||
|
this.done = true;
|
||||||
|
|
||||||
if (scroll && this.$refs.filter?.$el) {
|
if (scroll && this.$refs.filter?.$el) {
|
||||||
this.$refs.filter.$el.scrollIntoView();
|
this.$refs.filter.$el.scrollIntoView();
|
||||||
}
|
}
|
||||||
@@ -160,14 +187,17 @@ export default {
|
|||||||
Children,
|
Children,
|
||||||
Releases,
|
Releases,
|
||||||
Scroll,
|
Scroll,
|
||||||
|
Campaign,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
entity: null,
|
entity: null,
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
totalCount: null,
|
totalCount: null,
|
||||||
|
done: false,
|
||||||
limit: Number(this.$route.query.limit) || 20,
|
limit: Number(this.$route.query.limit) || 20,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
|
entityUrl: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -192,7 +222,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
max-width: 20rem;
|
max-width: 15rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -201,6 +231,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.link-child {
|
.link-child {
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
fill: var(--lighten);
|
fill: var(--lighten);
|
||||||
margin: 0 0 0 1rem;
|
margin: 0 0 0 1rem;
|
||||||
@@ -213,7 +245,15 @@ export default {
|
|||||||
|
|
||||||
.link-parent {
|
.link-parent {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
flex-shrink: 0;
|
||||||
margin: 0 0 0 3rem;
|
margin: 0 0 0 3rem;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
fill: var(--lighten);
|
||||||
|
margin: 0 .5rem 0 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@@ -223,7 +263,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logo-child {
|
.logo-child {
|
||||||
height: 2rem;
|
height: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-parent {
|
.logo-parent {
|
||||||
@@ -232,6 +272,7 @@ export default {
|
|||||||
|
|
||||||
.favicon {
|
.favicon {
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-inner {
|
.content-inner {
|
||||||
@@ -243,6 +284,12 @@ export default {
|
|||||||
background: var(--profile);
|
background: var(--profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.campaign-container {
|
||||||
|
background: var(--background-dim);
|
||||||
|
text-align: center;
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.releases {
|
.releases {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,39 +4,49 @@
|
|||||||
:title="entity.name"
|
:title="entity.name"
|
||||||
class="tile"
|
class="tile"
|
||||||
>
|
>
|
||||||
<template v-if="entity.hasLogo">
|
<div class="tile-logo">
|
||||||
<img
|
<template v-if="entity.hasLogo">
|
||||||
v-if="entity.type === 'network' || entity.independent"
|
<img
|
||||||
:src="`/img/logos/${entity.slug}/thumbs/network.png`"
|
v-if="entity.type === 'network' || entity.independent"
|
||||||
:alt="entity.name"
|
:src="`/img/logos/${entity.slug}/thumbs/network.png`"
|
||||||
loading="lazy"
|
:alt="entity.name"
|
||||||
class="logo"
|
loading="lazy"
|
||||||
@load="$emit('load', $event)"
|
class="logo"
|
||||||
>
|
@load="$emit('load', $event)"
|
||||||
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-else-if="entity.parent"
|
v-else-if="entity.parent"
|
||||||
:src="`/img/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`"
|
:src="`/img/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`"
|
||||||
:alt="entity.name"
|
:alt="entity.name"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="logo"
|
class="logo"
|
||||||
@load="$emit('load', $event)"
|
@load="$emit('load', $event)"
|
||||||
>
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
|
v-else
|
||||||
|
:src="`/img/logos/${entity.slug}/thumbs/${entity.slug}.png`"
|
||||||
|
:alt="entity.name"
|
||||||
|
loading="lazy"
|
||||||
|
class="logo"
|
||||||
|
@load="$emit('load', $event)"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<span
|
||||||
v-else
|
v-else
|
||||||
:src="`/img/logos/${entity.slug}/thumbs/${entity.slug}.png`"
|
class="name"
|
||||||
:alt="entity.name"
|
>{{ entity.name }}</span>
|
||||||
loading="lazy"
|
</div>
|
||||||
class="logo"
|
|
||||||
@load="$emit('load', $event)"
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-else
|
v-if="entity.childrenTotal > 0 || typeof entity.sceneTotal !== 'undefined'"
|
||||||
class="name"
|
class="count"
|
||||||
>{{ entity.name }}</span>
|
>
|
||||||
|
<span v-if="typeof entity.sceneTotal !== 'undefined'">{{ entity.sceneTotal }} scenes</span>
|
||||||
|
<span v-if="entity.type === 'network'">{{ entity.childrenTotal }} channels</span>
|
||||||
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -59,15 +69,28 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--tile);
|
background: var(--tile);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: .5rem 1rem;
|
|
||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
|
position: relative;
|
||||||
box-shadow: 0 0 3px rgba(0, 0, 0, .25);
|
box-shadow: 0 0 3px rgba(0, 0, 0, .25);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover .count {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-logo {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@@ -75,6 +98,7 @@ export default {
|
|||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
@@ -82,4 +106,20 @@ export default {
|
|||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -1.75rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: .25rem .5rem;
|
||||||
|
background: var(--darken-strong);
|
||||||
|
box-shadow: 0 0 3px var(--darken);
|
||||||
|
color: var(--text-light);
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 1px 1px var(--darken);
|
||||||
|
transition: bottom .1s ease;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,41 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="filter-bar noselect">
|
<div class="filter-bar noselect">
|
||||||
<div class="sort">
|
<div class="sort">
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="{ params: { range: 'latest', pageNumber: 1 }, query: $route.query }"
|
v-for="section in ranges"
|
||||||
:class="{ active: $route.name === 'latest' || range === 'latest' }"
|
:key="section"
|
||||||
|
:to="{ params: { range: section, pageNumber: 1 }, query: $route.query }"
|
||||||
|
:class="{ active: $route.name === section || range === section }"
|
||||||
class="range range-button"
|
class="range range-button"
|
||||||
>Latest</router-link>
|
>{{ section }}</RouterLink>
|
||||||
|
|
||||||
<router-link
|
|
||||||
:to="{ params: { range: 'upcoming', pageNumber: 1 }, query: $route.query }"
|
|
||||||
:class="{ active: $route.name === 'upcoming' || range === 'upcoming' }"
|
|
||||||
class="range-button"
|
|
||||||
>Upcoming</router-link>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
:to="{ params: { range: 'new', pageNumber: 1 }, query: $route.query }"
|
|
||||||
:class="{ active: $route.name === 'new' || range === 'new' }"
|
|
||||||
class="range-button"
|
|
||||||
>New</router-link>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="itemsTotal && showTotal"
|
||||||
|
class="total"
|
||||||
|
>{{ itemsTotal }} <template v-if="itemsTotal === 1">scene</template><template v-else>scenes</template></span>
|
||||||
|
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<ActorFilter
|
<ActorFilter
|
||||||
class="filters-filter"
|
class="filters-filter"
|
||||||
:filter="filter"
|
|
||||||
:available-actors="availableActors"
|
:available-actors="availableActors"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ChannelFilter
|
<ChannelFilter
|
||||||
class="filters-filter"
|
class="filters-filter"
|
||||||
:filter="filter"
|
|
||||||
:available-channels="availableChannels"
|
:available-channels="availableChannels"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TagFilter
|
<TagFilter
|
||||||
class="filters-filter"
|
class="filters-filter"
|
||||||
:filter="filter"
|
|
||||||
:available-tags="availableTags"
|
:available-tags="availableTags"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,10 +41,6 @@ import ActorFilter from './actor-filter.vue';
|
|||||||
import ChannelFilter from './channel-filter.vue';
|
import ChannelFilter from './channel-filter.vue';
|
||||||
import TagFilter from './tag-filter.vue';
|
import TagFilter from './tag-filter.vue';
|
||||||
|
|
||||||
function filter(state) {
|
|
||||||
return state.ui.filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
function range() {
|
function range() {
|
||||||
return this.$route.params.range;
|
return this.$route.params.range;
|
||||||
}
|
}
|
||||||
@@ -88,6 +76,10 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
showTotal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
itemsTotal: {
|
itemsTotal: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
@@ -96,6 +88,10 @@ export default {
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 10,
|
default: 10,
|
||||||
},
|
},
|
||||||
|
ranges: {
|
||||||
|
type: Array,
|
||||||
|
default: () => ['latest', 'upcoming', 'new'],
|
||||||
|
},
|
||||||
availableTags: {
|
availableTags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
@@ -111,7 +107,6 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
filter,
|
|
||||||
range,
|
range,
|
||||||
batch,
|
batch,
|
||||||
}),
|
}),
|
||||||
@@ -126,6 +121,43 @@ export default {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import 'breakpoints';
|
@import 'breakpoints';
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
color: var(--shadow);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.filter-applied {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: .75rem .5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
&.empty {
|
||||||
|
color: var(--shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--shadow);
|
||||||
|
margin: -.1rem 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.applied {
|
||||||
|
color: var(--shadow-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--shadow-strong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.filter-mode {
|
.filter-mode {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--shadow);
|
color: var(--shadow);
|
||||||
@@ -246,6 +278,10 @@ export default {
|
|||||||
.filters-filter:last-child .filter {
|
.filters-filter:last-child .filter {
|
||||||
padding: .5rem 0 .5rem .5rem;
|
padding: .5rem 0 .5rem .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.total {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -267,43 +303,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep(.filter) {
|
|
||||||
color: var(--shadow);
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.filter-applied {
|
|
||||||
flex-grow: 1;
|
|
||||||
padding: .75rem .5rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
&.empty {
|
|
||||||
color: var(--shadow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
fill: var(--shadow);
|
|
||||||
margin: -.1rem 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.applied {
|
|
||||||
color: var(--shadow-strong);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
fill: var(--shadow-strong);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters {
|
.filters {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@@ -324,6 +323,7 @@ export default {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border: solid 1px transparent;
|
border: solid 1px transparent;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
|
text-transform: capitalize;
|
||||||
|
|
||||||
&:hover:not(.active) {
|
&:hover:not(.active) {
|
||||||
color: var(--shadow-strong);
|
color: var(--shadow-strong);
|
||||||
@@ -356,4 +356,11 @@ export default {
|
|||||||
margin: 0 1rem 0 0;
|
margin: 0 1rem 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.total {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 1rem;
|
||||||
|
color: var(--shadow);
|
||||||
|
font-size: .8rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
>Tags</div>
|
>Tags</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-slot:tooltip>
|
<template #tooltip>
|
||||||
<div
|
<div
|
||||||
class="filter-options"
|
class="filter-options"
|
||||||
@click.stop
|
@click.stop
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<script>
|
<script>
|
||||||
function getNewRange(tag) {
|
function getNewRange(tag) {
|
||||||
if (this.selectedTags.includes(tag)) {
|
if (this.selectedTags.includes(tag)) {
|
||||||
return { tags: this.selectedTags.filter(selectedTag => selectedTag !== tag).join(',') || undefined };
|
return { tags: this.selectedTags.filter((selectedTag) => selectedTag !== tag).join(',') || undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { tags: this.selectedTags.concat(tag).join(',') };
|
return { tags: this.selectedTags.concat(tag).join(',') };
|
||||||
@@ -82,10 +82,6 @@ function selectedTags() {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
filter: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
compact: {
|
compact: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<ul class="menu-items noselect">
|
<ul class="menu-items noselect">
|
||||||
<router-link
|
<RouterLink
|
||||||
v-if="auth && me"
|
v-if="login && me"
|
||||||
:to="{ name: 'user', params: { username: me.username } }"
|
:to="{ name: 'user', params: { username: me.username } }"
|
||||||
class="menu-username"
|
class="menu-username"
|
||||||
>{{ me.username }}</router-link>
|
>{{ me.username }}</RouterLink>
|
||||||
|
|
||||||
<router-link
|
<RouterLink
|
||||||
v-else-if="auth"
|
v-if="me && favorites"
|
||||||
|
:to="{ name: 'stash', params: { stashId: favorites.id, range: 'scenes', pageNumber: 1 } }"
|
||||||
|
class="menu-item"
|
||||||
|
><Icon icon="heart7" />Favorites</RouterLink>
|
||||||
|
|
||||||
|
<RouterLink
|
||||||
|
v-else-if="login"
|
||||||
:to="{ name: 'login', query: { ref: $route.path } }"
|
:to="{ name: 'login', query: { ref: $route.path } }"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
<Icon icon="enter2" />Log in
|
<Icon icon="enter2" />Log in
|
||||||
</router-link>
|
</RouterLink>
|
||||||
|
|
||||||
<li
|
|
||||||
v-if="auth && me"
|
|
||||||
class="menu-item"
|
|
||||||
@click.stop="$store.dispatch('logout')"
|
|
||||||
>
|
|
||||||
<Icon icon="exit2" />Log out
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li
|
<li
|
||||||
v-show="!sfw"
|
v-show="!sfw"
|
||||||
@@ -74,6 +72,14 @@
|
|||||||
>
|
>
|
||||||
<Icon icon="filter" />Filters
|
<Icon icon="filter" />Filters
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li
|
||||||
|
v-if="login && me"
|
||||||
|
class="menu-item"
|
||||||
|
@click.stop="$store.dispatch('logout')"
|
||||||
|
>
|
||||||
|
<Icon icon="exit2" />Log out
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -89,8 +95,16 @@ function theme(state) {
|
|||||||
return state.ui.theme;
|
return state.ui.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
function auth(state) {
|
function login(state) {
|
||||||
return state.auth.enabled;
|
return state.auth.login;
|
||||||
|
}
|
||||||
|
|
||||||
|
function signup(state) {
|
||||||
|
return state.auth.signup;
|
||||||
|
}
|
||||||
|
|
||||||
|
function favorites() {
|
||||||
|
return this.me?.stashes.find(stash => stash.primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
function me(state) {
|
function me(state) {
|
||||||
@@ -108,11 +122,13 @@ function setSfw(enabled) {
|
|||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
auth,
|
login,
|
||||||
|
signup,
|
||||||
sfw,
|
sfw,
|
||||||
theme,
|
theme,
|
||||||
me,
|
me,
|
||||||
}),
|
}),
|
||||||
|
favorites,
|
||||||
},
|
},
|
||||||
emits: ['showFilters'],
|
emits: ['showFilters'],
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -8,9 +8,13 @@
|
|||||||
:items-total="totalCount"
|
:items-total="totalCount"
|
||||||
:items-per-page="limit"
|
:items-per-page="limit"
|
||||||
:content="$refs.content"
|
:content="$refs.content"
|
||||||
|
:show-total="false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Releases :releases="releases" />
|
<Releases
|
||||||
|
:releases="releases"
|
||||||
|
:done="done"
|
||||||
|
/>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
v-if="totalCount > 0"
|
v-if="totalCount > 0"
|
||||||
@@ -30,6 +34,8 @@ import Releases from '../releases/releases.vue';
|
|||||||
import Pagination from '../pagination/pagination.vue';
|
import Pagination from '../pagination/pagination.vue';
|
||||||
|
|
||||||
async function fetchReleases(scroll = true) {
|
async function fetchReleases(scroll = true) {
|
||||||
|
this.done = false;
|
||||||
|
|
||||||
const { releases, totalCount } = await this.$store.dispatch('fetchReleases', {
|
const { releases, totalCount } = await this.$store.dispatch('fetchReleases', {
|
||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
range: this.$route.params.range,
|
range: this.$route.params.range,
|
||||||
@@ -38,6 +44,7 @@ async function fetchReleases(scroll = true) {
|
|||||||
|
|
||||||
this.totalCount = totalCount;
|
this.totalCount = totalCount;
|
||||||
this.releases = releases;
|
this.releases = releases;
|
||||||
|
this.done = true;
|
||||||
|
|
||||||
if (scroll) {
|
if (scroll) {
|
||||||
this.$refs.filter?.$el.scrollIntoView();
|
this.$refs.filter?.$el.scrollIntoView();
|
||||||
@@ -64,6 +71,7 @@ export default {
|
|||||||
limit: 30,
|
limit: 30,
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
from: null,
|
from: null,
|
||||||
|
done: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
90
assets/components/loading/ellipsis.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div class="load-container">
|
||||||
|
<div class="load-ellipsis">
|
||||||
|
<div />
|
||||||
|
<div />
|
||||||
|
<div />
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.load-container {
|
||||||
|
display: inline-flex;
|
||||||
|
position: relative;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-ellipsis {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 5rem;
|
||||||
|
height: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-ellipsis div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: .75rem;
|
||||||
|
height: .75rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-ellipsis div:nth-child(1) {
|
||||||
|
left: .5rem;
|
||||||
|
animation: load-ellipsis1 0.5s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-ellipsis div:nth-child(2) {
|
||||||
|
left: .5rem;
|
||||||
|
animation: load-ellipsis2 0.5s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-ellipsis div:nth-child(3) {
|
||||||
|
left: 2rem;
|
||||||
|
animation: load-ellipsis3 0.5s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-ellipsis div:nth-child(4) {
|
||||||
|
left: 3.5rem;
|
||||||
|
animation: load-ellipsis4 0.5s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes load-ellipsis1 {
|
||||||
|
0% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes load-ellipsis2 {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0) scale(0.5);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(1.5rem, 0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes load-ellipsis3 {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0) scale(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(1.5rem, 0) scale(0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes load-ellipsis4 {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,44 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="networks">
|
<div class="networks">
|
||||||
<form
|
<div class="content-inner">
|
||||||
class="search"
|
<SearchBar
|
||||||
@submit.prevent="searchEntities"
|
:placeholder="`Search ${channelCount} channels in ${entities.length} networks`"
|
||||||
>
|
:eager="true"
|
||||||
<input
|
/>
|
||||||
v-model="query"
|
|
||||||
:placeholder="`Find ${channelCount} channels in ${entities.length} networks`"
|
<span
|
||||||
class="query"
|
v-if="done && entities.length === 0"
|
||||||
@input="searchEntities"
|
class="empty"
|
||||||
|
>No results for "{{ $route.query.query }}"</span>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="entity-tiles"
|
||||||
>
|
>
|
||||||
|
<Entity
|
||||||
<button
|
v-for="entity in entities"
|
||||||
type="submit"
|
:key="entity.parent ? `entity-tile-${entity.parent.slug}-${entity.slug}` : `entity-tile-${entity.slug}`"
|
||||||
class="search-button"
|
:entity="entity"
|
||||||
><Icon icon="search" /></button>
|
/>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="query.length > 0"
|
|
||||||
class="entity-tiles"
|
|
||||||
>
|
|
||||||
<Entity
|
|
||||||
v-for="entity in searchResults"
|
|
||||||
:key="`${entity.type}-tile-${entity.slug}`"
|
|
||||||
:entity="entity"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span v-if="searchResults.length === 0">No results for "{{ query }}"</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="entity-tiles"
|
|
||||||
>
|
|
||||||
<Entity
|
|
||||||
v-for="entity in entities"
|
|
||||||
:key="`entity-tile-${entity.slug}`"
|
|
||||||
:entity="entity"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
@@ -47,21 +29,39 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Entity from '../entities/tile.vue';
|
import Entity from '../entities/tile.vue';
|
||||||
|
import SearchBar from '../search/bar.vue';
|
||||||
|
|
||||||
async function searchEntities() {
|
async function fetchEntities() {
|
||||||
this.searchResults = await this.$store.dispatch('searchEntities', {
|
if (this.$route.query.query) {
|
||||||
query: this.query,
|
await this.searchEntities();
|
||||||
limit: 50,
|
return;
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
this.done = false;
|
||||||
|
|
||||||
async function mounted() {
|
|
||||||
this.entities = await this.$store.dispatch('fetchEntities', {
|
this.entities = await this.$store.dispatch('fetchEntities', {
|
||||||
type: 'network',
|
type: 'network',
|
||||||
entitySlugs: [],
|
entitySlugs: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pageTitle = 'Networks';
|
this.done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchEntities() {
|
||||||
|
this.done = false;
|
||||||
|
|
||||||
|
this.entities = await this.$store.dispatch('searchEntities', {
|
||||||
|
query: this.$route.query.query,
|
||||||
|
limit: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mounted() {
|
||||||
|
this.pageTitle = 'Channels';
|
||||||
|
|
||||||
|
await this.fetchEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
function channelCount() {
|
function channelCount() {
|
||||||
@@ -71,20 +71,24 @@ function channelCount() {
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Entity,
|
Entity,
|
||||||
|
SearchBar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
query: '',
|
done: false,
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
entities: [],
|
entities: [],
|
||||||
searchResults: [],
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
channelCount,
|
channelCount,
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route: fetchEntities,
|
||||||
|
},
|
||||||
mounted,
|
mounted,
|
||||||
methods: {
|
methods: {
|
||||||
|
fetchEntities,
|
||||||
searchEntities,
|
searchEntities,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -94,49 +98,17 @@ export default {
|
|||||||
@import 'theme';
|
@import 'theme';
|
||||||
|
|
||||||
.networks {
|
.networks {
|
||||||
padding: 0 1rem;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-inner {
|
||||||
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
display: flex;
|
margin: 1rem 0 0 0;
|
||||||
width: 100%;
|
|
||||||
max-width: 40rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.query {
|
|
||||||
color: var(--text);
|
|
||||||
background: var(--background);
|
|
||||||
flex-grow: 1;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 1rem;
|
|
||||||
border: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: 0 0 3px var(--darken-weak);
|
|
||||||
margin: 1rem 0;
|
|
||||||
font-size: 1rem;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: 0 0 3px var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-button {
|
|
||||||
padding: 1rem;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
fill: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
fill: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-tiles {
|
.entity-tiles {
|
||||||
@@ -150,6 +122,14 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
display: block;
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: var(--shadow);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint2) {
|
@media(max-width: $breakpoint2) {
|
||||||
.entity-tiles {
|
.entity-tiles {
|
||||||
grid-gap: .5rem;
|
grid-gap: .5rem;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="media-container">
|
<div class="media-container">
|
||||||
<div
|
<div
|
||||||
class="media"
|
class="media"
|
||||||
:class="{ center: release.photos.length < 2 }"
|
:class="{ center: release.photos.length < 2, preview: !me }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="release.trailer || release.teaser"
|
v-if="release.trailer || release.teaser"
|
||||||
@@ -38,15 +38,30 @@
|
|||||||
class="item trailer"
|
class="item trailer"
|
||||||
>
|
>
|
||||||
|
|
||||||
<a
|
<span
|
||||||
v-if="release.poster"
|
v-if="release.poster || release.teaser"
|
||||||
v-tooltip="'View poster'"
|
class="poster-links"
|
||||||
:href="getPath(release.poster)"
|
>
|
||||||
:class="{ playing }"
|
<a
|
||||||
target="_blank"
|
v-if="release.teaser"
|
||||||
rel="noopener noreferrer"
|
v-tooltip="'View teaser'"
|
||||||
class="poster-link"
|
:href="getPath(release.teaser)"
|
||||||
><Icon icon="image" /></a>
|
:class="{ playing }"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="poster-link"
|
||||||
|
><Icon icon="film4" /></a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-if="release.poster"
|
||||||
|
v-tooltip="'View poster'"
|
||||||
|
:href="getPath(release.poster)"
|
||||||
|
:class="{ playing }"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="poster-link"
|
||||||
|
><Icon icon="image" /></a>
|
||||||
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="sfw && !playing"
|
v-if="sfw && !playing"
|
||||||
@@ -112,9 +127,9 @@
|
|||||||
v-if="!me"
|
v-if="!me"
|
||||||
class="item-container item-more"
|
class="item-container item-more"
|
||||||
><router-link
|
><router-link
|
||||||
:to="{ name: 'signup', query: { ref: $route.path } }"
|
:to="{ name: 'login', query: { ref: $route.path } }"
|
||||||
class="link"
|
class="link"
|
||||||
>Sign up</router-link> for more photos, trailers and features!</div>
|
>Log in</router-link> for more photos, trailers and features!</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -217,11 +232,16 @@ export default {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poster-link {
|
.poster-links {
|
||||||
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: .5rem;
|
top: 0;
|
||||||
right: .5rem;
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-link {
|
||||||
transition: opacity .1s ease;
|
transition: opacity .1s ease;
|
||||||
|
padding: .5rem;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
@@ -234,6 +254,14 @@ export default {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-left: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -337,7 +365,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint-micro) {
|
@media(max-width: $breakpoint-micro) {
|
||||||
.media.center {
|
.media.center.preview {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<Details :release="movie" />
|
<Details :release="movie" />
|
||||||
|
|
||||||
<div class="movie">
|
<div class="movie">
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="{ name: 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }"
|
:to="{ name: 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }"
|
||||||
class="cover"
|
class="cover"
|
||||||
>
|
>
|
||||||
@@ -13,15 +13,42 @@
|
|||||||
:style="{ 'background-image': getBgPath(movie.covers[0], 'lazy') }"
|
:style="{ 'background-image': getBgPath(movie.covers[0], 'lazy') }"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
>
|
>
|
||||||
</router-link>
|
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
:title="movie.title"
|
||||||
|
class="unavailable"
|
||||||
|
><Icon icon="blocked" /></div>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
v-show="(!stash || stash.primary) && favorited"
|
||||||
|
icon="heart7"
|
||||||
|
class="stash stashed"
|
||||||
|
@click.prevent.native="unstashMovie"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
v-show="(!stash || stash.primary) && favorited === false"
|
||||||
|
icon="heart8"
|
||||||
|
class="stash unstashed"
|
||||||
|
@click.prevent.native="stashMovie"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
v-show="stash && !stash.primary"
|
||||||
|
icon="cross2"
|
||||||
|
class="stash unstash"
|
||||||
|
@click.prevent.native="unstashMovie"
|
||||||
|
/>
|
||||||
|
</RouterLink>
|
||||||
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="{ name: 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }"
|
:to="{ name: 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }"
|
||||||
class="title-link"
|
class="title-link"
|
||||||
>
|
>
|
||||||
<h3 class="title">{{ movie.title }}</h3>
|
<h3 class="title">{{ movie.title }}</h3>
|
||||||
</router-link>
|
</RouterLink>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
class="actors nolist"
|
class="actors nolist"
|
||||||
@@ -31,10 +58,10 @@
|
|||||||
v-for="actor in movie.actors"
|
v-for="actor in movie.actors"
|
||||||
:key="`tag-${movie.id}-${actor.id}`"
|
:key="`tag-${movie.id}-${actor.id}`"
|
||||||
class="actor"
|
class="actor"
|
||||||
><router-link
|
><RouterLink
|
||||||
:to="`/actor/${actor.id}/${actor.slug}`"
|
:to="`/actor/${actor.id}/${actor.slug}`"
|
||||||
class="actor-link"
|
class="actor-link"
|
||||||
>{{ actor.name }}</router-link></li>
|
>{{ actor.name }}</RouterLink></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
@@ -45,10 +72,10 @@
|
|||||||
v-for="tag in movie.tags"
|
v-for="tag in movie.tags"
|
||||||
:key="`tag-${movie.id}-${tag.id}`"
|
:key="`tag-${movie.id}-${tag.id}`"
|
||||||
class="tag"
|
class="tag"
|
||||||
><router-link
|
><RouterLink
|
||||||
:to="`/tag/${tag.slug}`"
|
:to="`/tag/${tag.slug}`"
|
||||||
class="tag-link"
|
class="tag-link"
|
||||||
>{{ tag.name }}</router-link></li>
|
>{{ tag.name }}</RouterLink></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,6 +85,38 @@
|
|||||||
<script>
|
<script>
|
||||||
import Details from './tile-details.vue';
|
import Details from './tile-details.vue';
|
||||||
|
|
||||||
|
async function stashMovie() {
|
||||||
|
this.favorited = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('stashMovie', {
|
||||||
|
movieId: this.movie.id,
|
||||||
|
stashId: this.$store.getters.favorites.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$emit('stash', true);
|
||||||
|
} catch (error) {
|
||||||
|
this.favorited = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unstashMovie() {
|
||||||
|
if (!this.stash || this.stash.primary) {
|
||||||
|
this.favorited = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('unstashMovie', {
|
||||||
|
movieId: this.movie.id,
|
||||||
|
stashId: this.stash?.id || this.$store.getters.favorites.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$emit('stash', false);
|
||||||
|
} catch (error) {
|
||||||
|
this.favorited = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function sfw() {
|
function sfw() {
|
||||||
return this.$store.state.ui.sfw;
|
return this.$store.state.ui.sfw;
|
||||||
}
|
}
|
||||||
@@ -71,10 +130,23 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
stash: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
favorited: this.movie.isFavorited,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
sfw,
|
sfw,
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
stashMovie,
|
||||||
|
unstashMovie,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -87,10 +159,14 @@ export default {
|
|||||||
background: var(--background);
|
background: var(--background);
|
||||||
box-shadow: 0 0 3px var(--darken-weak);
|
box-shadow: 0 0 3px var(--darken-weak);
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
|
|
||||||
|
&:hover .unstashed,
|
||||||
|
&:hover .unstash {
|
||||||
|
fill: var(--lighten);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.movie {
|
.movie {
|
||||||
height: 16rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,17 +176,30 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cover {
|
.cover {
|
||||||
height: 100%;
|
height: 16rem;
|
||||||
|
width: 11.25rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
box-shadow: 0 0 3px var(--darken-weak);
|
box-shadow: 0 0 3px var(--darken-weak);
|
||||||
|
background-color: var(--shadow-hint);
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-width: 12rem;
|
width: 100%;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center;
|
object-position: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unavailable .icon {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
fill: var(--shadow-hint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
@@ -179,9 +268,31 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint) {
|
.stash {
|
||||||
.movie {
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
padding: .25rem .5rem .5rem .5rem;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
fill: var(--lighten-weak);
|
||||||
|
filter: drop-shadow(0 0 2px var(--darken));
|
||||||
|
|
||||||
|
&:hover.unstashed,
|
||||||
|
&.stashed {
|
||||||
|
fill: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover.unstash {
|
||||||
|
fill: var(--text-light);
|
||||||
|
filter: drop-shadow(0 0 2px var(--darken-weak));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-kilo) {
|
||||||
|
.cover {
|
||||||
height: 12rem;
|
height: 12rem;
|
||||||
|
width: 8.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ensure no half actor names show */
|
/* ensure no half actor names show */
|
||||||
|
|||||||
@@ -1,13 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="movies">
|
<div class="movies">
|
||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
<div class="tiles">
|
<SearchBar :placeholder="`Search ${totalCount} movies`" />
|
||||||
|
|
||||||
|
<TagFilter
|
||||||
|
class="filters-filter"
|
||||||
|
:filter="filter"
|
||||||
|
:available-tags="availableTags"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref="tiles"
|
||||||
|
class="tiles"
|
||||||
|
>
|
||||||
<MovieTile
|
<MovieTile
|
||||||
v-for="movie in movies"
|
v-for="movie in movies"
|
||||||
:key="`movie-${movie.id}`"
|
:key="`movie-${movie.id}`"
|
||||||
:movie="movie"
|
:movie="movie"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Pagination
|
||||||
|
v-if="totalCount > 0"
|
||||||
|
:items-total="totalCount"
|
||||||
|
:items-per-page="limit"
|
||||||
|
class="pagination-bottom"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
@@ -16,27 +34,69 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MovieTile from './movie-tile.vue';
|
import MovieTile from './movie-tile.vue';
|
||||||
|
import SearchBar from '../search/bar.vue';
|
||||||
|
import Pagination from '../pagination/pagination.vue';
|
||||||
|
import TagFilter from '../filters/tag-filter.vue';
|
||||||
|
|
||||||
|
async function fetchMovies() {
|
||||||
|
if (this.$route.query.query) {
|
||||||
|
await this.searchMovies();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
async function mounted() {
|
|
||||||
const { movies, totalCount } = await this.$store.dispatch('fetchMovies', {
|
const { movies, totalCount } = await this.$store.dispatch('fetchMovies', {
|
||||||
limit: 30,
|
limit: this.limit,
|
||||||
|
range: this.$route.params.range,
|
||||||
|
pageNumber: this.$route.params.pageNumber,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.movies = movies;
|
this.movies = movies;
|
||||||
this.totalCount = totalCount;
|
this.totalCount = totalCount;
|
||||||
|
|
||||||
|
this.$refs.tiles.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchMovies() {
|
||||||
|
const { movies, totalCount } = await this.$store.dispatch('searchMovies', {
|
||||||
|
query: this.$route.query.query,
|
||||||
|
limit: this.limit,
|
||||||
|
pageNumber: this.$route.params.pageNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.movies = movies;
|
||||||
|
this.totalCount = totalCount;
|
||||||
|
|
||||||
|
this.$refs.tiles.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mounted() {
|
||||||
|
this.pageTitle = 'Movies';
|
||||||
|
|
||||||
|
await this.fetchMovies();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
MovieTile,
|
MovieTile,
|
||||||
|
SearchBar,
|
||||||
|
Pagination,
|
||||||
|
TagFilter,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
movies: [],
|
movies: [],
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
|
limit: 20,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route: fetchMovies,
|
||||||
|
},
|
||||||
mounted,
|
mounted,
|
||||||
|
methods: {
|
||||||
|
fetchMovies,
|
||||||
|
searchMovies,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -49,14 +109,22 @@ export default {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-inner {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
margin: 1rem 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.tiles {
|
.tiles {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
|
||||||
grid-gap: 1rem;
|
grid-gap: 1rem;
|
||||||
padding: 1rem;
|
padding: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint) {
|
@media(max-width: $breakpoint-kilo) {
|
||||||
.tiles {
|
.tiles {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,10 @@
|
|||||||
<h2
|
<h2
|
||||||
v-if="release.title"
|
v-if="release.title"
|
||||||
class="title"
|
class="title"
|
||||||
>{{ release.title }}</h2>
|
>
|
||||||
|
{{ release.title }}
|
||||||
|
<template v-if="release.movies?.[0]?.title && /^scene \d+$/i.test(release.title)"><span class="title-composed"> from </span>{{ release.movies[0].title }}</template>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<h2
|
<h2
|
||||||
v-else-if="release.actors.length > 0"
|
v-else-if="release.actors.length > 0"
|
||||||
@@ -275,7 +278,7 @@ function bannerBackground() {
|
|||||||
function pageTitle() {
|
function pageTitle() {
|
||||||
return this.release
|
return this.release
|
||||||
&& (this.release.title
|
&& (this.release.title
|
||||||
|| (this.release.actors.length > 0 ? `${this.release.actors.map(actor => actor.name).join(', ')} for ${this.release.entity.name}` : null));
|
|| (this.release.actors.length > 0 ? `${this.release.actors.map((actor) => actor.name).join(', ')} for ${this.release.entity.name}` : null));
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAlbum() {
|
function showAlbum() {
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
class="heading"
|
class="heading"
|
||||||
><span class="range">{{ range }}</span> releases for '{{ context }}'</h3>
|
><span class="range">{{ range }}</span> releases for '{{ context }}'</h3>
|
||||||
|
|
||||||
|
<Ellipsis v-if="!done" />
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
v-if="releases.length > 0"
|
v-else-if="releases.length > 0"
|
||||||
:key="sfw"
|
:key="sfw"
|
||||||
class="nolist tiles"
|
class="nolist tiles"
|
||||||
>
|
>
|
||||||
@@ -18,13 +20,14 @@
|
|||||||
:release="release"
|
:release="release"
|
||||||
:referer="referer"
|
:referer="referer"
|
||||||
:index="index"
|
:index="index"
|
||||||
|
:stash="stash"
|
||||||
@stash="isStashed => $emit('stash', isStashed)"
|
@stash="isStashed => $emit('stash', isStashed)"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="releases.length === 0 && range !== 'all'"
|
v-else-if="releases.length === 0 && range !== 'all'"
|
||||||
class="empty"
|
class="empty"
|
||||||
>No {{ range }} releases</span>
|
>No {{ range }} releases</span>
|
||||||
|
|
||||||
@@ -36,6 +39,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Ellipsis from '../loading/ellipsis.vue';
|
||||||
import SceneTile from './scene-tile.vue';
|
import SceneTile from './scene-tile.vue';
|
||||||
|
|
||||||
function range() {
|
function range() {
|
||||||
@@ -48,6 +52,7 @@ function sfw() {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
Ellipsis,
|
||||||
SceneTile,
|
SceneTile,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -59,10 +64,18 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
done: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
referer: {
|
referer: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
stash: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['stash'],
|
emits: ['stash'],
|
||||||
computed: {
|
computed: {
|
||||||
@@ -113,10 +126,29 @@ export default {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-mega) {
|
||||||
|
.tiles {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint-kilo) {
|
@media(max-width: $breakpoint-kilo) {
|
||||||
.tiles {
|
.tiles {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(18.5rem, 1fr));
|
||||||
grid-gap: .5rem;
|
grid-gap: .5rem;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint) {
|
||||||
|
.tiles {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-micro) {
|
||||||
|
.tiles {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -46,18 +46,25 @@
|
|||||||
><Icon icon="blocked" />No thumbnail available</div>
|
><Icon icon="blocked" />No thumbnail available</div>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="favorited"
|
v-show="(!stash || stash.primary) && favorited"
|
||||||
icon="heart7"
|
icon="heart7"
|
||||||
class="stash stashed"
|
class="stash stashed"
|
||||||
@click.prevent.native="unstashScene"
|
@click.prevent.native="unstashScene"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="favorited === false"
|
v-show="(!stash || stash.primary) && favorited === false"
|
||||||
icon="heart8"
|
icon="heart8"
|
||||||
class="stash unstashed"
|
class="stash unstashed"
|
||||||
@click.prevent.native="stashScene"
|
@click.prevent.native="stashScene"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
v-show="stash && !stash.primary"
|
||||||
|
icon="cross2"
|
||||||
|
class="stash unstash"
|
||||||
|
@click.prevent.native="unstashScene"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -78,7 +85,9 @@
|
|||||||
v-tooltip.bottom="release.title"
|
v-tooltip.bottom="release.title"
|
||||||
:title="release.title"
|
:title="release.title"
|
||||||
class="title"
|
class="title"
|
||||||
>{{ release.title }}</h3>
|
>
|
||||||
|
{{ release.title }}<template v-if="release.movies?.[0]?.title && /^scene \d+$/i.test(release.title)"><span class="title-composed"> from </span>{{ release.movies[0].title }}</template>
|
||||||
|
</h3>
|
||||||
|
|
||||||
<h3
|
<h3
|
||||||
v-else-if="release.actors.length > 0"
|
v-else-if="release.actors.length > 0"
|
||||||
@@ -104,21 +113,22 @@
|
|||||||
:key="actor.id"
|
:key="actor.id"
|
||||||
class="actor"
|
class="actor"
|
||||||
>
|
>
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="{ name: 'actor', params: { actorId: actor.id, actorSlug: actor.slug } }"
|
:to="{ name: 'actor', params: { actorId: actor.id, actorSlug: actor.slug } }"
|
||||||
|
:class="{ [actor.gender]: !!actor.gender }"
|
||||||
class="actor-link"
|
class="actor-link"
|
||||||
>{{ actor.name }}</router-link>
|
>{{ actor.name }}</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="labels">
|
<div class="labels">
|
||||||
<router-link
|
<RouterLink
|
||||||
v-if="release.shootId && release.studio"
|
v-if="release.shootId && release.studio"
|
||||||
:to="{ name: 'studio', params: { entitySlug: release.studio.slug } }"
|
:to="`/studio/${release.studio.slug}`"
|
||||||
:title="release.studio && release.studio.name"
|
:title="release.studio && release.studio.name"
|
||||||
class="shoot nolink"
|
class="shoot nolink"
|
||||||
>{{ release.shootId }}</router-link>
|
>{{ release.shootId }}</RouterLink>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-else-if="release.shootId"
|
v-else-if="release.shootId"
|
||||||
@@ -136,10 +146,10 @@
|
|||||||
:key="`tag-${tag.slug}`"
|
:key="`tag-${tag.slug}`"
|
||||||
class="tag"
|
class="tag"
|
||||||
>
|
>
|
||||||
<router-link
|
<RouterLink
|
||||||
:to="`/tag/${tag.slug}`"
|
:to="`/tag/${tag.slug}`"
|
||||||
class="tag-link"
|
class="tag-link"
|
||||||
>{{ tag.name }}</router-link>
|
>{{ tag.name }}</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,12 +177,14 @@ async function stashScene() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function unstashScene() {
|
async function unstashScene() {
|
||||||
this.favorited = false;
|
if (!this.stash || this.stash.primary) {
|
||||||
|
this.favorited = false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('unstashScene', {
|
await this.$store.dispatch('unstashScene', {
|
||||||
sceneId: this.release.id,
|
sceneId: this.release.id,
|
||||||
stashId: this.$store.getters.favorites.id,
|
stashId: this.stash?.id || this.$store.getters.favorites.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('stash', false);
|
this.$emit('stash', false);
|
||||||
@@ -190,6 +202,10 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
stash: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['stash'],
|
emits: ['stash'],
|
||||||
data() {
|
data() {
|
||||||
@@ -218,23 +234,27 @@ export default {
|
|||||||
box-shadow: 0 0 3px var(--darken-weak);
|
box-shadow: 0 0 3px var(--darken-weak);
|
||||||
|
|
||||||
&.new .poster::after {
|
&.new .poster::after {
|
||||||
content: '★';
|
content: 'new';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
display: flex;
|
||||||
left: 0;
|
align-items: center;
|
||||||
width: 1rem;
|
justify-content: center;
|
||||||
height: 1rem;
|
bottom: 0;
|
||||||
|
right: .5rem;
|
||||||
|
width: 2rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: .25rem;
|
padding: .1rem 0 .05rem 0;
|
||||||
border-radius: 0 0 .5rem 0;
|
border-radius: .25rem .25rem 0 0;
|
||||||
color: var(--primary);
|
background: var(--info);
|
||||||
font-size: 1rem;
|
color: var(--text-light);
|
||||||
|
font-size: .7rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
text-shadow: 0 0 2px var(--darken-weak);
|
box-shadow: 0 0 3px var(--shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .unstashed {
|
&:hover .unstashed,
|
||||||
|
&:hover .unstash {
|
||||||
fill: var(--lighten);
|
fill: var(--lighten);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,8 +299,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stash {
|
.stash {
|
||||||
width: 1.25rem;
|
width: 1.5rem;
|
||||||
height: 1.25rem;
|
height: 1.5rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
@@ -293,20 +313,26 @@ export default {
|
|||||||
fill: var(--primary);
|
fill: var(--primary);
|
||||||
filter: drop-shadow(0 0 2px var(--darken-weak));
|
filter: drop-shadow(0 0 2px var(--darken-weak));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover.unstash {
|
||||||
|
fill: var(--text-light);
|
||||||
|
filter: drop-shadow(0 0 2px var(--darken-weak));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
|
max-width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0 .5rem;
|
padding: 0 .5rem;
|
||||||
margin: 0 0 .1rem 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -318,7 +344,7 @@ export default {
|
|||||||
.title {
|
.title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 1rem;
|
font-size: .9rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@@ -344,8 +370,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.actor-link {
|
.actor-link {
|
||||||
color: var(--link);
|
color: var(--text);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
font-size: .9rem;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
@@ -353,7 +380,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.labels {
|
.labels {
|
||||||
padding: 0 .5rem 1.5rem .5rem;
|
padding: 0 .5rem 1.25rem .5rem;
|
||||||
max-height: .5rem;
|
max-height: .5rem;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
@@ -384,11 +411,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tag-link {
|
.tag-link {
|
||||||
background: var(--background);
|
background: var(--shadow-touch);
|
||||||
color: var(--shadow);
|
color: var(--shadow);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: .25rem .5rem;
|
padding: .25rem .5rem;
|
||||||
border: solid 1px var(--shadow-hint);
|
margin: 0 1px 0 0;
|
||||||
font-size: .75rem;
|
font-size: .75rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -400,14 +427,20 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.details-wide {
|
.details-wide {
|
||||||
margin: 0 0 .5rem 0;
|
margin: 0 0 .4rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-compact {
|
.details-compact {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint-kilo) {
|
@media(max-width: $breakpoint) {
|
||||||
|
.thumbnail {
|
||||||
|
height: 11rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-micro) {
|
||||||
.tile-body {
|
.tile-body {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
@@ -453,14 +486,15 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tile.new .poster::after {
|
.tile.new .poster::after {
|
||||||
bottom: 0;
|
top: 0;
|
||||||
top: auto;
|
right: .25rem;
|
||||||
margin: .1rem .25rem;
|
bottom: auto;
|
||||||
|
border-radius: 0 0 .25rem .25rem;
|
||||||
|
padding: .05rem 0 .1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stash {
|
.stash {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: auto;
|
|
||||||
padding: .25rem .5rem .5rem .25rem;
|
padding: .25rem .5rem .5rem .25rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,10 +57,7 @@
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="date"
|
class="date"
|
||||||
><Icon
|
>{{ formatDate(release.date, 'MMMM D, YYYY', release.datePrecision) }}</a>
|
||||||
v-if="release.url"
|
|
||||||
icon="share2"
|
|
||||||
/>{{ formatDate(release.date, 'MMMM D, YYYY', release.datePrecision) }}</a>
|
|
||||||
|
|
||||||
<a
|
<a
|
||||||
v-else
|
v-else
|
||||||
@@ -91,27 +88,17 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
z-index: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background: var(--profile);
|
background: var(--info);
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
.favicon {
|
.favicon {
|
||||||
width: 2rem;
|
width: 2rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: .5rem;
|
padding: .4rem .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
&.new .date::before {
|
|
||||||
content: '';
|
|
||||||
width: .5rem;
|
|
||||||
height: .5rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin: 0 .25rem 0 0;
|
|
||||||
background: var(--primary);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.site,
|
.site,
|
||||||
@@ -120,7 +107,7 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
padding: .5rem;
|
padding: .4rem .5rem;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
|
|||||||
101
assets/components/search/bar.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<form
|
||||||
|
class="search"
|
||||||
|
@submit.prevent="() => search()"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="query"
|
||||||
|
:placeholder="placeholder || 'Search'"
|
||||||
|
class="query"
|
||||||
|
@input="() => search(true)"
|
||||||
|
>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="search-button"
|
||||||
|
><Icon icon="search" /></button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function search(typing) {
|
||||||
|
if (!typing || this.eager) {
|
||||||
|
this.$router.replace({
|
||||||
|
query: { query: this.query || undefined },
|
||||||
|
params: { ...this.$route.params, pageNumber: 1 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
this.query = this.$route.query.query || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
eager: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
query: this.$route.query.query || null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: resetQuery,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
search,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.search {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query {
|
||||||
|
max-width: 40rem;
|
||||||
|
min-width: 10rem;
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--background);
|
||||||
|
flex-grow: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1rem;
|
||||||
|
border: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0 0 3px var(--darken-weak);
|
||||||
|
font-size: 1rem;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 3px var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-button {
|
||||||
|
padding: 1rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
fill: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
|
|
||||||
<div class="sidebar-section controls noselect">
|
<div class="sidebar-section controls noselect">
|
||||||
<label
|
<label
|
||||||
v-if="auth && me"
|
v-if="login && me"
|
||||||
@click="$emit('toggleSidebar', false)"
|
@click="$emit('toggleSidebar', false)"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
|
|
||||||
<div class="toggles noselect">
|
<div class="toggles noselect">
|
||||||
<label
|
<label
|
||||||
v-if="auth && !me"
|
v-if="login && !me"
|
||||||
@click="$emit('toggleSidebar', false)"
|
@click="$emit('toggleSidebar', false)"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label
|
<label
|
||||||
v-if="auth && me"
|
v-if="login && me"
|
||||||
class="toggle"
|
class="toggle"
|
||||||
@click.stop="$store.dispatch('logout')"
|
@click.stop="$store.dispatch('logout')"
|
||||||
><Icon icon="exit2" />Log out</label>
|
><Icon icon="exit2" />Log out</label>
|
||||||
@@ -206,8 +206,12 @@ function theme(state) {
|
|||||||
return state.ui.theme;
|
return state.ui.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
function auth(state) {
|
function login(state) {
|
||||||
return state.auth.enabled;
|
return state.auth.login;
|
||||||
|
}
|
||||||
|
|
||||||
|
function signup(state) {
|
||||||
|
return state.auth.signup;
|
||||||
}
|
}
|
||||||
|
|
||||||
function me(state) {
|
function me(state) {
|
||||||
@@ -234,7 +238,8 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
auth,
|
login,
|
||||||
|
signup,
|
||||||
me,
|
me,
|
||||||
sfw,
|
sfw,
|
||||||
theme,
|
theme,
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
>{{ stash.name }}</h2>
|
>{{ stash.name }}</h2>
|
||||||
|
|
||||||
<span class="header-section">
|
<span class="header-section">
|
||||||
<router-link
|
<RouterLink
|
||||||
v-if="stash.user"
|
v-if="stash.user"
|
||||||
:to="{ name: 'user', params: { username: stash.user.username } }"
|
:to="{ name: 'user', params: { username: stash.user.username } }"
|
||||||
class="header-item stash-username nolink"
|
class="header-item stash-username nolink"
|
||||||
><Icon icon="user3" /><span class="username-name">{{ stash.user.username }}</span></router-link>
|
><Icon icon="user3" /><span class="username-name">{{ stash.user.username }}</span></RouterLink>
|
||||||
|
|
||||||
<label
|
<label
|
||||||
v-if="isMine"
|
v-if="isMine"
|
||||||
@@ -55,22 +55,50 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
|
<FilterBar :ranges="['scenes', 'actors', 'movies']" />
|
||||||
|
|
||||||
<Releases
|
<Releases
|
||||||
v-if="stash.scenes?.length > 0"
|
v-if="$route.params.range === 'scenes' && stash.scenes?.length > 0"
|
||||||
:releases="stash.scenes.map(item => item.scene)"
|
:releases="stash.scenes.map(item => item.scene)"
|
||||||
|
:stash="stash"
|
||||||
class="stash-section stash-scenes"
|
class="stash-section stash-scenes"
|
||||||
@stash="fetchStash"
|
@stash="fetchStash"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
v-if="stash.actors?.length > 0"
|
v-if="$route.params.range === 'actors'"
|
||||||
class="stash-section stash-actors nolist"
|
class="stash-section stash-actors nolist"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-for="item in stash.actors"
|
v-for="item in stash.actors"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
><Actor :actor="item.actor" /></li>
|
><Actor
|
||||||
|
:actor="item.actor"
|
||||||
|
:stash="stash"
|
||||||
|
@stash="fetchStash"
|
||||||
|
/></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="$route.params.range === 'movies'"
|
||||||
|
class="stash-movies"
|
||||||
|
>
|
||||||
|
<Movie
|
||||||
|
v-for="item in stash.movies"
|
||||||
|
:key="`movie-${item.id}`"
|
||||||
|
:movie="item.movie"
|
||||||
|
:stash="stash"
|
||||||
|
@stash="fetchStash"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Pagination
|
||||||
|
:items-total="totalCount"
|
||||||
|
:items-per-page="limit"
|
||||||
|
class="pagination-bottom"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -78,12 +106,35 @@
|
|||||||
<script>
|
<script>
|
||||||
import Actor from '../actors/tile.vue';
|
import Actor from '../actors/tile.vue';
|
||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
|
import Movie from '../releases/movie-tile.vue';
|
||||||
import RemoveStash from './remove.vue';
|
import RemoveStash from './remove.vue';
|
||||||
import Toggle from '../form/toggle.vue';
|
import Toggle from '../form/toggle.vue';
|
||||||
|
import FilterBar from '../filters/filter-bar.vue';
|
||||||
|
import Pagination from '../pagination/pagination.vue';
|
||||||
|
|
||||||
async function fetchStash() {
|
async function fetchStash() {
|
||||||
this.stash = await this.$store.dispatch('fetchStash', this.$route.params.stashId);
|
this.stash = await this.$store.dispatch('fetchStash', {
|
||||||
|
stashId: this.$route.params.stashId,
|
||||||
|
section: this.$route.params.range,
|
||||||
|
pageNumber: this.$route.params.pageNumber || 1,
|
||||||
|
limit: this.limit,
|
||||||
|
});
|
||||||
|
|
||||||
this.isMine = this.stash.user?.id === this.$store.state.auth.user?.id;
|
this.isMine = this.stash.user?.id === this.$store.state.auth.user?.id;
|
||||||
|
|
||||||
|
if (this.$route.params.range === 'scenes') {
|
||||||
|
this.totalCount = this.stash.sceneTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$route.params.range === 'actors') {
|
||||||
|
this.totalCount = this.stash.actorTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$route.params.range === 'movies') {
|
||||||
|
this.totalCount = this.stash.movieTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pageTitle = this.stash.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function publishStash(isPublic) {
|
async function publishStash(isPublic) {
|
||||||
@@ -109,23 +160,32 @@ async function removeStash(removed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function mounted() {
|
async function mounted() {
|
||||||
this.fetchStash();
|
await this.fetchStash();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Actor,
|
Actor,
|
||||||
|
Movie,
|
||||||
Releases,
|
Releases,
|
||||||
RemoveStash,
|
RemoveStash,
|
||||||
|
Pagination,
|
||||||
|
FilterBar,
|
||||||
Toggle,
|
Toggle,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
stash: null,
|
stash: null,
|
||||||
|
limit: Number(this.$route.query.limit) || 20,
|
||||||
|
pageTitle: null,
|
||||||
showRemoveStash: false,
|
showRemoveStash: false,
|
||||||
isMine: false,
|
isMine: false,
|
||||||
|
totalCount: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route: fetchStash,
|
||||||
|
},
|
||||||
mounted,
|
mounted,
|
||||||
methods: {
|
methods: {
|
||||||
fetchStash,
|
fetchStash,
|
||||||
@@ -199,24 +259,29 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stash-section:not(:last-child) {
|
|
||||||
border-bottom: solid 1px var(--shadow-hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stash-actors {
|
.stash-actors {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: .5rem;
|
grid-gap: .5rem;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||||
|
grid-template-rows: min-content;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
border-top: solid 1px var(--shadow-hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stash-scenes {
|
.stash-movies {
|
||||||
flex-grow: 0;
|
display: grid;
|
||||||
|
flex-grow: 1;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
|
||||||
|
grid-template-rows: min-content;
|
||||||
|
grid-gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border-top: solid 1px var(--shadow-hint);
|
||||||
|
}
|
||||||
|
|
||||||
.tiles {
|
.stash-scenes .tiles {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
|
||||||
}
|
grid-template-rows: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint-small) {
|
@media(max-width: $breakpoint-small) {
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="photos">
|
<div class="photos">
|
||||||
|
<Campaign
|
||||||
|
:tag="tag"
|
||||||
|
:min-height="240"
|
||||||
|
:max-ratio="1.5"
|
||||||
|
class="photo-link photo"
|
||||||
|
@campaign="campaign => $emit('campaign', campaign)"
|
||||||
|
/>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
v-for="photo in photos"
|
v-for="photo in photos"
|
||||||
:key="`photo-${photo.id}`"
|
:key="`photo-${photo.id}`"
|
||||||
@@ -37,6 +45,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Logo from '../album/logo.vue';
|
import Logo from '../album/logo.vue';
|
||||||
|
import Campaign from '../campaigns/campaign.vue';
|
||||||
|
|
||||||
function photos() {
|
function photos() {
|
||||||
if (this.tag.poster && this.$store.state.ui.sfw) {
|
if (this.tag.poster && this.$store.state.ui.sfw) {
|
||||||
@@ -57,6 +66,7 @@ function photos() {
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Logo,
|
Logo,
|
||||||
|
Campaign,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
tag: {
|
tag: {
|
||||||
@@ -64,7 +74,7 @@ export default {
|
|||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emits: ['load'],
|
emits: ['load', 'campaign'],
|
||||||
computed: {
|
computed: {
|
||||||
photos,
|
photos,
|
||||||
},
|
},
|
||||||
@@ -150,4 +160,9 @@ export default {
|
|||||||
transform: translateY(100%);
|
transform: translateY(100%);
|
||||||
transition: transform .25s ease;
|
transition: transform .25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep(.campaign) .campaign-banner {
|
||||||
|
max-height: 15rem;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
:tag="tag"
|
:tag="tag"
|
||||||
:class="{ expanded }"
|
:class="{ expanded }"
|
||||||
@load="scroll.loaded"
|
@load="scroll.loaded"
|
||||||
|
@campaign="campaign => showBannerCampaign = !campaign"
|
||||||
/>
|
/>
|
||||||
</Scroll>
|
</Scroll>
|
||||||
|
|
||||||
@@ -43,16 +44,30 @@
|
|||||||
:items="[tag.poster, ...tag.photos]"
|
:items="[tag.poster, ...tag.photos]"
|
||||||
:title="tag.name"
|
:title="tag.name"
|
||||||
:local="true"
|
:local="true"
|
||||||
|
:tag="tag"
|
||||||
class="portrait"
|
class="portrait"
|
||||||
@close="$router.replace({ hash: undefined })"
|
@close="$router.replace({ hash: undefined })"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="showBannerCampaign"
|
||||||
|
class="campaign-container"
|
||||||
|
>
|
||||||
|
<Campaign
|
||||||
|
:tag="tag"
|
||||||
|
:min-ratio="3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<FilterBar
|
<FilterBar
|
||||||
ref="filter"
|
ref="filter"
|
||||||
:fetch-releases="fetchReleases"
|
:fetch-releases="fetchReleases"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Releases :releases="releases" />
|
<Releases
|
||||||
|
:releases="releases"
|
||||||
|
:done="done"
|
||||||
|
/>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
:items-total="totalCount"
|
:items-total="totalCount"
|
||||||
@@ -76,10 +91,13 @@ import Album from '../album/album.vue';
|
|||||||
import Releases from '../releases/releases.vue';
|
import Releases from '../releases/releases.vue';
|
||||||
import Pagination from '../pagination/pagination.vue';
|
import Pagination from '../pagination/pagination.vue';
|
||||||
import Scroll from '../scroll/scroll.vue';
|
import Scroll from '../scroll/scroll.vue';
|
||||||
|
import Campaign from '../campaigns/campaign.vue';
|
||||||
|
|
||||||
const converter = new Converter();
|
const converter = new Converter();
|
||||||
|
|
||||||
async function fetchReleases(scroll = true) {
|
async function fetchReleases(scroll = true) {
|
||||||
|
this.done = false;
|
||||||
|
|
||||||
const { tag, releases, totalCount } = await this.$store.dispatch('fetchTagBySlug', {
|
const { tag, releases, totalCount } = await this.$store.dispatch('fetchTagBySlug', {
|
||||||
tagSlug: this.$route.params.tagSlug,
|
tagSlug: this.$route.params.tagSlug,
|
||||||
pageNumber: Number(this.$route.params.pageNumber),
|
pageNumber: Number(this.$route.params.pageNumber),
|
||||||
@@ -94,6 +112,12 @@ async function fetchReleases(scroll = true) {
|
|||||||
this.hasMedia = this.tag.poster || this.tag.photos.length > 0;
|
this.hasMedia = this.tag.poster || this.tag.photos.length > 0;
|
||||||
this.description = this.tag.description && converter.makeHtml(escapeHtml(this.tag.description));
|
this.description = this.tag.description && converter.makeHtml(escapeHtml(this.tag.description));
|
||||||
|
|
||||||
|
this.done = true;
|
||||||
|
|
||||||
|
if (this.hasMedia) {
|
||||||
|
this.showBannerCampaign = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (scroll && this.$refs.filter) {
|
if (scroll && this.$refs.filter) {
|
||||||
this.$refs.filter.$el.scrollIntoView();
|
this.$refs.filter.$el.scrollIntoView();
|
||||||
}
|
}
|
||||||
@@ -122,17 +146,20 @@ export default {
|
|||||||
Photos,
|
Photos,
|
||||||
Pagination,
|
Pagination,
|
||||||
Scroll,
|
Scroll,
|
||||||
|
Campaign,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tag: null,
|
tag: null,
|
||||||
description: null,
|
description: null,
|
||||||
releases: null,
|
releases: null,
|
||||||
|
done: false,
|
||||||
totalCount: 0,
|
totalCount: 0,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
pageTitle: null,
|
pageTitle: null,
|
||||||
hasMedia: false,
|
hasMedia: false,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
|
showBannerCampaign: false, // only show if photo campaign is not available
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -172,12 +199,14 @@ export default {
|
|||||||
@import 'theme';
|
@import 'theme';
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
display: flex;
|
||||||
background: var(--profile);
|
background: var(--profile);
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
display: inline-block;
|
||||||
padding: .5rem 1rem;
|
padding: .5rem 1rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -200,4 +229,11 @@ export default {
|
|||||||
.scroll {
|
.scroll {
|
||||||
background: var(--background-dim);
|
background: var(--background-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.campaign-container {
|
||||||
|
max-height: 90px;
|
||||||
|
padding: .5rem 1rem 0 1rem;
|
||||||
|
background: var(--background-dim);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<div
|
<div class="content-inner">
|
||||||
v-for="(tags, category) in categories"
|
<SearchBar
|
||||||
:key="category"
|
:placeholder="'Search tags'"
|
||||||
class="category"
|
class="search"
|
||||||
>
|
/>
|
||||||
<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,121 +33,135 @@
|
|||||||
|
|
||||||
<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',
|
||||||
|
'gay',
|
||||||
|
'transsexual',
|
||||||
|
'dp',
|
||||||
|
'gangbang',
|
||||||
|
'facial',
|
||||||
|
'creampie',
|
||||||
|
'squirting',
|
||||||
|
],
|
||||||
|
appearance: [
|
||||||
|
'asian',
|
||||||
|
'black',
|
||||||
|
'latina',
|
||||||
|
'white',
|
||||||
|
'natural-boobs',
|
||||||
|
'enhanced-boobs',
|
||||||
|
'blonde',
|
||||||
|
'brunette',
|
||||||
|
'redhead',
|
||||||
|
'tattoos',
|
||||||
|
'piercings',
|
||||||
|
],
|
||||||
|
sexuality: [
|
||||||
|
'gay',
|
||||||
|
'bisexual',
|
||||||
|
'transsexual',
|
||||||
|
],
|
||||||
|
oral: [
|
||||||
|
'blowjob',
|
||||||
|
'pussy-eating',
|
||||||
|
'ass-eating',
|
||||||
|
'deepthroat',
|
||||||
|
'facefucking',
|
||||||
|
'69',
|
||||||
|
'atm',
|
||||||
|
],
|
||||||
|
manual: [
|
||||||
|
'handjob',
|
||||||
|
'fingering',
|
||||||
|
'anal-fingering',
|
||||||
|
'titty-fucking',
|
||||||
|
'fisting',
|
||||||
|
'anal-fisting',
|
||||||
|
'fisting-dp',
|
||||||
|
],
|
||||||
|
group: [
|
||||||
|
'mfm',
|
||||||
|
'mff',
|
||||||
|
'orgy',
|
||||||
|
'gangbang',
|
||||||
|
'blowbang',
|
||||||
|
],
|
||||||
|
cumshot: [
|
||||||
|
'facial',
|
||||||
|
'creampie',
|
||||||
|
'cum-in-mouth',
|
||||||
|
'cum-on-boobs',
|
||||||
|
'cum-on-butt',
|
||||||
|
'cum-on-pussy',
|
||||||
|
'anal-creampie',
|
||||||
|
'oral-creampie',
|
||||||
|
'bukkake',
|
||||||
|
'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',
|
||||||
|
'free-use',
|
||||||
|
'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',
|
|
||||||
'creampie',
|
|
||||||
'cum-in-mouth',
|
|
||||||
'cum-on-boobs',
|
|
||||||
'cum-on-butt',
|
|
||||||
'cum-on-pussy',
|
|
||||||
'anal-creampie',
|
|
||||||
'oral-creampie',
|
|
||||||
'bukkake',
|
|
||||||
'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',
|
|
||||||
],
|
|
||||||
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(),
|
||||||
@@ -148,17 +169,42 @@ async function mounted() {
|
|||||||
|
|
||||||
const tagsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag }), {});
|
const tagsBySlug = tags.reduce((acc, tag) => ({ ...acc, [tag.slug]: tag }), {});
|
||||||
|
|
||||||
this.categories = Object.entries(tagSlugsByCategory).reduce((acc, [category, tagSlugs]) => ({
|
this.categories = Object.entries(tagSlugsByCategory).reduce((acc, [category, tagSlugs]) => {
|
||||||
...acc,
|
const categoryTags = tagSlugs.map((tagSlug) => tagsBySlug[tagSlug]).filter(Boolean);
|
||||||
[category]: tagSlugs.map(tagSlug => tagsBySlug[tagSlug]),
|
|
||||||
}), {});
|
|
||||||
|
|
||||||
|
if (categoryTags.length === 0) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[category]: categoryTags,
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
@@ -169,14 +215,28 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
sfw,
|
sfw,
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
$route: fetchTags,
|
||||||
|
'$store.state.ui.tagFilter': fetchTags,
|
||||||
|
},
|
||||||
mounted,
|
mounted,
|
||||||
|
methods: {
|
||||||
|
fetchTags,
|
||||||
|
searchTags,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import 'theme';
|
@import 'breakpoints';
|
||||||
|
|
||||||
.tags {
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-inner {
|
||||||
padding: 1rem 1rem 0 1rem;
|
padding: 1rem 1rem 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,35 +244,97 @@ export default {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(20rem, .33fr));
|
grid-template-columns: repeat(auto-fill, minmax(20rem, .33fr));
|
||||||
grid-gap: 1rem;
|
grid-gap: 1rem;
|
||||||
margin: 0 0 1.5rem 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.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.25rem 0 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category:first-child .heading {
|
.category:first-child .heading {
|
||||||
margin: .5rem 0 1rem 0;
|
margin: .5rem 0 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint3) {
|
.search {
|
||||||
|
margin: 0 0 .25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-mega) {
|
||||||
.tiles {
|
.tiles {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(18rem, .5fr));
|
grid-template-columns: repeat(auto-fill, minmax(17.75rem, .5fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 12rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-kilo) {
|
||||||
|
.tiles {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
|
||||||
|
grid-gap: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 14rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint) {
|
@media(max-width: $breakpoint) {
|
||||||
.tiles {
|
.tiles {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(14.5rem, 1fr));
|
||||||
grid-gap: .5rem;
|
grid-gap: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 11rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: $breakpoint0) {
|
@media(max-width: $breakpoint-small) {
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-micro) {
|
||||||
.tiles {
|
.tiles {
|
||||||
grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 7.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-mini) {
|
||||||
|
.tiles {
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 12rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-nano) {
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $breakpoint-pico) {
|
||||||
|
::v-deep(.poster),
|
||||||
|
::v-deep(.blank) {
|
||||||
|
height: 8rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -16,16 +16,19 @@
|
|||||||
:style="{ transform: `translate3d(${tooltipX}px, ${tooltipY}px, 0)` }"
|
:style="{ transform: `translate3d(${tooltipX}px, ${tooltipY}px, 0)` }"
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
<div class="tooltip-inner">
|
<div
|
||||||
|
class="tooltip-inner"
|
||||||
|
:style="{ 'max-height': `calc(100vh - ${tooltipY}px - 1rem)` }"
|
||||||
|
>
|
||||||
<div class="tooltip">
|
<div class="tooltip">
|
||||||
<slot name="tooltip" />
|
<slot name="tooltip" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
|
||||||
class="tooltip-arrow"
|
|
||||||
:style="{ transform: `translate3d(${arrowOffset}px, 0, 0)` }"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="tooltip-arrow"
|
||||||
|
:style="{ transform: `translate3d(${arrowOffset}px, 0, 0)` }"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</teleport>
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
@@ -155,6 +158,8 @@ export default {
|
|||||||
|
|
||||||
.tooltip-inner {
|
.tooltip-inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: var(--background);
|
||||||
box-shadow: 0 0 .5rem var(--darken);
|
box-shadow: 0 0 .5rem var(--darken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="stash">
|
<div class="stash">
|
||||||
<div class="stash-section stash-header">
|
<div class="stash-section stash-header">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'stash', params: { stashId: stash.id, stashSlug: stash.slug } }"
|
:to="{ name: 'stash', params: { stashId: stash.id, stashSlug: stash.slug, range: 'scenes', pageNumber: 1 } }"
|
||||||
class="stash-link nolink"
|
class="stash-link nolink"
|
||||||
>
|
>
|
||||||
<h4 class="stash-name">{{ stash.name }}</h4>
|
<h4 class="stash-name">{{ stash.name }}</h4>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
$breakpoint-pico: 270px;
|
$breakpoint-pico: 270px;
|
||||||
$breakpoint-nano: 320px;
|
$breakpoint-nano: 320px;
|
||||||
|
$breakpoint-mini: 400px;
|
||||||
$breakpoint-micro: 540px;
|
$breakpoint-micro: 540px;
|
||||||
$breakpoint-small: 620px;
|
$breakpoint-small: 620px;
|
||||||
$breakpoint: 720px;
|
$breakpoint: 720px;
|
||||||
|
|||||||
@@ -44,6 +44,11 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: var(--primary-strong);
|
background: var(--primary-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: var(--shadow-weak);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-secondary {
|
.button-secondary {
|
||||||
|
|||||||
@@ -5,14 +5,21 @@ $breakpoint3: 1200px;
|
|||||||
$breakpoint4: 1500px;
|
$breakpoint4: 1500px;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
--primary: #f65596;
|
||||||
|
--primary-strong: #f90071;
|
||||||
|
--primary-faded: #ffcce4;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
--primary: #f04288;
|
||||||
|
|
||||||
--primary: #ff6c88;
|
--primary: #ff6c88;
|
||||||
--primary-strong: #ff4166;
|
--primary-strong: #ff4166;
|
||||||
--primary-faded: #ffdfee;
|
--primary-faded: #ffdfee;
|
||||||
*/
|
|
||||||
--primary: #f28;
|
--primary: #f28;
|
||||||
--primary-strong: #f90071;
|
--primary-strong: #f90071;
|
||||||
--primary-faded: #ffcce4;
|
--primary-faded: #ffcce4;
|
||||||
|
*/
|
||||||
|
|
||||||
--text-dark: #222;
|
--text-dark: #222;
|
||||||
--text-light: #fff;
|
--text-light: #fff;
|
||||||
@@ -21,16 +28,17 @@ $breakpoint4: 1500px;
|
|||||||
--background-dark: #222;
|
--background-dark: #222;
|
||||||
|
|
||||||
--darken: rgba(0, 0, 0, .5);
|
--darken: rgba(0, 0, 0, .5);
|
||||||
--darken-strong: rgba(0, 0, 0, .7);
|
|
||||||
--darken-extreme: rgba(0, 0, 0, .9);
|
|
||||||
--darken-censor: rgba(0, 0, 0, .95);
|
--darken-censor: rgba(0, 0, 0, .95);
|
||||||
|
--darken-extreme: rgba(0, 0, 0, .9);
|
||||||
|
--darken-strong: rgba(0, 0, 0, .7);
|
||||||
--darken-weak: rgba(0, 0, 0, .2);
|
--darken-weak: rgba(0, 0, 0, .2);
|
||||||
--darken-hint: rgba(0, 0, 0, .1);
|
--darken-hint: rgba(0, 0, 0, .1);
|
||||||
--darken-touch: rgba(0, 0, 0, .05);
|
--darken-touch: rgba(0, 0, 0, .05);
|
||||||
|
|
||||||
--lighten: rgba(255, 255, 255, .5);
|
--lighten-censor: rgba(255, 255, 255, .95);
|
||||||
--lighten-strong: rgba(255, 255, 255, .7);
|
|
||||||
--lighten-extreme: rgba(255, 255, 255, .9);
|
--lighten-extreme: rgba(255, 255, 255, .9);
|
||||||
|
--lighten-strong: rgba(255, 255, 255, .7);
|
||||||
|
--lighten: rgba(255, 255, 255, .5);
|
||||||
--lighten-weak: rgba(255, 255, 255, .2);
|
--lighten-weak: rgba(255, 255, 255, .2);
|
||||||
--lighten-hint: rgba(255, 255, 255, .05);
|
--lighten-hint: rgba(255, 255, 255, .05);
|
||||||
--lighten-touch: rgba(255, 255, 255, .03);
|
--lighten-touch: rgba(255, 255, 255, .03);
|
||||||
@@ -38,6 +46,8 @@ $breakpoint4: 1500px;
|
|||||||
--logo-shadow: drop-shadow(1px 0 0 $shadow-weak) drop-shadow(-1px 0 0 $shadow-weak) drop-shadow(0 1px 0 $shadow-weak) drop-shadow(0 -1px 0 $shadow-weak);
|
--logo-shadow: drop-shadow(1px 0 0 $shadow-weak) drop-shadow(-1px 0 0 $shadow-weak) drop-shadow(0 1px 0 $shadow-weak) drop-shadow(0 -1px 0 $shadow-weak);
|
||||||
--logo-highlight: drop-shadow(0 0 1px $highlight);
|
--logo-highlight: drop-shadow(0 0 1px $highlight);
|
||||||
|
|
||||||
|
--info: #321b24;
|
||||||
|
|
||||||
--male: #0af;
|
--male: #0af;
|
||||||
--female: #f0a;
|
--female: #f0a;
|
||||||
|
|
||||||
@@ -58,6 +68,7 @@ $breakpoint4: 1500px;
|
|||||||
--text-contrast: #fff;
|
--text-contrast: #fff;
|
||||||
|
|
||||||
--background: var(--background-light);
|
--background: var(--background-light);
|
||||||
|
--background-censor: rgba(255, 255, 255, .95);
|
||||||
--background-dim: #f5f5f5;
|
--background-dim: #f5f5f5;
|
||||||
--background-soft: #fdfdfd;
|
--background-soft: #fdfdfd;
|
||||||
|
|
||||||
@@ -71,6 +82,7 @@ $breakpoint4: 1500px;
|
|||||||
--crease: #eaeaea;
|
--crease: #eaeaea;
|
||||||
|
|
||||||
--shadow: rgba(0, 0, 0, .5);
|
--shadow: rgba(0, 0, 0, .5);
|
||||||
|
--shadow-censor: rgba(0, 0, 0, .95);
|
||||||
--shadow-extreme: rgba(0, 0, 0, .9);
|
--shadow-extreme: rgba(0, 0, 0, .9);
|
||||||
--shadow-strong: rgba(0, 0, 0, .7);
|
--shadow-strong: rgba(0, 0, 0, .7);
|
||||||
--shadow-modest: rgba(0, 0, 0, .3);
|
--shadow-modest: rgba(0, 0, 0, .3);
|
||||||
@@ -91,6 +103,7 @@ $breakpoint4: 1500px;
|
|||||||
--text-contrast: #222;
|
--text-contrast: #222;
|
||||||
|
|
||||||
--background: #181818;
|
--background: #181818;
|
||||||
|
--background-censor: rgba(0, 0, 0, .95);
|
||||||
--background-dim: #111;
|
--background-dim: #111;
|
||||||
--background-soft: #000;
|
--background-soft: #000;
|
||||||
|
|
||||||
|
|||||||
5
assets/img/icons/film4.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<title>film4</title>
|
||||||
|
<path d="M0 2v12h16v-12h-16zM3 13h-2v-2h2v2zM3 9h-2v-2h2v2zM3 5h-2v-2h2v2zM12 13h-8v-10h8v10zM15 13h-2v-2h2v2zM15 9h-2v-2h2v2zM15 5h-2v-2h2v2zM6 5v6l4-3z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 322 B |
5
assets/img/icons/presentation.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<title>presentation</title>
|
||||||
|
<path d="M14.25 0h-13.5c-0.412 0-0.75 0.338-0.75 0.75v11.5c0 0.412 0.338 0.75 0.75 0.75h4.455l-2.040 2.040c-0.22 0.22-0.22 0.576 0 0.795 0.11 0.11 0.254 0.165 0.398 0.165s0.288-0.055 0.398-0.165l2.835-2.835h0.205v2.5c0 0.276 0.224 0.5 0.5 0.5s0.5-0.224 0.5-0.5v-2.5h0.205l2.835 2.835c0.11 0.11 0.254 0.165 0.398 0.165s0.288-0.055 0.398-0.165c0.22-0.22 0.22-0.576 0-0.795l-2.040-2.040h4.455c0.412 0 0.75-0.338 0.75-0.75v-11.5c0-0.412-0.338-0.75-0.75-0.75zM6 3l4 2.5-4 2.5v-5zM12.5 11h-10c-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5h10c0.276 0 0.5 0.224 0.5 0.5s-0.224 0.5-0.5 0.5z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 756 B |
@@ -281,7 +281,7 @@ function initActorActions(store, router) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
actor: curateActor(actor, null, curateRelease),
|
actor: curateActor(actor, null, curateRelease),
|
||||||
releases: actor.scenesConnection.releases.map(release => curateRelease(release)),
|
releases: actor.scenesConnection.releases.map((release) => curateRelease(release)),
|
||||||
totalCount: actor.scenesConnection.totalCount,
|
totalCount: actor.scenesConnection.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -289,9 +289,10 @@ function initActorActions(store, router) {
|
|||||||
async function fetchActors({ _commit }, {
|
async function fetchActors({ _commit }, {
|
||||||
limit = 10,
|
limit = 10,
|
||||||
pageNumber = 1,
|
pageNumber = 1,
|
||||||
letter,
|
query,
|
||||||
gender,
|
gender,
|
||||||
age,
|
age,
|
||||||
|
country,
|
||||||
dob,
|
dob,
|
||||||
naturalBoobs,
|
naturalBoobs,
|
||||||
boobSize,
|
boobSize,
|
||||||
@@ -302,7 +303,8 @@ function initActorActions(store, router) {
|
|||||||
|
|
||||||
const genderFilter = (gender === null && 'gender: { isNull: true }')
|
const genderFilter = (gender === null && 'gender: { isNull: true }')
|
||||||
|| (gender === 'all' && ' ')
|
|| (gender === 'all' && ' ')
|
||||||
|| `gender: { equalTo: "${gender}" }`;
|
|| (gender && `gender: { equalTo: "${gender}" }`)
|
||||||
|
|| '';
|
||||||
|
|
||||||
const ageFilter = age ? `
|
const ageFilter = age ? `
|
||||||
or: [
|
or: [
|
||||||
@@ -325,37 +327,38 @@ function initActorActions(store, router) {
|
|||||||
` : '';
|
` : '';
|
||||||
|
|
||||||
const dobFilter = dob ? `dateOfBirth: { equalTo: "${dob}" }` : '';
|
const dobFilter = dob ? `dateOfBirth: { equalTo: "${dob}" }` : '';
|
||||||
|
const countryFilter = country ? `countryByBirthCountryAlpha2: { alpha2: { equalTo: "${country}" } }` : '';
|
||||||
|
|
||||||
const heightFilter = height ? `height: { greaterThanOrEqualTo: ${height[0]}, lessThanOrEqualTo: ${height[1]} }` : '';
|
const heightFilter = height ? `height: { greaterThanOrEqualTo: ${height[0]}, lessThanOrEqualTo: ${height[1]} }` : '';
|
||||||
const weightFilter = weight ? `weight: { greaterThanOrEqualTo: ${weight[0]}, lessThanOrEqualTo: ${weight[1]} }` : '';
|
const weightFilter = weight ? `weight: { greaterThanOrEqualTo: ${weight[0]}, lessThanOrEqualTo: ${weight[1]} }` : '';
|
||||||
const cupFilter = boobSize ? `cup: { greaterThanOrEqualTo: "${boobSize[0]}", lessThanOrEqualTo: "${boobSize[1]}" }` : '';
|
const cupFilter = boobSize ? `cup: { greaterThanOrEqualTo: "${boobSize[0]}", lessThanOrEqualTo: "${boobSize[1]}" }` : '';
|
||||||
|
|
||||||
const { connection: { actors, totalCount } } = await graphql(`
|
const { connection: { actors, totalCount }, countries } = await graphql(`
|
||||||
query Actors(
|
query Actors(
|
||||||
|
$query: String
|
||||||
$limit: Int,
|
$limit: Int,
|
||||||
$offset: Int = 0,
|
$offset: Int = 0,
|
||||||
$letter: String! = "",
|
|
||||||
$naturalBoobs: Boolean,
|
$naturalBoobs: Boolean,
|
||||||
$userId: Int,
|
$userId: Int,
|
||||||
$hasAuth: Boolean!,
|
$hasAuth: Boolean!,
|
||||||
) {
|
) {
|
||||||
connection: actorsConnection(
|
connection: searchActorsConnection(
|
||||||
|
query: $query
|
||||||
first: $limit,
|
first: $limit,
|
||||||
offset: $offset
|
offset: $offset
|
||||||
orderBy: NAME_ASC
|
orderBy: NAME_ASC
|
||||||
|
minLength: 0
|
||||||
filter: {
|
filter: {
|
||||||
aliasFor: {
|
aliasFor: {
|
||||||
isNull: true
|
isNull: true
|
||||||
}
|
}
|
||||||
name: {
|
|
||||||
startsWith: $letter
|
|
||||||
}
|
|
||||||
${genderFilter}
|
${genderFilter}
|
||||||
${ageFilter}
|
${ageFilter}
|
||||||
${dobFilter}
|
${dobFilter}
|
||||||
${heightFilter}
|
${heightFilter}
|
||||||
${weightFilter}
|
${weightFilter}
|
||||||
${cupFilter}
|
${cupFilter}
|
||||||
|
${countryFilter}
|
||||||
naturalBoobs: {
|
naturalBoobs: {
|
||||||
equalTo: $naturalBoobs
|
equalTo: $naturalBoobs
|
||||||
}
|
}
|
||||||
@@ -408,110 +411,30 @@ function initActorActions(store, router) {
|
|||||||
${actorStashesFields}
|
${actorStashesFields}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
countries(
|
||||||
|
orderBy: NAME_ASC,
|
||||||
|
) {
|
||||||
|
alpha2
|
||||||
|
name
|
||||||
|
alias
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
|
query,
|
||||||
offset: Math.max(0, (pageNumber - 1)) * limit,
|
offset: Math.max(0, (pageNumber - 1)) * limit,
|
||||||
limit,
|
limit,
|
||||||
letter,
|
naturalBoobs: naturalBoobs || null,
|
||||||
naturalBoobs,
|
|
||||||
hasAuth: !!store.state.auth.user,
|
hasAuth: !!store.state.auth.user,
|
||||||
userId: store.state.auth.user?.id,
|
userId: store.state.auth.user?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
actors: actors.map(actor => curateActor(actor)),
|
actors: actors.map((actor) => curateActor(actor)),
|
||||||
totalCount,
|
totalCount,
|
||||||
|
countries,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchActors({ _commit }, { query, limit = 20, minLength = 2 }) {
|
|
||||||
const { actors } = await graphql(`
|
|
||||||
query SearchActors(
|
|
||||||
$query: String!
|
|
||||||
$limit: Int = 20
|
|
||||||
$minLength: Int = 2
|
|
||||||
$hasAuth: Boolean!
|
|
||||||
$userId: Int
|
|
||||||
) {
|
|
||||||
actors: searchActors(
|
|
||||||
search: $query,
|
|
||||||
minLength: $minLength
|
|
||||||
first: $limit
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
age
|
|
||||||
ageAtDeath
|
|
||||||
dateOfBirth
|
|
||||||
dateOfDeath
|
|
||||||
gender
|
|
||||||
aliasFor: actorByAliasFor {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
age
|
|
||||||
ageAtDeath
|
|
||||||
dateOfBirth
|
|
||||||
dateOfDeath
|
|
||||||
gender
|
|
||||||
entity {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
avatar: avatarMedia {
|
|
||||||
id
|
|
||||||
path
|
|
||||||
thumbnail
|
|
||||||
lazy
|
|
||||||
isS3
|
|
||||||
width
|
|
||||||
height
|
|
||||||
comment
|
|
||||||
credit
|
|
||||||
}
|
|
||||||
birthCountry: countryByBirthCountryAlpha2 {
|
|
||||||
alpha2
|
|
||||||
name
|
|
||||||
alias
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entity {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
avatar: avatarMedia {
|
|
||||||
id
|
|
||||||
path
|
|
||||||
thumbnail
|
|
||||||
lazy
|
|
||||||
isS3
|
|
||||||
width
|
|
||||||
height
|
|
||||||
comment
|
|
||||||
credit
|
|
||||||
}
|
|
||||||
birthCountry: countryByBirthCountryAlpha2 {
|
|
||||||
alpha2
|
|
||||||
name
|
|
||||||
alias
|
|
||||||
}
|
|
||||||
${actorStashesFields}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, {
|
|
||||||
query,
|
|
||||||
limit,
|
|
||||||
minLength,
|
|
||||||
hasAuth: !!store.state.auth.user,
|
|
||||||
userId: store.state.auth.user?.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return actors.map(actor => curateActor(actor));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchActorReleases({ _commit }, actorId) {
|
async function fetchActorReleases({ _commit }, actorId) {
|
||||||
const releases = await get(`/actors/${actorId}/releases`, {
|
const releases = await get(`/actors/${actorId}/releases`, {
|
||||||
filter: store.state.ui.filter,
|
filter: store.state.ui.filter,
|
||||||
@@ -526,7 +449,6 @@ function initActorActions(store, router) {
|
|||||||
fetchActorById,
|
fetchActorById,
|
||||||
fetchActors,
|
fetchActors,
|
||||||
fetchActorReleases,
|
fetchActorReleases,
|
||||||
searchActors,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
function favoritesStash(state) {
|
function favoritesStash(state) {
|
||||||
return state.user.stashes.find(stash => stash.slug === 'favorites');
|
return state.user.stashes.find((stash) => stash.slug === 'favorites');
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
enabled: window.env.auth,
|
login: window.env.login,
|
||||||
|
signup: window.env.signup,
|
||||||
user: null,
|
user: null,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ function curateActor(actor, release) {
|
|||||||
|
|
||||||
if (actor.profiles) {
|
if (actor.profiles) {
|
||||||
const photos = actor.profiles
|
const photos = actor.profiles
|
||||||
.map(profile => ({ entity: profile.entity, ...profile.avatar }))
|
.map((profile) => ({ entity: profile.entity, ...profile.avatar }))
|
||||||
.filter(avatar => avatar.id && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash));
|
.filter((avatar) => avatar.id && (!curatedActor.avatar || avatar.hash !== curatedActor.avatar.hash));
|
||||||
|
|
||||||
const descriptions = actor.profiles.reduce((acc, profile) => ({
|
const descriptions = actor.profiles.reduce((acc, profile) => ({
|
||||||
...acc,
|
...acc,
|
||||||
@@ -57,10 +57,10 @@ function curateActor(actor, release) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (actor.stashes) {
|
if (actor.stashes) {
|
||||||
curatedActor.stashes = actor.stashes.filter(Boolean).map(stash => curateStash(stash.stash || stash)); // eslint-disable-line no-use-before-define
|
curatedActor.stashes = actor.stashes.filter(Boolean).map((stash) => curateStash(stash.stash || stash)); // eslint-disable-line no-use-before-define
|
||||||
}
|
}
|
||||||
|
|
||||||
curatedActor.stashes = actor.stashes?.map(stash => stash.stash || stash) || [];
|
curatedActor.stashes = actor.stashes?.map((stash) => stash.stash || stash) || [];
|
||||||
|
|
||||||
return curatedActor;
|
return curatedActor;
|
||||||
}
|
}
|
||||||
@@ -70,21 +70,21 @@ function curateRelease(release) {
|
|||||||
...release,
|
...release,
|
||||||
actors: [],
|
actors: [],
|
||||||
poster: release.poster && release.poster.media,
|
poster: release.poster && release.poster.media,
|
||||||
tags: release.tags ? release.tags.map(tag => tag.tag || tag) : [],
|
tags: release.tags ? release.tags.map((tag) => tag.tag || tag) : [],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (release.scenes) curatedRelease.scenes = release.scenes.filter(Boolean).map(({ scene }) => curateRelease(scene));
|
if (release.scenes) curatedRelease.scenes = release.scenes.filter(Boolean).map(({ scene }) => curateRelease(scene));
|
||||||
if (release.movies) curatedRelease.movies = release.movies.filter(Boolean).map(({ movie }) => curateRelease(movie));
|
if (release.movies) curatedRelease.movies = release.movies.filter(Boolean).map(({ movie }) => curateRelease(movie));
|
||||||
if (release.chapters) curatedRelease.chapters = release.chapters.filter(Boolean).map(chapter => curateRelease(chapter));
|
if (release.chapters) curatedRelease.chapters = release.chapters.filter(Boolean).map((chapter) => curateRelease(chapter));
|
||||||
if (release.photos) curatedRelease.photos = release.photos.filter(Boolean).map(photo => photo.media || photo);
|
if (release.photos) curatedRelease.photos = release.photos.filter(Boolean).map((photo) => photo.media || photo);
|
||||||
if (release.covers) curatedRelease.covers = release.covers.filter(Boolean).map(({ media }) => media);
|
if (release.covers) curatedRelease.covers = release.covers.filter(Boolean).map(({ media }) => media);
|
||||||
if (release.trailer) curatedRelease.trailer = release.trailer.media;
|
if (release.trailer) curatedRelease.trailer = release.trailer.media;
|
||||||
if (release.teaser) curatedRelease.teaser = release.teaser.media;
|
if (release.teaser) curatedRelease.teaser = release.teaser.media;
|
||||||
if (release.actors) curatedRelease.actors = release.actors.filter(Boolean).map(actor => curateActor(actor.actor || actor, curatedRelease));
|
if (release.actors) curatedRelease.actors = release.actors.filter(Boolean).map((actor) => curateActor(actor.actor || actor, curatedRelease));
|
||||||
if (release.directors) curatedRelease.directors = release.directors.filter(Boolean).map(director => curateActor(director.director || director, curatedRelease));
|
if (release.directors) curatedRelease.directors = release.directors.filter(Boolean).map((director) => curateActor(director.director || director, curatedRelease));
|
||||||
if (release.movieTags && release.movieTags.length > 0) curatedRelease.tags = release.movieTags.filter(Boolean).map(({ tag }) => tag);
|
if (release.movieTags && release.movieTags.length > 0) curatedRelease.tags = release.movieTags.filter(Boolean).map(({ tag }) => tag);
|
||||||
if (release.movieActors && release.movieActors.length > 0) curatedRelease.actors = release.movieActors.filter(Boolean).map(({ actor }) => curateActor(actor, curatedRelease));
|
if (release.movieActors && release.movieActors.length > 0) curatedRelease.actors = release.movieActors.filter(Boolean).map(({ actor }) => curateActor(actor, curatedRelease));
|
||||||
if (release.stashes) curatedRelease.stashes = release.stashes.filter(Boolean).map(stash => curateStash(stash.stash || stash)); // eslint-disable-line no-use-before-define
|
if (release.stashes) curatedRelease.stashes = release.stashes.filter(Boolean).map((stash) => curateStash(stash.stash || stash)); // eslint-disable-line no-use-before-define
|
||||||
|
|
||||||
if (release.productionLocation) {
|
if (release.productionLocation) {
|
||||||
curatedRelease.productionLocation = {
|
curatedRelease.productionLocation = {
|
||||||
@@ -108,14 +108,16 @@ function curateEntity(entity, parent, releases) {
|
|||||||
|
|
||||||
if (entity.children) {
|
if (entity.children) {
|
||||||
if (entity.children.nodes) {
|
if (entity.children.nodes) {
|
||||||
curatedEntity.children = entity.children.nodes.map(childEntity => curateEntity(childEntity, curatedEntity));
|
curatedEntity.children = entity.children.nodes.map((childEntity) => curateEntity(childEntity, curatedEntity));
|
||||||
}
|
}
|
||||||
|
|
||||||
curatedEntity.childrenTotal = entity.children.totalCount;
|
curatedEntity.childrenTotal = entity.children.totalCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.parent || parent) curatedEntity.parent = curateEntity(entity.parent || parent);
|
if (entity.parent || parent) curatedEntity.parent = curateEntity(entity.parent || parent);
|
||||||
if (releases) curatedEntity.releases = releases.map(release => curateRelease(release));
|
if (releases) curatedEntity.releases = releases.map((release) => curateRelease(release));
|
||||||
|
|
||||||
|
curatedEntity.sceneTotal = entity.sceneTotal;
|
||||||
|
|
||||||
return curatedEntity;
|
return curatedEntity;
|
||||||
}
|
}
|
||||||
@@ -126,6 +128,7 @@ function curateTag(tag) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (tag.releases) curatedTag.releases = tag.releases.map(({ release }) => curateRelease(release));
|
if (tag.releases) curatedTag.releases = tag.releases.map(({ release }) => curateRelease(release));
|
||||||
|
if (tag.banners) curatedTag.banners = tag.banners.map(({ banner }) => banner);
|
||||||
if (tag.photos) curatedTag.photos = tag.photos.map(({ media }) => media);
|
if (tag.photos) curatedTag.photos = tag.photos.map(({ media }) => media);
|
||||||
if (tag.poster) curatedTag.poster = tag.poster.media;
|
if (tag.poster) curatedTag.poster = tag.poster.media;
|
||||||
|
|
||||||
@@ -135,20 +138,30 @@ function curateTag(tag) {
|
|||||||
function curateStash(stash) {
|
function curateStash(stash) {
|
||||||
const curatedStash = stash;
|
const curatedStash = stash;
|
||||||
|
|
||||||
if (stash.scenes) {
|
if (stash.scenes || stash.scenesConnection?.scenes) {
|
||||||
curatedStash.scenes = stash.scenes.map(item => ({
|
curatedStash.sceneTotal = stash.scenesConnection?.totalCount || null;
|
||||||
|
curatedStash.scenes = (stash.scenesConnection?.scenes || stash.scenes).map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
scene: curateRelease(item.scene),
|
scene: curateRelease(item.scene),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stash.actors) {
|
if (stash.actors || stash.actorsConnection?.actors) {
|
||||||
curatedStash.actors = stash.actors.map(item => ({
|
curatedStash.actorTotal = stash.actorsConnection?.totalCount || null;
|
||||||
|
curatedStash.actors = (stash.actorsConnection?.actors || stash.actors).map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
actor: curateActor(item.actor),
|
actor: curateActor(item.actor),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stash.movies || stash.moviesConnection?.movies) {
|
||||||
|
curatedStash.movieTotal = stash.moviesConnection?.totalCount || null;
|
||||||
|
curatedStash.movies = (stash.moviesConnection?.movies || stash.movies).map((item) => ({
|
||||||
|
...item,
|
||||||
|
movie: curateRelease(item.movie),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
return curatedStash;
|
return curatedStash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,11 +173,11 @@ function curateAlert(alert) {
|
|||||||
const curatedAlert = alert;
|
const curatedAlert = alert;
|
||||||
|
|
||||||
if (alert.actors) {
|
if (alert.actors) {
|
||||||
curatedAlert.actors = alert.actors.map(actor => curateActor(actor.actor || actor));
|
curatedAlert.actors = alert.actors.map((actor) => curateActor(actor.actor || actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alert.tags) {
|
if (alert.tags) {
|
||||||
curatedAlert.tags = alert.tags.map(tag => curateTag(tag.tag || tag));
|
curatedAlert.tags = alert.tags.map((tag) => curateTag(tag.tag || tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alert.entity) {
|
if (alert.entity) {
|
||||||
@@ -172,7 +185,7 @@ function curateAlert(alert) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (alert.stashes) {
|
if (alert.stashes) {
|
||||||
curatedAlert.stashes = alert.stashes.map(stash => curateStash(stash.stash || stash));
|
curatedAlert.stashes = alert.stashes.map((stash) => curateStash(stash.stash || stash));
|
||||||
}
|
}
|
||||||
|
|
||||||
return curatedAlert;
|
return curatedAlert;
|
||||||
@@ -186,11 +199,11 @@ function curateUser(user) {
|
|||||||
const curatedUser = user;
|
const curatedUser = user;
|
||||||
|
|
||||||
if (user.stashes) {
|
if (user.stashes) {
|
||||||
curatedUser.stashes = user.stashes.map(stash => curateStash(stash.stash || stash));
|
curatedUser.stashes = user.stashes.map((stash) => curateStash(stash.stash || stash));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.alerts) {
|
if (user.alerts) {
|
||||||
curatedUser.alerts = user.alerts.map(alert => curateAlert(alert.alert || alert));
|
curatedUser.alerts = user.alerts.map((alert) => curateAlert(alert.alert || alert));
|
||||||
}
|
}
|
||||||
|
|
||||||
return curatedUser;
|
return curatedUser;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { graphql } from '../api';
|
import { graphql } from '../api';
|
||||||
// import { sitesFragment, releaseFields } from '../fragments';
|
// import { sitesFragment, releaseFields } from '../fragments';
|
||||||
import { releaseFields } from '../fragments';
|
import { releaseFields, campaignsFragment } from '../fragments';
|
||||||
import { curateEntity } from '../curate';
|
import { curateEntity } from '../curate';
|
||||||
import getDateRange from '../get-date-range';
|
import getDateRange from '../get-date-range';
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ function initEntitiesActions(store, router) {
|
|||||||
}) {
|
}) {
|
||||||
const { before, after, orderBy } = getDateRange(range);
|
const { before, after, orderBy } = getDateRange(range);
|
||||||
|
|
||||||
const { entity, connection: { releases, totalCount } } = await graphql(`
|
const { entity } = await graphql(`
|
||||||
query Entity(
|
query Entity(
|
||||||
$entitySlug: String!
|
$entitySlug: String!
|
||||||
$entityType: String! = "channel"
|
$entityType: String! = "channel"
|
||||||
@@ -22,8 +22,6 @@ function initEntitiesActions(store, router) {
|
|||||||
$offset: Int = 0,
|
$offset: Int = 0,
|
||||||
$after: Datetime = "1900-01-01",
|
$after: Datetime = "1900-01-01",
|
||||||
$before: Datetime = "2100-01-01",
|
$before: Datetime = "2100-01-01",
|
||||||
$afterTime: Datetime = "1900-01-01",
|
|
||||||
$beforeTime: Datetime = "2100-01-01",
|
|
||||||
$orderBy: [ReleasesOrderBy!]
|
$orderBy: [ReleasesOrderBy!]
|
||||||
$exclude: [String!]
|
$exclude: [String!]
|
||||||
$hasAuth: Boolean!
|
$hasAuth: Boolean!
|
||||||
@@ -63,8 +61,14 @@ function initEntitiesActions(store, router) {
|
|||||||
priority
|
priority
|
||||||
independent
|
independent
|
||||||
hasLogo
|
hasLogo
|
||||||
|
${campaignsFragment}
|
||||||
|
sceneTotal
|
||||||
|
children: childEntitiesConnection {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${campaignsFragment}
|
||||||
parent {
|
parent {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@@ -73,83 +77,46 @@ function initEntitiesActions(store, router) {
|
|||||||
url
|
url
|
||||||
independent
|
independent
|
||||||
hasLogo
|
hasLogo
|
||||||
|
${campaignsFragment}
|
||||||
}
|
}
|
||||||
}
|
connection: scenesConnection(
|
||||||
connection: releasesConnection(
|
first: $limit
|
||||||
first: $limit
|
offset: $offset
|
||||||
offset: $offset
|
orderBy: $orderBy
|
||||||
orderBy: $orderBy
|
filter: {
|
||||||
filter: {
|
or: [
|
||||||
and: [
|
{
|
||||||
{
|
date: {
|
||||||
entity: {
|
isNull: ${entityType !== 'network'}
|
||||||
or: [
|
|
||||||
{
|
|
||||||
slug: { equalTo: $entitySlug }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parent: {
|
|
||||||
slug: { equalTo: $entitySlug }
|
|
||||||
type: { equalTo: $entityType }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parent: {
|
|
||||||
parent: {
|
|
||||||
slug: { equalTo: $entitySlug }
|
|
||||||
type: { equalTo: $entityType }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
parent: {
|
|
||||||
parent: {
|
|
||||||
parent: {
|
|
||||||
slug: { equalTo: $entitySlug }
|
|
||||||
type: { equalTo: $entityType }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
or: [
|
|
||||||
{
|
|
||||||
date: {
|
|
||||||
lessThan: $before,
|
|
||||||
greaterThan: $after
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
date: {
|
|
||||||
isNull: true
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
lessThan: $beforeTime,
|
|
||||||
greaterThan: $afterTime,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
|
{
|
||||||
|
date: {
|
||||||
|
isNull: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
date: {
|
||||||
|
lessThan: $before,
|
||||||
|
greaterThan: $after
|
||||||
}
|
}
|
||||||
]
|
releasesTagsConnection: {
|
||||||
releasesTagsConnection: {
|
none: {
|
||||||
none: {
|
tag: {
|
||||||
tag: {
|
slug: {
|
||||||
slug: {
|
in: $exclude
|
||||||
in: $exclude
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
releases: nodes {
|
||||||
|
${releaseFields}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
}
|
}
|
||||||
) {
|
}
|
||||||
releases: nodes {
|
|
||||||
${releaseFields}
|
|
||||||
}
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
entitySlug,
|
entitySlug,
|
||||||
@@ -159,8 +126,6 @@ function initEntitiesActions(store, router) {
|
|||||||
after,
|
after,
|
||||||
before,
|
before,
|
||||||
orderBy,
|
orderBy,
|
||||||
afterTime: store.getters.after,
|
|
||||||
beforeTime: store.getters.before,
|
|
||||||
exclude: store.state.ui.tagFilter,
|
exclude: store.state.ui.tagFilter,
|
||||||
hasAuth: !!store.state.auth.user,
|
hasAuth: !!store.state.auth.user,
|
||||||
userId: store.state.auth.user?.id,
|
userId: store.state.auth.user?.id,
|
||||||
@@ -172,8 +137,8 @@ function initEntitiesActions(store, router) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entity: curateEntity(entity, null, releases),
|
entity: curateEntity(entity, null, entity.connection.releases),
|
||||||
totalCount,
|
totalCount: entity.connection.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,17 +202,17 @@ function initEntitiesActions(store, router) {
|
|||||||
entitySlugs,
|
entitySlugs,
|
||||||
});
|
});
|
||||||
|
|
||||||
return entities.map(entity => curateEntity(entity));
|
return entities.map((entity) => curateEntity(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchEntities({ _commit }, { query, limit = 20 }) {
|
async function searchEntities({ _commit }, { query, limit = 20 }) {
|
||||||
const { entities } = await graphql(`
|
const { entities } = await graphql(`
|
||||||
query SearchEntities(
|
query SearchEntities(
|
||||||
$query: String!
|
$query: String!
|
||||||
$limit:Int = 20,
|
$limit: Int = 20,
|
||||||
) {
|
) {
|
||||||
entities: searchEntities(
|
entities: searchEntities(
|
||||||
search: $query,
|
search: $query
|
||||||
first: $limit
|
first: $limit
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
@@ -271,7 +236,7 @@ function initEntitiesActions(store, router) {
|
|||||||
limit,
|
limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
return entities.map(entity => curateEntity(entity));
|
return entities.map((entity) => curateEntity(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function formatDuration(duration, forceHours) {
|
|||||||
const minutes = Math.floor((duration % 3600) / 60);
|
const minutes = Math.floor((duration % 3600) / 60);
|
||||||
const seconds = Math.floor(duration % 60);
|
const seconds = Math.floor(duration % 60);
|
||||||
|
|
||||||
const [formattedHours, formattedMinutes, formattedSeconds] = [hours, minutes, seconds].map(segment => segment.toString().padStart(2, '0'));
|
const [formattedHours, formattedMinutes, formattedSeconds] = [hours, minutes, seconds].map((segment) => segment.toString().padStart(2, '0'));
|
||||||
|
|
||||||
if (duration >= 3600 || forceHours) {
|
if (duration >= 3600 || forceHours) {
|
||||||
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
|
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
|
||||||
|
|||||||
@@ -96,6 +96,137 @@ const actorFields = `
|
|||||||
${actorStashesFields}
|
${actorStashesFields}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const movieFields = `
|
||||||
|
id
|
||||||
|
title
|
||||||
|
url
|
||||||
|
slug
|
||||||
|
date
|
||||||
|
datePrecision
|
||||||
|
actors {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
entity {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
type
|
||||||
|
parent {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
covers: moviesCovers(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
|
||||||
|
media {
|
||||||
|
id
|
||||||
|
path
|
||||||
|
thumbnail
|
||||||
|
lazy
|
||||||
|
width
|
||||||
|
height
|
||||||
|
thumbnailWidth
|
||||||
|
thumbnailHeight
|
||||||
|
isS3
|
||||||
|
sfw: sfwMedia {
|
||||||
|
id
|
||||||
|
path
|
||||||
|
thumbnail
|
||||||
|
lazy
|
||||||
|
comment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isFavorited
|
||||||
|
isStashed(includeFavorites: false)
|
||||||
|
stashes: stashesMovies(
|
||||||
|
filter: {
|
||||||
|
stash: {
|
||||||
|
userId: {
|
||||||
|
equalTo: $userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) @include(if: $hasAuth) {
|
||||||
|
stash {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const campaignsFragment = `
|
||||||
|
campaigns(filter: {
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
banner: {
|
||||||
|
bannersTagsConnection: {
|
||||||
|
none: {
|
||||||
|
tag: {
|
||||||
|
slug: {
|
||||||
|
in: $exclude
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
bannerExists: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}) {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
affiliate {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
parameters
|
||||||
|
}
|
||||||
|
banner {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
width
|
||||||
|
height
|
||||||
|
ratio
|
||||||
|
entity {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
parent {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entity {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
parent {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const releaseActorsFragment = `
|
const releaseActorsFragment = `
|
||||||
actors: releasesActors(orderBy: ACTOR_BY_ACTOR_ID__GENDER_ASC) {
|
actors: releasesActors(orderBy: ACTOR_BY_ACTOR_ID__GENDER_ASC) {
|
||||||
actor {
|
actor {
|
||||||
@@ -113,7 +244,12 @@ const releaseDirectorFragment = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const releaseTagsFragment = `
|
const releaseTagsFragment = `
|
||||||
tags: releasesTags(orderBy: TAG_BY_TAG_ID__PRIORITY_DESC) {
|
tags: releasesTags(
|
||||||
|
orderBy: TAG_BY_TAG_ID__PRIORITY_DESC
|
||||||
|
filter: {
|
||||||
|
tagId: { isNull: false }
|
||||||
|
}
|
||||||
|
) {
|
||||||
tag {
|
tag {
|
||||||
name
|
name
|
||||||
priority
|
priority
|
||||||
@@ -124,7 +260,7 @@ const releaseTagsFragment = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const releasePosterFragment = `
|
const releasePosterFragment = `
|
||||||
poster: releasesPosterByReleaseId {
|
poster: releasesPoster {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
index
|
index
|
||||||
@@ -199,7 +335,7 @@ const releasePhotosFragment = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const releaseTrailerFragment = `
|
const releaseTrailerFragment = `
|
||||||
trailer: releasesTrailerByReleaseId {
|
trailer: releasesTrailer {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
index
|
index
|
||||||
@@ -213,7 +349,7 @@ const releaseTrailerFragment = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const releaseTeaserFragment = `
|
const releaseTeaserFragment = `
|
||||||
teaser: releasesTeaserByReleaseId {
|
teaser: releasesTeaser {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
index
|
index
|
||||||
@@ -249,6 +385,13 @@ const releaseFields = `
|
|||||||
slug
|
slug
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
|
movies: moviesScenesBySceneId {
|
||||||
|
movie {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
isNew
|
isNew
|
||||||
isFavorited
|
isFavorited
|
||||||
isStashed(includeFavorites: false)
|
isStashed(includeFavorites: false)
|
||||||
@@ -344,7 +487,7 @@ const releaseFragment = `
|
|||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
poster: chaptersPosterByChapterId {
|
poster: chaptersPoster {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
index
|
index
|
||||||
@@ -373,7 +516,7 @@ const releaseFragment = `
|
|||||||
id
|
id
|
||||||
title
|
title
|
||||||
slug
|
slug
|
||||||
covers: moviesCovers {
|
covers: moviesCovers(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
index
|
index
|
||||||
@@ -480,6 +623,8 @@ function getIncludedActors(router) {
|
|||||||
export {
|
export {
|
||||||
actorFields,
|
actorFields,
|
||||||
actorStashesFields,
|
actorStashesFields,
|
||||||
|
campaignsFragment,
|
||||||
|
movieFields,
|
||||||
releaseActorsFragment,
|
releaseActorsFragment,
|
||||||
releaseFields,
|
releaseFields,
|
||||||
releaseTagsFragment,
|
releaseTagsFragment,
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ const dateRanges = {
|
|||||||
latest: () => ({
|
latest: () => ({
|
||||||
after: '1900-01-01',
|
after: '1900-01-01',
|
||||||
before: dayjs.utc().toDate(),
|
before: dayjs.utc().toDate(),
|
||||||
orderBy: ['DATE_DESC', 'CREATED_AT_DESC'],
|
orderBy: ['DATE_DESC'],
|
||||||
}),
|
}),
|
||||||
upcoming: () => ({
|
upcoming: () => ({
|
||||||
after: dayjs.utc().toDate(),
|
after: dayjs.utc().toDate(),
|
||||||
before: '2100-01-01',
|
before: '2100-01-01',
|
||||||
orderBy: ['DATE_ASC', 'CREATED_AT_ASC'],
|
orderBy: ['DATE_DESC'],
|
||||||
}),
|
}),
|
||||||
new: () => ({
|
new: () => ({
|
||||||
after: '1900-01-01 00:00:00',
|
after: '1900-01-01 00:00:00',
|
||||||
@@ -22,7 +22,7 @@ const dateRanges = {
|
|||||||
all: () => ({
|
all: () => ({
|
||||||
after: '1900-01-01',
|
after: '1900-01-01',
|
||||||
before: '2100-01-01',
|
before: '2100-01-01',
|
||||||
orderBy: ['DATE_DESC', 'CREATED_AT_DESC'],
|
orderBy: ['DATE_DESC'],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,10 @@ async function init() {
|
|||||||
document.title = 'traxxx';
|
document.title = 'traxxx';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
beforeCreate() {
|
||||||
|
this.uid = uid;
|
||||||
|
uid += 1;
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatDate,
|
formatDate,
|
||||||
formatDuration,
|
formatDuration,
|
||||||
@@ -117,14 +121,14 @@ async function init() {
|
|||||||
getPath,
|
getPath,
|
||||||
getBgPath: (media, type) => `url(${getPath(media, type)})`,
|
getBgPath: (media, type) => `url(${getPath(media, type)})`,
|
||||||
},
|
},
|
||||||
beforeCreate() {
|
|
||||||
this.uid = uid;
|
|
||||||
uid += 1;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.directive('tooltip', {
|
app.directive('tooltip', {
|
||||||
beforeMount(el, binding) {
|
beforeMount(el, binding) {
|
||||||
|
if (!binding.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// don't include HTML in native title attribute
|
// don't include HTML in native title attribute
|
||||||
const textEl = document.createElement('div');
|
const textEl = document.createElement('div');
|
||||||
textEl.innerHTML = binding.value;
|
textEl.innerHTML = binding.value;
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { graphql } from '../api';
|
import { graphql } from '../api';
|
||||||
import { releasesFragment, releaseFragment, releaseFields } from '../fragments';
|
import {
|
||||||
|
releasesFragment,
|
||||||
|
releaseFragment,
|
||||||
|
releaseFields,
|
||||||
|
movieFields,
|
||||||
|
} from '../fragments';
|
||||||
import { curateRelease } from '../curate';
|
import { curateRelease } from '../curate';
|
||||||
import getDateRange from '../get-date-range';
|
import getDateRange from '../get-date-range';
|
||||||
|
|
||||||
@@ -32,7 +37,7 @@ function initReleasesActions(store, router) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
releases: releases.map(release => curateRelease(release)),
|
releases: releases.map((release) => curateRelease(release)),
|
||||||
totalCount,
|
totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -67,6 +72,8 @@ function initReleasesActions(store, router) {
|
|||||||
query Movies(
|
query Movies(
|
||||||
$limit:Int = 1000,
|
$limit:Int = 1000,
|
||||||
$offset:Int = 0,
|
$offset:Int = 0,
|
||||||
|
$hasAuth: Boolean!
|
||||||
|
$userId: Int
|
||||||
) {
|
) {
|
||||||
connection: moviesConnection(
|
connection: moviesConnection(
|
||||||
first: $limit
|
first: $limit
|
||||||
@@ -79,65 +86,57 @@ function initReleasesActions(store, router) {
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
movies: nodes {
|
movies: nodes {
|
||||||
id
|
${movieFields}
|
||||||
title
|
|
||||||
url
|
|
||||||
slug
|
|
||||||
date
|
|
||||||
datePrecision
|
|
||||||
actors {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
tags {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
entity {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
type
|
|
||||||
parent {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
covers: moviesCovers {
|
|
||||||
media {
|
|
||||||
id
|
|
||||||
path
|
|
||||||
thumbnail
|
|
||||||
lazy
|
|
||||||
width
|
|
||||||
height
|
|
||||||
thumbnailWidth
|
|
||||||
thumbnailHeight
|
|
||||||
isS3
|
|
||||||
sfw: sfwMedia {
|
|
||||||
id
|
|
||||||
path
|
|
||||||
thumbnail
|
|
||||||
lazy
|
|
||||||
comment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
|
hasAuth: !!store.state.auth.user,
|
||||||
|
userId: store.state.auth.user?.id,
|
||||||
limit,
|
limit,
|
||||||
offset: Math.max(0, (pageNumber - 1)) * limit,
|
offset: Math.max(0, (pageNumber - 1)) * limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
movies: movies.map(release => curateRelease(release)),
|
movies: movies.map((release) => curateRelease(release)),
|
||||||
|
totalCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function searchMovies({ _commit }, { query, limit = 20, pageNumber = 1 }) {
|
||||||
|
const { connection: { movies, totalCount } } = await graphql(`
|
||||||
|
query SearchMovies(
|
||||||
|
$query: String!
|
||||||
|
$limit:Int = 20
|
||||||
|
$offset:Int = 0
|
||||||
|
$hasAuth: Boolean!
|
||||||
|
$userId: Int
|
||||||
|
) {
|
||||||
|
connection: searchMoviesConnection(
|
||||||
|
query: $query
|
||||||
|
first: $limit
|
||||||
|
offset: $offset
|
||||||
|
orderBy: RANK_DESC
|
||||||
|
) {
|
||||||
|
movies: nodes {
|
||||||
|
movie {
|
||||||
|
${movieFields}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, {
|
||||||
|
hasAuth: !!store.state.auth.user,
|
||||||
|
userId: store.state.auth.user?.id,
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
offset: Math.max(0, (pageNumber - 1)) * limit,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
movies: movies.map(({ movie }) => curateRelease(movie)),
|
||||||
totalCount,
|
totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -183,7 +182,7 @@ function initReleasesActions(store, router) {
|
|||||||
isS3
|
isS3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
posters: moviesPosterByMovieId {
|
poster: moviesPoster {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
path
|
path
|
||||||
@@ -196,7 +195,7 @@ function initReleasesActions(store, router) {
|
|||||||
isS3
|
isS3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
covers: moviesCovers {
|
covers: moviesCovers(orderBy: MEDIA_BY_MEDIA_ID__INDEX_ASC) {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
path
|
path
|
||||||
@@ -209,7 +208,7 @@ function initReleasesActions(store, router) {
|
|||||||
isS3
|
isS3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trailer: moviesTrailerByMovieId {
|
trailer: moviesTrailer {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
path
|
path
|
||||||
@@ -297,6 +296,7 @@ function initReleasesActions(store, router) {
|
|||||||
fetchReleaseById,
|
fetchReleaseById,
|
||||||
fetchMovies,
|
fetchMovies,
|
||||||
fetchMovieById,
|
fetchMovieById,
|
||||||
|
searchMovies,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/actor/:actorId/:actorSlug',
|
path: '/actor/:actorId/:actorSlug',
|
||||||
name: 'actor',
|
name: 'actor',
|
||||||
redirect: from => ({
|
redirect: (from) => ({
|
||||||
name: 'actorRange',
|
name: 'actorRange',
|
||||||
params: {
|
params: {
|
||||||
...from.params,
|
...from.params,
|
||||||
@@ -91,7 +91,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/director/:actorId/:actorSlug',
|
path: '/director/:actorId/:actorSlug',
|
||||||
name: 'director',
|
name: 'director',
|
||||||
redirect: from => ({
|
redirect: (from) => ({
|
||||||
name: 'directorRange',
|
name: 'directorRange',
|
||||||
params: {
|
params: {
|
||||||
...from.params,
|
...from.params,
|
||||||
@@ -107,7 +107,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/channel/:entitySlug',
|
path: '/channel/:entitySlug',
|
||||||
redirect: from => ({
|
redirect: (from) => ({
|
||||||
name: 'channel',
|
name: 'channel',
|
||||||
params: {
|
params: {
|
||||||
...from.params,
|
...from.params,
|
||||||
@@ -123,7 +123,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/network/:entitySlug',
|
path: '/network/:entitySlug',
|
||||||
redirect: from => ({
|
redirect: (from) => ({
|
||||||
name: 'network',
|
name: 'network',
|
||||||
params: {
|
params: {
|
||||||
...from.params,
|
...from.params,
|
||||||
@@ -139,7 +139,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/studio/:entitySlug',
|
path: '/studio/:entitySlug',
|
||||||
redirect: from => ({
|
redirect: (from) => ({
|
||||||
name: 'studio',
|
name: 'studio',
|
||||||
params: {
|
params: {
|
||||||
...from.params,
|
...from.params,
|
||||||
@@ -155,7 +155,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/tag/:tagSlug',
|
path: '/tag/:tagSlug',
|
||||||
redirect: from => ({
|
redirect: (from) => ({
|
||||||
name: 'tag',
|
name: 'tag',
|
||||||
params: {
|
params: {
|
||||||
...from.params,
|
...from.params,
|
||||||
@@ -171,12 +171,11 @@ const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/actors',
|
path: '/actors',
|
||||||
redirect: from => ({
|
redirect: (from) => ({
|
||||||
name: 'actors',
|
name: 'actors',
|
||||||
params: {
|
params: {
|
||||||
...from.params,
|
...from.params,
|
||||||
gender: 'all',
|
gender: 'all',
|
||||||
letter: 'all',
|
|
||||||
tags: 'all',
|
tags: 'all',
|
||||||
range: 'latest',
|
range: 'latest',
|
||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
@@ -184,7 +183,7 @@ const routes = [
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/actors/:gender?/:letter?/:pageNumber',
|
path: '/actors/:gender?/:pageNumber',
|
||||||
component: Actors,
|
component: Actors,
|
||||||
name: 'actors',
|
name: 'actors',
|
||||||
},
|
},
|
||||||
@@ -196,6 +195,17 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/movies',
|
path: '/movies',
|
||||||
component: Movies,
|
component: Movies,
|
||||||
|
redirect: {
|
||||||
|
name: 'movies',
|
||||||
|
params: {
|
||||||
|
range: 'latest',
|
||||||
|
pageNumber: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/movies/:range/:pageNumber',
|
||||||
|
component: Movies,
|
||||||
name: 'movies',
|
name: 'movies',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -218,7 +228,18 @@ const routes = [
|
|||||||
name: 'notifications',
|
name: 'notifications',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/stash/:stashId/:stashSlug?',
|
path: '/stash/:stashId/:stashSlug',
|
||||||
|
redirect: (from) => ({
|
||||||
|
name: 'stash',
|
||||||
|
params: {
|
||||||
|
...from.params,
|
||||||
|
range: 'scenes',
|
||||||
|
pageNumber: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/stash/:stashId/:stashSlug?/:range/:pageNumber',
|
||||||
component: Stash,
|
component: Stash,
|
||||||
name: 'stash',
|
name: 'stash',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,14 +5,24 @@ import {
|
|||||||
patch,
|
patch,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
|
|
||||||
import { releaseFields, actorStashesFields } from '../fragments';
|
import { releaseFields, actorStashesFields, movieFields } from '../fragments';
|
||||||
import { curateStash } from '../curate';
|
import { curateStash } from '../curate';
|
||||||
|
|
||||||
function initStashesActions(store, _router) {
|
function initStashesActions(store, _router) {
|
||||||
async function fetchStash(context, stashId) {
|
async function fetchStash(context, {
|
||||||
|
stashId,
|
||||||
|
section = 'scenes',
|
||||||
|
pageNumber = 1,
|
||||||
|
limit = 20,
|
||||||
|
}) {
|
||||||
const { stash } = await graphql(`
|
const { stash } = await graphql(`
|
||||||
query Stash(
|
query Stash(
|
||||||
$stashId: Int!
|
$stashId: Int!
|
||||||
|
$includeScenes: Boolean!
|
||||||
|
$includeActors: Boolean!
|
||||||
|
$includeMovies: Boolean!
|
||||||
|
$limit:Int = 10,
|
||||||
|
$offset:Int = 0,
|
||||||
$hasAuth: Boolean!
|
$hasAuth: Boolean!
|
||||||
$userId: Int
|
$userId: Int
|
||||||
) {
|
) {
|
||||||
@@ -26,45 +36,77 @@ function initStashesActions(store, _router) {
|
|||||||
id
|
id
|
||||||
username
|
username
|
||||||
}
|
}
|
||||||
actors: stashesActors {
|
actorsConnection: stashesActorsConnection(
|
||||||
comment
|
orderBy: CREATED_AT_DESC
|
||||||
actor {
|
first: $limit
|
||||||
id
|
offset: $offset
|
||||||
name
|
) @include(if: $includeActors) {
|
||||||
slug
|
totalCount
|
||||||
gender
|
actors: nodes {
|
||||||
age
|
comment
|
||||||
ageFromBirth
|
actor {
|
||||||
dateOfBirth
|
|
||||||
birthCity
|
|
||||||
birthState
|
|
||||||
birthCountry: countryByBirthCountryAlpha2 {
|
|
||||||
alpha2
|
|
||||||
name
|
|
||||||
alias
|
|
||||||
}
|
|
||||||
avatar: avatarMedia {
|
|
||||||
id
|
id
|
||||||
path
|
name
|
||||||
thumbnail
|
slug
|
||||||
lazy
|
gender
|
||||||
isS3
|
age
|
||||||
width
|
ageFromBirth
|
||||||
height
|
dateOfBirth
|
||||||
|
birthCity
|
||||||
|
birthState
|
||||||
|
birthCountry: countryByBirthCountryAlpha2 {
|
||||||
|
alpha2
|
||||||
|
name
|
||||||
|
alias
|
||||||
|
}
|
||||||
|
avatar: avatarMedia {
|
||||||
|
id
|
||||||
|
path
|
||||||
|
thumbnail
|
||||||
|
lazy
|
||||||
|
isS3
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
${actorStashesFields}
|
||||||
}
|
}
|
||||||
${actorStashesFields}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scenes: stashesScenes {
|
scenesConnection: stashesScenesConnection(
|
||||||
comment
|
orderBy: [CREATED_AT_DESC, RELEASE_BY_SCENE_ID__DATE_DESC]
|
||||||
scene {
|
first: $limit
|
||||||
${releaseFields}
|
offset: $offset
|
||||||
|
) @include(if: $includeScenes) {
|
||||||
|
totalCount
|
||||||
|
scenes: nodes {
|
||||||
|
comment
|
||||||
|
scene {
|
||||||
|
${releaseFields}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
moviesConnection: stashesMoviesConnection(
|
||||||
|
orderBy: [CREATED_AT_DESC, MOVIE_BY_MOVIE_ID__DATE_DESC]
|
||||||
|
first: $limit
|
||||||
|
offset: $offset
|
||||||
|
) @include(if: $includeMovies) {
|
||||||
|
totalCount
|
||||||
|
movies: nodes {
|
||||||
|
comment
|
||||||
|
movie {
|
||||||
|
${movieFields}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
stashId: Number(stashId),
|
stashId: Number(stashId),
|
||||||
|
includeScenes: section === 'scenes',
|
||||||
|
includeActors: section === 'actors',
|
||||||
|
includeMovies: section === 'movies',
|
||||||
|
offset: Math.max(0, (pageNumber - 1)) * limit,
|
||||||
|
limit: Number(limit),
|
||||||
hasAuth: !!store.state.auth.user,
|
hasAuth: !!store.state.auth.user,
|
||||||
userId: store.state.auth.user?.id,
|
userId: store.state.auth.user?.id,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function initTagsActions(store, _router) {
|
|||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
poster: tagsPosterByTagId {
|
poster: tagsPoster {
|
||||||
media {
|
media {
|
||||||
id
|
id
|
||||||
thumbnail
|
thumbnail
|
||||||
@@ -103,6 +103,57 @@ function initTagsActions(store, _router) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
banners: bannersTags(filter: {
|
||||||
|
banner: {
|
||||||
|
bannersTagsConnection: {
|
||||||
|
none: {
|
||||||
|
tag: {
|
||||||
|
slug: {
|
||||||
|
in: $exclude
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
banner {
|
||||||
|
id
|
||||||
|
width
|
||||||
|
height
|
||||||
|
type
|
||||||
|
ratio
|
||||||
|
entity {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
independent
|
||||||
|
parent {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
independent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
campaigns {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
entity {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
parent {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
scenesConnection(
|
scenesConnection(
|
||||||
filter: {
|
filter: {
|
||||||
date: {
|
date: {
|
||||||
@@ -137,14 +188,14 @@ function initTagsActions(store, _router) {
|
|||||||
before,
|
before,
|
||||||
orderBy,
|
orderBy,
|
||||||
offset: Math.max(0, (pageNumber - 1)) * limit,
|
offset: Math.max(0, (pageNumber - 1)) * limit,
|
||||||
exclude: store.state.ui.tagFilter.filter(tagFilter => tagFilter !== tagSlug),
|
exclude: store.state.ui.tagFilter.filter((tagFilter) => tagFilter !== tagSlug),
|
||||||
hasAuth: !!store.state.auth.user,
|
hasAuth: !!store.state.auth.user,
|
||||||
userId: store.state.auth.user?.id,
|
userId: store.state.auth.user?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tag: curateTag(tagBySlug, null, curateRelease),
|
tag: curateTag(tagBySlug, null, curateRelease),
|
||||||
releases: tagBySlug.scenesConnection.releases.map(release => curateRelease(release)),
|
releases: tagBySlug.scenesConnection.releases.map((release) => curateRelease(release)),
|
||||||
totalCount: tagBySlug.scenesConnection.totalCount,
|
totalCount: tagBySlug.scenesConnection.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -159,15 +210,21 @@ function initTagsActions(store, _router) {
|
|||||||
query Tags(
|
query Tags(
|
||||||
$slugs: [String!] = [],
|
$slugs: [String!] = [],
|
||||||
$limit: Int = 100
|
$limit: Int = 100
|
||||||
|
$exclude: [String!]
|
||||||
) {
|
) {
|
||||||
tags(
|
tags(
|
||||||
filter: { slug: { in: $slugs } },
|
filter: {
|
||||||
|
slug: {
|
||||||
|
in: $slugs
|
||||||
|
notIn: $exclude
|
||||||
|
}
|
||||||
|
},
|
||||||
first: $limit
|
first: $limit
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
poster: tagsPosterByTagId {
|
poster: tagsPoster {
|
||||||
media {
|
media {
|
||||||
thumbnail
|
thumbnail
|
||||||
comment
|
comment
|
||||||
@@ -206,9 +263,10 @@ function initTagsActions(store, _router) {
|
|||||||
`, {
|
`, {
|
||||||
slugs,
|
slugs,
|
||||||
limit,
|
limit,
|
||||||
|
exclude: store.state.ui.tagFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
return tags.map(tag => curateTag(tag, store.state.ui.sfw));
|
return tags.map((tag) => curateTag(tag, store.state.ui.sfw));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchTags({ _commit }, {
|
async function searchTags({ _commit }, {
|
||||||
@@ -225,14 +283,14 @@ 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
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
poster: tagsPosterByTagId {
|
poster: tagsPoster {
|
||||||
media {
|
media {
|
||||||
thumbnail
|
thumbnail
|
||||||
comment
|
comment
|
||||||
@@ -274,7 +332,7 @@ function initTagsActions(store, _router) {
|
|||||||
minLength,
|
minLength,
|
||||||
});
|
});
|
||||||
|
|
||||||
return tags.map(tag => curateTag(tag, store.state.ui.sfw));
|
return tags.map((tag) => curateTag(tag, store.state.ui.sfw));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchTagReleases({ _commit }, tagId) {
|
async function fetchTagReleases({ _commit }, tagId) {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ function initUiActions(store, _router) {
|
|||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entity: alertsEntityByAlertId {
|
entity: alertsEntity {
|
||||||
entity {
|
entity {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@@ -103,7 +103,7 @@ function initUiActions(store, _router) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const curatedNotifications = notifications.nodes.map(notification => curateNotification(notification));
|
const curatedNotifications = notifications.nodes.map((notification) => curateNotification(notification));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
notifications: curatedNotifications,
|
notifications: curatedNotifications,
|
||||||
@@ -148,7 +148,7 @@ function initUiActions(store, _router) {
|
|||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
actors: searchActors(
|
actors: searchActors(
|
||||||
search: $query,
|
query: $query,
|
||||||
first: $limit
|
first: $limit
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
@@ -221,11 +221,9 @@ function initUiActions(store, _router) {
|
|||||||
userId: store.state.auth.user?.id,
|
userId: store.state.auth.user?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(res.results);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
releases: res?.results.map(result => curateRelease(result.release)) || [],
|
releases: res?.results.map((result) => curateRelease(result.release)) || [],
|
||||||
actors: res?.actors.map(actor => curateActor(actor)) || [],
|
actors: res?.actors.map((actor) => curateActor(actor)) || [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ async function initUiObservers(store, _router) {
|
|||||||
|
|
||||||
body.classList.add(store.state.ui.theme);
|
body.classList.add(store.state.ui.theme);
|
||||||
|
|
||||||
store.watch(state => state.ui.theme, (newTheme, oldTheme) => {
|
store.watch((state) => state.ui.theme, (newTheme, oldTheme) => {
|
||||||
body.classList.add(newTheme);
|
body.classList.add(newTheme);
|
||||||
body.classList.remove(oldTheme);
|
body.classList.remove(oldTheme);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ function initUsersActions(store, _router) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scenes: stashesScenes(first: 20) {
|
scenes: stashesScenes(
|
||||||
|
first: 20
|
||||||
|
orderBy: CREATED_AT_DESC
|
||||||
|
) {
|
||||||
comment
|
comment
|
||||||
scene {
|
scene {
|
||||||
${releaseFields}
|
${releaseFields}
|
||||||
@@ -87,7 +90,7 @@ function initUsersActions(store, _router) {
|
|||||||
${actorFields}
|
${actorFields}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entity: alertsEntityByAlertId {
|
entity: alertsEntity {
|
||||||
entity {
|
entity {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ module.exports = {
|
|||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
cookie: {
|
cookie: {
|
||||||
secure: true,
|
secure: true,
|
||||||
|
maxAge: 2629800000, // 1 month
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -34,7 +35,8 @@ module.exports = {
|
|||||||
secretKey: 'abcdefghijklmnopqrstuvwxyz1234567890ABCD',
|
secretKey: 'abcdefghijklmnopqrstuvwxyz1234567890ABCD',
|
||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
enabled: true,
|
login: true,
|
||||||
|
signup: true,
|
||||||
},
|
},
|
||||||
exclude: {
|
exclude: {
|
||||||
channels: [
|
channels: [
|
||||||
@@ -64,10 +66,7 @@ module.exports = {
|
|||||||
// pornpros
|
// pornpros
|
||||||
'milfhumiliation',
|
'milfhumiliation',
|
||||||
'humiliated',
|
'humiliated',
|
||||||
'flexiblepositions',
|
|
||||||
'publicviolations',
|
|
||||||
'amateurviolations',
|
'amateurviolations',
|
||||||
'squirtdisgrace',
|
|
||||||
'cumdisgrace',
|
'cumdisgrace',
|
||||||
'webcamhackers',
|
'webcamhackers',
|
||||||
'collegeteens',
|
'collegeteens',
|
||||||
@@ -77,6 +76,11 @@ module.exports = {
|
|||||||
'pornmegaload',
|
'pornmegaload',
|
||||||
'scorelandtv',
|
'scorelandtv',
|
||||||
'scoretv',
|
'scoretv',
|
||||||
|
// spizoo
|
||||||
|
'intimatelesbians',
|
||||||
|
'porngoespro',
|
||||||
|
'pornstartease',
|
||||||
|
'thestripperexperience',
|
||||||
// teenscoreclub
|
// teenscoreclub
|
||||||
'maiko',
|
'maiko',
|
||||||
'ncuksinners',
|
'ncuksinners',
|
||||||
@@ -89,6 +93,9 @@ module.exports = {
|
|||||||
networks: [
|
networks: [
|
||||||
// dummy network for testing
|
// dummy network for testing
|
||||||
'traxxx',
|
'traxxx',
|
||||||
|
// porn doe
|
||||||
|
'forbondage',
|
||||||
|
'score',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
profiles: [
|
profiles: [
|
||||||
@@ -140,6 +147,11 @@ module.exports = {
|
|||||||
'hotcrazymess',
|
'hotcrazymess',
|
||||||
'thatsitcomshow',
|
'thatsitcomshow',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
// Adult DVD Empire
|
||||||
|
'elegantangel',
|
||||||
|
'westcoastproductions',
|
||||||
|
],
|
||||||
'21sextury',
|
'21sextury',
|
||||||
'julesjordan',
|
'julesjordan',
|
||||||
'dorcelclub',
|
'dorcelclub',
|
||||||
@@ -237,6 +249,7 @@ module.exports = {
|
|||||||
'www.tushy.com',
|
'www.tushy.com',
|
||||||
'www.tushyraw.com',
|
'www.tushyraw.com',
|
||||||
'www.deeper.com',
|
'www.deeper.com',
|
||||||
|
'www.slayed.com',
|
||||||
'sthw-trailer-vixen.ssl-cdn.com',
|
'sthw-trailer-vixen.ssl-cdn.com',
|
||||||
'sthw-trailer-tushy.ssl-cdn.com',
|
'sthw-trailer-tushy.ssl-cdn.com',
|
||||||
'sthw-trailer-tushyraw.ssl-cdn.com',
|
'sthw-trailer-tushyraw.ssl-cdn.com',
|
||||||
@@ -249,6 +262,30 @@ module.exports = {
|
|||||||
'streamhw-trailer-blacked.ssl-cdn.com',
|
'streamhw-trailer-blacked.ssl-cdn.com',
|
||||||
'streamhw-trailer-blackedraw.ssl-cdn.com',
|
'streamhw-trailer-blackedraw.ssl-cdn.com',
|
||||||
'streamhw-trailer-deeper.ssl-cdn.com',
|
'streamhw-trailer-deeper.ssl-cdn.com',
|
||||||
|
'cdn.vixen.com',
|
||||||
|
'cdn.tushy.com',
|
||||||
|
'cdn.blacked.com',
|
||||||
|
'cdn.deeper.com',
|
||||||
|
'cdn.blackedraw.com',
|
||||||
|
'cdn.tushyraw.com',
|
||||||
|
'cdn.slayed.com',
|
||||||
|
'www.vogov.com',
|
||||||
|
'www.vogov.com',
|
||||||
|
'www.nubiles.net',
|
||||||
|
'www.brattysis.com',
|
||||||
|
'www.deeplush.com',
|
||||||
|
'www.detentiongirls.com',
|
||||||
|
'www.daddyslilangel.com',
|
||||||
|
'www.myfamilypies.com',
|
||||||
|
'www.bountyhunterporn.com',
|
||||||
|
'www.anilos.com',
|
||||||
|
'www.thatsitcomshow.com',
|
||||||
|
'www.nubileset.com',
|
||||||
|
'www.nubilesunscripted.com',
|
||||||
|
'www.nubilefilms.com',
|
||||||
|
'www.badteenspunished.com',
|
||||||
|
'www.cumlouder.com',
|
||||||
|
'im0.imgcm.com',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
limits: {
|
limits: {
|
||||||
@@ -261,15 +298,26 @@ module.exports = {
|
|||||||
interval: 1000,
|
interval: 1000,
|
||||||
concurrency: 1,
|
concurrency: 1,
|
||||||
},
|
},
|
||||||
'www.realitykings.com': {
|
'westcoastproductions.com': {
|
||||||
interval: 1000,
|
interval: 100,
|
||||||
|
concurrency: 1,
|
||||||
|
},
|
||||||
|
'images.mylfcdn.net': {
|
||||||
|
interval: 50,
|
||||||
concurrency: 1,
|
concurrency: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fetchAfter: [1, 'week'],
|
fetchAfter: [1, 'week'],
|
||||||
missingDateLimit: 3,
|
missingDateLimit: 3,
|
||||||
|
memorySampling: {
|
||||||
|
enabled: false,
|
||||||
|
sampleDuration: 300000, // 5 minutes
|
||||||
|
snapshotIntervals: [],
|
||||||
|
},
|
||||||
media: {
|
media: {
|
||||||
path: './media',
|
path: './media',
|
||||||
|
maxSize: 1000,
|
||||||
|
quality: 80,
|
||||||
thumbnailSize: 320, // width for 16:9 will be exactly 576px
|
thumbnailSize: 320, // width for 16:9 will be exactly 576px
|
||||||
thumbnailQuality: 100,
|
thumbnailQuality: 100,
|
||||||
lazySize: 90,
|
lazySize: 90,
|
||||||
|
|||||||
36768
docs/names.md
Normal file
@@ -1,6 +1,6 @@
|
|||||||
const config = require('config');
|
const config = require('config');
|
||||||
|
|
||||||
exports.up = knex => Promise.resolve()
|
exports.up = (knex) => Promise.resolve()
|
||||||
.then(() => knex.schema.createTable('countries', (table) => {
|
.then(() => knex.schema.createTable('countries', (table) => {
|
||||||
table.text('alpha2', 2)
|
table.text('alpha2', 2)
|
||||||
.unique()
|
.unique()
|
||||||
@@ -108,7 +108,7 @@ exports.up = knex => Promise.resolve()
|
|||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.date('date');
|
table.date('date');
|
||||||
table.enum('date_precision', ['year', 'month', 'day', 'hour', 'minute', 'second'])
|
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
|
||||||
.defaultTo('year');
|
.defaultTo('year');
|
||||||
|
|
||||||
table.text('comment');
|
table.text('comment');
|
||||||
@@ -643,7 +643,7 @@ exports.up = knex => Promise.resolve()
|
|||||||
.references('alpha2')
|
.references('alpha2')
|
||||||
.inTable('countries');
|
.inTable('countries');
|
||||||
|
|
||||||
table.enum('date_precision', ['year', 'month', 'day', 'hour', 'minute', 'second'])
|
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
|
||||||
.defaultTo('day');
|
.defaultTo('day');
|
||||||
|
|
||||||
table.text('description');
|
table.text('description');
|
||||||
@@ -667,6 +667,11 @@ exports.up = knex => Promise.resolve()
|
|||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.datetime('created_at')
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
|
||||||
|
table.datetime('updated_at')
|
||||||
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('releases_actors', (table) => {
|
.then(() => knex.schema.createTable('releases_actors', (table) => {
|
||||||
@@ -787,7 +792,6 @@ exports.up = knex => Promise.resolve()
|
|||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('releases_tags', (table) => {
|
.then(() => knex.schema.createTable('releases_tags', (table) => {
|
||||||
table.integer('tag_id', 12)
|
table.integer('tag_id', 12)
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('tags');
|
.inTable('tags');
|
||||||
|
|
||||||
@@ -797,6 +801,8 @@ exports.up = knex => Promise.resolve()
|
|||||||
.inTable('releases')
|
.inTable('releases')
|
||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('original_tag');
|
||||||
|
|
||||||
table.unique(['tag_id', 'release_id']);
|
table.unique(['tag_id', 'release_id']);
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('releases_search', (table) => {
|
.then(() => knex.schema.createTable('releases_search', (table) => {
|
||||||
@@ -827,7 +833,7 @@ exports.up = knex => Promise.resolve()
|
|||||||
table.timestamp('date');
|
table.timestamp('date');
|
||||||
table.index('date');
|
table.index('date');
|
||||||
|
|
||||||
table.enum('date_precision', ['year', 'month', 'day', 'hour', 'minute', 'second'])
|
table.enum('date_precision', ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'])
|
||||||
.defaultTo('day');
|
.defaultTo('day');
|
||||||
|
|
||||||
table.text('description');
|
table.text('description');
|
||||||
@@ -905,10 +911,17 @@ exports.up = knex => Promise.resolve()
|
|||||||
table.text('media_id', 21)
|
table.text('media_id', 21)
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('media');
|
.inTable('media')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.unique('movie_id');
|
table.unique('movie_id');
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('movies_search', (table) => {
|
||||||
|
table.integer('movie_id', 16)
|
||||||
|
.references('id')
|
||||||
|
.inTable('movies')
|
||||||
|
.onDelete('cascade');
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('chapters', (table) => {
|
.then(() => knex.schema.createTable('chapters', (table) => {
|
||||||
table.increments('id', 16);
|
table.increments('id', 16);
|
||||||
|
|
||||||
@@ -973,7 +986,6 @@ exports.up = knex => Promise.resolve()
|
|||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('chapters_tags', (table) => {
|
.then(() => knex.schema.createTable('chapters_tags', (table) => {
|
||||||
table.integer('tag_id', 12)
|
table.integer('tag_id', 12)
|
||||||
.notNullable()
|
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('tags')
|
.inTable('tags')
|
||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
@@ -984,6 +996,8 @@ exports.up = knex => Promise.resolve()
|
|||||||
.inTable('chapters')
|
.inTable('chapters')
|
||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.text('original_tag');
|
||||||
|
|
||||||
table.unique(['tag_id', 'chapter_id']);
|
table.unique(['tag_id', 'chapter_id']);
|
||||||
}))
|
}))
|
||||||
.then(() => knex.schema.createTable('users_roles', (table) => {
|
.then(() => knex.schema.createTable('users_roles', (table) => {
|
||||||
@@ -1268,11 +1282,107 @@ exports.up = knex => Promise.resolve()
|
|||||||
.notNullable()
|
.notNullable()
|
||||||
.defaultTo(knex.fn.now());
|
.defaultTo(knex.fn.now());
|
||||||
}))
|
}))
|
||||||
// SEARCH
|
.then(() => knex.schema.createTable('affiliates', (table) => {
|
||||||
|
table.string('id')
|
||||||
|
.primary()
|
||||||
|
.unique()
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.integer('entity_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
|
||||||
|
table.text('url');
|
||||||
|
table.text('parameters');
|
||||||
|
|
||||||
|
table.unique(['entity_id', 'url']);
|
||||||
|
table.unique(['entity_id', 'parameters']);
|
||||||
|
|
||||||
|
table.text('comment');
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('banners', (table) => {
|
||||||
|
table.string('id')
|
||||||
|
.primary()
|
||||||
|
.unique()
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.string('type')
|
||||||
|
.defaultTo('jpg');
|
||||||
|
|
||||||
|
table.integer('width')
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.integer('height')
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.integer('entity_id', 12)
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
|
||||||
|
table.text('comment');
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('banners_tags', (table) => {
|
||||||
|
table.increments('id');
|
||||||
|
|
||||||
|
table.string('banner_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('banners');
|
||||||
|
|
||||||
|
table.integer('tag_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('tags');
|
||||||
|
|
||||||
|
table.unique(['banner_id', 'tag_id']);
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.createTable('campaigns', (table) => {
|
||||||
|
table.increments('id');
|
||||||
|
|
||||||
|
table.integer('entity_id', 12)
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('entities');
|
||||||
|
|
||||||
|
table.text('affiliate_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('affiliates');
|
||||||
|
|
||||||
|
table.text('url');
|
||||||
|
|
||||||
|
table.string('banner_id')
|
||||||
|
.references('id')
|
||||||
|
.inTable('banners');
|
||||||
|
|
||||||
|
table.text('comment');
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
|
// SEARCH AND SORT
|
||||||
.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(`
|
||||||
ALTER TABLE releases_search ADD COLUMN document tsvector;
|
ALTER TABLE releases_search ADD COLUMN document tsvector;
|
||||||
|
ALTER TABLE movies_search ADD COLUMN document tsvector;
|
||||||
|
|
||||||
|
/* allow scenes without dates to be mixed inbetween scenes with dates */
|
||||||
|
ALTER TABLE releases
|
||||||
|
ADD COLUMN effective_date timestamptz
|
||||||
|
GENERATED ALWAYS AS (COALESCE(date, created_at)) STORED;
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// INDEXES
|
// INDEXES
|
||||||
@@ -1282,14 +1392,23 @@ exports.up = knex => Promise.resolve()
|
|||||||
CREATE UNIQUE INDEX unique_actor_slugs_network ON actors (slug, entity_id, entry_id);
|
CREATE UNIQUE INDEX unique_actor_slugs_network ON actors (slug, entity_id, entry_id);
|
||||||
CREATE UNIQUE INDEX unique_actor_slugs ON actors (slug) WHERE entity_id IS NULL;
|
CREATE UNIQUE INDEX unique_actor_slugs ON actors (slug) WHERE entity_id IS NULL;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX unique_entity_campaigns_banner_url ON campaigns (entity_id, url, banner_id) WHERE affiliate_id IS NULL;
|
||||||
|
CREATE UNIQUE INDEX unique_entity_campaigns_url ON campaigns (entity_id, url) WHERE banner_id IS NULL AND affiliate_id IS NULL;
|
||||||
|
CREATE UNIQUE INDEX unique_entity_campaigns_banner_affiliate ON campaigns (entity_id, affiliate_id, banner_id) WHERE url IS NULL;
|
||||||
|
CREATE UNIQUE INDEX unique_entity_campaigns_affiliate ON campaigns (entity_id, affiliate_id) WHERE banner_id IS NULL AND url IS NULL;
|
||||||
|
|
||||||
CREATE UNIQUE INDEX releases_search_unique ON releases_search (release_id);
|
CREATE UNIQUE INDEX releases_search_unique ON releases_search (release_id);
|
||||||
|
CREATE UNIQUE INDEX movies_search_unique ON movies_search (movie_id);
|
||||||
CREATE INDEX releases_search_index ON releases_search USING GIN (document);
|
CREATE INDEX releases_search_index ON releases_search USING GIN (document);
|
||||||
|
CREATE INDEX movies_search_index ON movies_search USING GIN (document);
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
.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 IF NOT EXISTS 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;
|
||||||
@@ -1299,6 +1418,7 @@ exports.up = knex => Promise.resolve()
|
|||||||
* Using a table as a proxy for the search results allows us to get both a reference to the releases table, and the ranking.
|
* Using a table as a proxy for the search results allows us to get both a reference to the releases table, and the ranking.
|
||||||
* A composite type does not seem to be compatible with PostGraphile's @sortable, and a view does not allow for many native constraints */
|
* A composite type does not seem to be compatible with PostGraphile's @sortable, and a view does not allow for many native constraints */
|
||||||
CREATE TABLE releases_search_results (release_id integer, rank real, FOREIGN KEY (release_id) REFERENCES releases (id));
|
CREATE TABLE releases_search_results (release_id integer, rank real, FOREIGN KEY (release_id) REFERENCES releases (id));
|
||||||
|
CREATE TABLE movies_search_results (movie_id integer, rank real, FOREIGN KEY (movie_id) REFERENCES movies (id));
|
||||||
|
|
||||||
CREATE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
CREATE FUNCTION search_releases(query text) RETURNS SETOF releases_search_results AS $$
|
||||||
SELECT releases.id, ranks.rank FROM (
|
SELECT releases.id, ranks.rank FROM (
|
||||||
@@ -1312,6 +1432,18 @@ exports.up = knex => Promise.resolve()
|
|||||||
ORDER BY ranks.rank DESC;
|
ORDER BY ranks.rank DESC;
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION search_movies(query text) RETURNS SETOF movies_search_results AS $$
|
||||||
|
SELECT movies.id, ranks.rank FROM (
|
||||||
|
SELECT
|
||||||
|
movies_search.movie_id,
|
||||||
|
ts_rank(movies_search.document, to_tsquery('english', array_to_string(array(SELECT * FROM regexp_matches(query, '[A-Za-zÀ-ÖØ-öø-ÿ0-9]+', 'g')), '|'))) AS rank
|
||||||
|
FROM movies_search
|
||||||
|
) ranks
|
||||||
|
LEFT JOIN movies ON movies.id = ranks.movie_id
|
||||||
|
WHERE ranks.rank > 0
|
||||||
|
ORDER BY ranks.rank DESC;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION search_entities(search text) RETURNS SETOF entities AS $$
|
CREATE FUNCTION search_entities(search text) RETURNS SETOF entities AS $$
|
||||||
SELECT * FROM entities
|
SELECT * FROM entities
|
||||||
WHERE
|
WHERE
|
||||||
@@ -1320,25 +1452,29 @@ exports.up = knex => Promise.resolve()
|
|||||||
array_to_string(alias, '') ILIKE ('%' || TRIM(search) || '%') OR
|
array_to_string(alias, '') ILIKE ('%' || TRIM(search) || '%') OR
|
||||||
replace(array_to_string(alias, ''), ' ', '') ILIKE ('%' || TRIM(search) || '%') OR
|
replace(array_to_string(alias, ''), ' ', '') ILIKE ('%' || TRIM(search) || '%') OR
|
||||||
url ILIKE ('%' || search || '%')
|
url ILIKE ('%' || search || '%')
|
||||||
|
ORDER BY similarity(entities.name, search) DESC, name ASC
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
CREATE FUNCTION search_actors(search text, min_length smallint DEFAULT 2) RETURNS SETOF actors AS $$
|
CREATE FUNCTION search_actors(query text, min_length smallint DEFAULT 2) RETURNS SETOF actors AS $$
|
||||||
SELECT * FROM actors
|
SELECT * FROM actors
|
||||||
WHERE length(search) >= min_length
|
WHERE query IS NULL
|
||||||
AND name ILIKE ('%' || TRIM(search) || '%')
|
OR length(query) >= min_length
|
||||||
$$ 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
|
AND CASE
|
||||||
WHEN is_primary
|
WHEN length(query) > 1 THEN name ILIKE ('%' || TRIM(query) || '%')
|
||||||
THEN tags.alias_for IS NULL
|
WHEN length(query) = 1 THEN name ILIKE (TRIM(query) || '%')
|
||||||
ELSE true
|
ELSE true
|
||||||
END
|
END
|
||||||
$$ LANGUAGE SQL STABLE;
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
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 $$
|
CREATE FUNCTION actors_tags(actor actors, selectable_tags text[]) RETURNS SETOF tags AS $$
|
||||||
SELECT tags.*
|
SELECT tags.*
|
||||||
FROM releases_actors
|
FROM releases_actors
|
||||||
@@ -1470,6 +1606,16 @@ exports.up = knex => Promise.resolve()
|
|||||||
CREATE FUNCTION releases_is_new(release releases) RETURNS boolean AS $$
|
CREATE FUNCTION releases_is_new(release releases) RETURNS boolean AS $$
|
||||||
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
|
SELECT EXISTS(SELECT true WHERE (SELECT id FROM batches ORDER BY created_at DESC LIMIT 1) = release.created_batch_id);
|
||||||
$$ LANGUAGE sql STABLE;
|
$$ LANGUAGE sql STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION banners_ratio(banner banners) RETURNS numeric AS $$
|
||||||
|
SELECT ROUND(banner.width::decimal / banner.height::decimal, 2);
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION get_random_campaign() RETURNS SETOF campaigns AS $$
|
||||||
|
SELECT * FROM campaigns
|
||||||
|
ORDER BY random()
|
||||||
|
LIMIT 1;
|
||||||
|
$$ LANGUAGE sql STABLE;
|
||||||
`);
|
`);
|
||||||
})
|
})
|
||||||
// POLICIES
|
// POLICIES
|
||||||
@@ -1603,8 +1749,11 @@ exports.up = knex => Promise.resolve()
|
|||||||
COMMENT ON FUNCTION actors_actors IS E'@sortable';
|
COMMENT ON FUNCTION actors_actors IS E'@sortable';
|
||||||
COMMENT ON FUNCTION actors_scenes IS E'@sortable';
|
COMMENT ON FUNCTION actors_scenes IS E'@sortable';
|
||||||
COMMENT ON FUNCTION tags_scenes IS E'@sortable';
|
COMMENT ON FUNCTION tags_scenes IS E'@sortable';
|
||||||
|
|
||||||
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
COMMENT ON FUNCTION search_releases IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_entities IS E'@sortable';
|
||||||
COMMENT ON FUNCTION search_actors IS E'@sortable';
|
COMMENT ON FUNCTION search_actors IS E'@sortable';
|
||||||
|
COMMENT ON FUNCTION search_movies IS E'@sortable';
|
||||||
COMMENT ON FUNCTION search_tags IS E'@sortable';
|
COMMENT ON FUNCTION search_tags IS E'@sortable';
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
@@ -1622,6 +1771,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
|||||||
DROP TABLE IF EXISTS releases_teasers CASCADE;
|
DROP TABLE IF EXISTS releases_teasers CASCADE;
|
||||||
DROP TABLE IF EXISTS releases_tags CASCADE;
|
DROP TABLE IF EXISTS releases_tags CASCADE;
|
||||||
DROP TABLE IF EXISTS releases_search CASCADE;
|
DROP TABLE IF EXISTS releases_search CASCADE;
|
||||||
|
DROP TABLE IF EXISTS movies_search CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS movies_scenes CASCADE;
|
DROP TABLE IF EXISTS movies_scenes CASCADE;
|
||||||
DROP TABLE IF EXISTS movies_covers CASCADE;
|
DROP TABLE IF EXISTS movies_covers CASCADE;
|
||||||
@@ -1636,6 +1786,11 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
|||||||
DROP TABLE IF EXISTS chapters_posters CASCADE;
|
DROP TABLE IF EXISTS chapters_posters CASCADE;
|
||||||
DROP TABLE IF EXISTS chapters_photos CASCADE;
|
DROP TABLE IF EXISTS chapters_photos CASCADE;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS banners_tags CASCADE;
|
||||||
|
DROP TABLE IF EXISTS banners CASCADE;
|
||||||
|
DROP TABLE IF EXISTS campaigns CASCADE;
|
||||||
|
DROP TABLE IF EXISTS affiliates CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS batches CASCADE;
|
DROP TABLE IF EXISTS batches CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS actors_avatars CASCADE;
|
DROP TABLE IF EXISTS actors_avatars CASCADE;
|
||||||
@@ -1648,6 +1803,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
|||||||
|
|
||||||
DROP TABLE IF EXISTS entities_tags CASCADE;
|
DROP TABLE IF EXISTS entities_tags CASCADE;
|
||||||
DROP TABLE IF EXISTS entities_social CASCADE;
|
DROP TABLE IF EXISTS entities_social CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS sites_tags CASCADE;
|
DROP TABLE IF EXISTS sites_tags CASCADE;
|
||||||
DROP TABLE IF EXISTS sites_social CASCADE;
|
DROP TABLE IF EXISTS sites_social CASCADE;
|
||||||
DROP TABLE IF EXISTS networks_social CASCADE;
|
DROP TABLE IF EXISTS networks_social CASCADE;
|
||||||
@@ -1692,6 +1848,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;
|
||||||
|
|
||||||
@@ -1710,7 +1867,9 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
|||||||
DROP POLICY IF EXISTS stashes_policy ON stashes_actors;
|
DROP POLICY IF EXISTS stashes_policy ON stashes_actors;
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS current_user_id;
|
DROP FUNCTION IF EXISTS current_user_id;
|
||||||
|
DROP FUNCTION IF EXISTS get_random_campaign;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS releases_search_results;
|
DROP TABLE IF EXISTS releases_search_results;
|
||||||
|
DROP TABLE IF EXISTS movies_search_results;
|
||||||
`);
|
`);
|
||||||
};
|
};
|
||||||
|
|||||||
29
migrations/20211030162955_entity_scenes.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
exports.up = async (knex) => knex.raw(`
|
||||||
|
CREATE FUNCTION entities_scenes(entity entities) RETURNS SETOF releases AS $$
|
||||||
|
WITH RECURSIVE children AS (
|
||||||
|
SELECT entities.id
|
||||||
|
FROM entities
|
||||||
|
WHERE entities.id = entity.id
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT entities.id
|
||||||
|
FROM entities
|
||||||
|
INNER JOIN children ON children.id = entities.parent_id
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT releases FROM releases
|
||||||
|
INNER JOIN children ON children.id = releases.entity_id
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
SELECT releases FROM releases
|
||||||
|
INNER JOIN children ON children.id = releases.studio_id;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION entities_scenes IS E'@sortable';
|
||||||
|
`);
|
||||||
|
|
||||||
|
exports.down = async (knex) => knex.raw(`
|
||||||
|
DROP FUNCTION IF EXISTS entities_scenes;
|
||||||
|
`);
|
||||||
15
migrations/20220123234131_scene_indexes.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
exports.up = async (knex) => Promise.resolve()
|
||||||
|
.then(() => knex.schema.alterTable('releases_tags', (table) => {
|
||||||
|
table.index('release_id');
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.alterTable('movies_scenes', (table) => {
|
||||||
|
table.index('scene_id');
|
||||||
|
}));
|
||||||
|
|
||||||
|
exports.down = async (knex) => Promise.resolve()
|
||||||
|
.then(() => knex.schema.alterTable('releases_tags', (table) => {
|
||||||
|
table.dropIndex('release_id');
|
||||||
|
}))
|
||||||
|
.then(() => knex.schema.alterTable('movies_scenes', (table) => {
|
||||||
|
table.dropIndex('scene_id');
|
||||||
|
}));
|
||||||
11
migrations/20220126132955_scene_counts.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
exports.up = async (knex) => knex.raw(`
|
||||||
|
CREATE OR REPLACE FUNCTION entities_scene_total(entity entities) RETURNS integer AS $$
|
||||||
|
SELECT COUNT(id)
|
||||||
|
FROM releases
|
||||||
|
WHERE releases.entity_id = entity.id;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
`);
|
||||||
|
|
||||||
|
exports.down = async (knex) => knex.raw(`
|
||||||
|
DROP FUNCTION IF EXISTS entities_scene_total;
|
||||||
|
`);
|
||||||
8
migrations/20220209010315_movies_tags.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
exports.up = async (knex) => knex.raw(`
|
||||||
|
CREATE VIEW movies_tagged AS
|
||||||
|
SELECT * FROM movies;
|
||||||
|
`);
|
||||||
|
|
||||||
|
exports.down = async (knex) => knex.raw(`
|
||||||
|
DROP VIEW IF EXISTS movies_tagged;
|
||||||
|
`);
|
||||||
29684
package-lock.json
generated
90
package.json
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.193.3",
|
"version": "1.209.4",
|
||||||
"description": "All the latest porn releases in one place",
|
"description": "All the latest porn releases in one place",
|
||||||
"main": "src/app.js",
|
"main": "src/app.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node -r source-map-support/register src/init.js",
|
"start": "node -r source-map-support/register src/init.js",
|
||||||
"webpack": "webpack --env=production --mode=production",
|
"webpack": "webpack --env=production --mode=production",
|
||||||
"webpack-dev": "webpack --env=development --mode=development",
|
"webpack-dev": "webpack --env=development --mode=development",
|
||||||
"webpack-watch": "webpack --progress --colors --watch --env=development --mode=development",
|
"webpack-watch": "webpack --progress --color --watch --env=development --mode=development",
|
||||||
"babel": "babel src --source-maps -d dist",
|
"babel": "babel src --source-maps -d dist",
|
||||||
"babel-watch": "babel src -w --source-maps -d dist",
|
"babel-watch": "babel src -w --source-maps -d dist",
|
||||||
"build": "babel src --source-maps -d dist && webpack --env=production --mode=production",
|
"build": "babel src --source-maps -d dist && webpack --env=production --mode=production",
|
||||||
@@ -40,38 +40,37 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.12.10",
|
"@babel/cli": "^7.12.10",
|
||||||
"@babel/core": "^7.8.4",
|
"@babel/core": "^7.8.4",
|
||||||
|
"@babel/eslint-parser": "^7.16.0",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
|
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
|
||||||
"@babel/preset-env": "^7.8.4",
|
"@babel/preset-env": "^7.8.4",
|
||||||
"@babel/register": "^7.8.3",
|
"@babel/register": "^7.8.3",
|
||||||
"@vue/compiler-sfc": "^3.0.4",
|
"autoprefixer": "^10.4.0",
|
||||||
"autoprefixer": "^9.7.4",
|
|
||||||
"babel-eslint": "^10.1.0",
|
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-preset-airbnb": "^3.3.2",
|
"babel-preset-airbnb": "^5.0.0",
|
||||||
"css-loader": "^5.0.1",
|
"css-loader": "^6.5.0",
|
||||||
"eslint": "^7.20.0",
|
"eslint": "^8.1.0",
|
||||||
"eslint-config-airbnb": "^17.1.1",
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
"eslint-config-airbnb-base": "^13.2.0",
|
"eslint-config-airbnb-base": "^14.2.1",
|
||||||
"eslint-plugin-import": "^2.20.1",
|
"eslint-plugin-import": "^2.20.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||||
"eslint-plugin-react": "^7.18.3",
|
"eslint-plugin-react": "^7.18.3",
|
||||||
"eslint-plugin-vue": "^6.2.1",
|
"eslint-plugin-vue": "^8.0.3",
|
||||||
"eslint-watch": "^4.0.2",
|
"eslint-watch": "^7.0.0",
|
||||||
"eslint-webpack-plugin": "^2.5.2",
|
"eslint-webpack-plugin": "^3.1.0",
|
||||||
"mini-css-extract-plugin": "^1.3.3",
|
"mini-css-extract-plugin": "^2.4.3",
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^6.0.1",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^6.2.0",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"sass-loader": "^11.0.1",
|
"sass-loader": "^12.3.0",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^3.3.1",
|
||||||
"vue-loader": "^16.1.2",
|
"vue-loader": "^16.8.2",
|
||||||
"webpack": "^5.11.0",
|
"webpack": "^5.11.0",
|
||||||
"webpack-cli": "^3.3.11"
|
"webpack-cli": "^4.9.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@casl/ability": "^5.2.2",
|
"@casl/ability": "^5.2.2",
|
||||||
"@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6",
|
"@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6",
|
||||||
"@graphile-contrib/pg-simplify-inflector": "^5.0.0-beta.1",
|
"@graphile-contrib/pg-simplify-inflector": "^6.1.0",
|
||||||
"acorn": "^8.0.4",
|
"acorn": "^8.0.4",
|
||||||
"array-equal": "^1.0.0",
|
"array-equal": "^1.0.0",
|
||||||
"aws-sdk": "^2.847.0",
|
"aws-sdk": "^2.847.0",
|
||||||
@@ -88,61 +87,62 @@
|
|||||||
"cloudscraper": "^4.6.0",
|
"cloudscraper": "^4.6.0",
|
||||||
"config": "^3.2.5",
|
"config": "^3.2.5",
|
||||||
"connect-session-knex": "^2.0.0",
|
"connect-session-knex": "^2.0.0",
|
||||||
"convert": "^1.6.2",
|
"convert": "^4.2.4",
|
||||||
"cookie": "^0.4.0",
|
"cookie": "^0.4.0",
|
||||||
"csv-stringify": "^5.3.6",
|
"csv-stringify": "^5.3.6",
|
||||||
"dayjs": "^1.8.21",
|
"dayjs": "^1.8.21",
|
||||||
"dompurify": "^2.0.11",
|
"dompurify": "^2.0.11",
|
||||||
"ejs": "^3.0.1",
|
"ejs": "^3.0.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-promise-router": "^3.0.3",
|
"express-promise-router": "^4.1.0",
|
||||||
"express-react-views": "^0.11.0",
|
"express-react-views": "^0.11.0",
|
||||||
"express-session": "^1.17.1",
|
"express-session": "^1.17.1",
|
||||||
"face-api.js": "^0.22.2",
|
"face-api.js": "^0.22.2",
|
||||||
"faker": "^5.1.0",
|
"faker": "^5.1.0",
|
||||||
"file-type": "^14.1.4",
|
"file-type": "^16.5.3",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"fs-extra": "^7.0.1",
|
"fs-extra": "^10.0.0",
|
||||||
"graphile-utils": "^4.5.6",
|
"graphile-utils": "^4.12.2",
|
||||||
"graphql": "^14.6.0",
|
"graphql": "^15.4.0",
|
||||||
"iconv-lite": "^0.5.1",
|
"html-entities": "^2.3.2",
|
||||||
"inquirer": "^7.3.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"jsdom": "^16.3.0",
|
"inquirer": "^8.2.0",
|
||||||
"knex": "^0.21.13",
|
"inspector-api": "^1.4.2",
|
||||||
|
"jsdom": "^18.0.0",
|
||||||
|
"knex": "^0.95.12",
|
||||||
"knex-migrate": "^1.7.4",
|
"knex-migrate": "^1.7.4",
|
||||||
"longjohn": "^0.2.12",
|
"longjohn": "^0.2.12",
|
||||||
"mime": "^2.4.4",
|
"mime": "^2.4.4",
|
||||||
"mitt": "^2.1.0",
|
"mitt": "^3.0.0",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"nanoid": "^2.1.11",
|
"nanoid": "^3.1.30",
|
||||||
"object-merge-advanced": "^10.12.1",
|
"node-fetch": "^2.6.7",
|
||||||
|
"object-merge-advanced": "^12.1.0",
|
||||||
"object.omit": "^3.0.0",
|
"object.omit": "^3.0.0",
|
||||||
"opn": "^5.5.0",
|
"opn": "^6.0.0",
|
||||||
"pg": "^8.5.1",
|
"pg": "^8.5.1",
|
||||||
"postgraphile": "^4.10.0",
|
"postgraphile": "^4.10.0",
|
||||||
"postgraphile-plugin-connection-filter": "^1.1.3",
|
"postgraphile-plugin-connection-filter": "^2.2.2",
|
||||||
"promise-task-queue": "^1.2.0",
|
"promise-task-queue": "^1.2.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^16.13.0",
|
"sharp": "^0.29.2",
|
||||||
"react-dom": "^16.13.0",
|
|
||||||
"sharp": "^0.27.2",
|
|
||||||
"showdown": "^1.9.1",
|
"showdown": "^1.9.1",
|
||||||
"source-map-support": "^0.5.16",
|
"source-map-support": "^0.5.16",
|
||||||
"template-format": "^1.2.5",
|
"template-format": "^1.2.5",
|
||||||
"tippy.js": "^6.3.1",
|
"tippy.js": "^6.3.1",
|
||||||
"tough-cookie": "^3.0.1",
|
"tough-cookie": "^4.0.0",
|
||||||
"tty-table": "^2.8.12",
|
|
||||||
"tunnel": "0.0.6",
|
"tunnel": "0.0.6",
|
||||||
|
"undici": "^4.13.0",
|
||||||
"url-pattern": "^1.0.3",
|
"url-pattern": "^1.0.3",
|
||||||
"v-tooltip": "^2.0.3",
|
"v-tooltip": "^2.0.3",
|
||||||
"video.js": "^7.11.4",
|
"video.js": "^7.11.4",
|
||||||
"videojs-vr": "^1.7.1",
|
"videojs-vr": "^1.7.1",
|
||||||
"vue": "^3.0.4",
|
"vue": "^3.2.20",
|
||||||
"vue-router": "^4.0.1",
|
"vue-router": "^4.0.12",
|
||||||
"vuex": "^4.0.0-rc.2",
|
"vuex": "^4.0.2",
|
||||||
"why-is-node-running": "^2.2.0",
|
"why-is-node-running": "^2.2.0",
|
||||||
"winston": "^3.2.1",
|
"winston": "^3.2.1",
|
||||||
"winston-daily-rotate-file": "^4.4.2",
|
"winston-daily-rotate-file": "^4.4.2",
|
||||||
"yargs": "^13.3.0"
|
"yargs": "^17.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/img/banners/21naturals/21naturals_315_300.jpg
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
public/img/banners/21naturals/21naturals_315_300_1.jpg
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
public/img/banners/21naturals/21naturals_315_300_gina_gerson.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 33 KiB |
BIN
public/img/banners/21naturals/21naturals_970_90.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/img/banners/21sextreme/21sextreme_300_250_cum.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/img/banners/21sextury/21sextury_300_250_anal.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/img/banners/21sextury/21sextury_770_76_gina_gerson_dp.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 50 KiB |
BIN
public/img/banners/archangel/archangel_970_90_kendra_lust.jpg
Executable file
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 85 KiB |
BIN
public/img/banners/evilangel/evilangel_970_90_one_dollar.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
public/img/banners/julesjordan/julesjordan_728_90_adriana_chechik.jpg
Executable file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/img/banners/julesjordan/julesjordan_728_90_angela_white.jpg
Executable file
|
After Width: | Height: | Size: 83 KiB |
BIN
public/img/banners/julesjordan/julesjordan_728_90_autumn_falls.jpg
Executable file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/img/banners/julesjordan/julesjordan_728_90_gabbie_carter.jpg
Executable file
|
After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
BIN
public/img/banners/julesjordan/manuelferrara/manuelferrara_728_90_asses.jpg
Executable file
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 498 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 191 KiB |