Added sections and pagination to stash page.

This commit is contained in:
DebaucheryLibrarian 2021-09-12 00:05:45 +02:00
parent 8c5ef21459
commit d542889827
17 changed files with 37095 additions and 95 deletions

View File

@ -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"
@ -122,7 +122,7 @@
/> />
</span> </span>
</div> </div>
</router-link> </RouterLink>
</div> </div>
</template> </template>

View File

@ -2,22 +2,12 @@
<div class="filter-bar noselect"> <div class="filter-bar noselect">
<div class="sort"> <div class="sort">
<router-link <router-link
: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 }}</router-link>
<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>
<div class="filters"> <div class="filters">
@ -96,6 +86,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: () => [],
@ -324,6 +318,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);

View File

@ -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,29 @@
:style="{ 'background-image': getBgPath(movie.covers[0], 'lazy') }" :style="{ 'background-image': getBgPath(movie.covers[0], 'lazy') }"
loading="lazy" loading="lazy"
> >
</router-link>
<Icon
v-show="favorited"
icon="heart7"
class="stash stashed"
@click.prevent.native="unstashMovie"
/>
<Icon
v-show="favorited === false"
icon="heart8"
class="stash unstashed"
@click.prevent.native="stashMovie"
/>
</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 +45,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 +59,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 +72,36 @@
<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() {
this.favorited = false;
try {
await this.$store.dispatch('unstashMovie', {
movieId: this.movie.id,
stashId: 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;
} }
@ -72,9 +116,18 @@ export default {
default: null, default: null,
}, },
}, },
data() {
return {
favorited: this.movie.isFavorited,
};
},
computed: { computed: {
sfw, sfw,
}, },
methods: {
stashMovie,
unstashMovie,
},
}; };
</script> </script>
@ -87,6 +140,10 @@ 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 {
fill: var(--lighten);
}
} }
.movie { .movie {
@ -101,6 +158,7 @@ export default {
.cover { .cover {
height: 100%; height: 100%;
position: relative;
box-shadow: 0 0 3px var(--darken-weak); box-shadow: 0 0 3px var(--darken-weak);
img { img {
@ -179,6 +237,22 @@ export default {
} }
} }
.stash {
width: 1.25rem;
height: 1.25rem;
padding: .25rem .5rem .5rem .25rem;
position: absolute;
top: 0;
right: 0;
fill: var(--lighten-weak);
filter: drop-shadow(0 0 2px var(--darken));
&:hover,
&.stashed {
fill: var(--primary);
}
}
@media(max-width: $breakpoint-kilo) { @media(max-width: $breakpoint-kilo) {
.movie { .movie {
height: 12rem; height: 12rem;

View File

@ -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,15 +55,17 @@
</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)"
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
@ -71,6 +73,26 @@
:key="item.id" :key="item.id"
><Actor :actor="item.actor" /></li> ><Actor :actor="item.actor" /></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="fetchStash"
/>
</div>
<Pagination
:items-total="totalCount"
:items-per-page="limit"
class="pagination-bottom"
/>
<Footer />
</div> </div>
</div> </div>
</template> </template>
@ -78,12 +100,37 @@
<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,
});
console.log(this.stash.movies);
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 +156,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,21 +255,26 @@ 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));
flex-grow: 1; flex-grow: 1;
padding: 1rem; padding: 1rem;
border-top: solid 1px var(--shadow-hint);
}
.stash-movies {
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);
} }
.stash-scenes { .stash-scenes {
flex-grow: 0;
.tiles { .tiles {
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr)); grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
} }

View File

@ -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>

View File

@ -136,20 +136,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;
} }

View File

@ -145,6 +145,24 @@ const movieFields = `
} }
} }
} }
isFavorited
isStashed(includeFavorites: false)
stashes: stashesMovies(
filter: {
stash: {
userId: {
equalTo: $userId
}
}
}
) @include(if: $hasAuth) {
stash {
id
name
slug
primary
}
}
`; `;
const campaignsFragment = ` const campaignsFragment = `

View File

@ -72,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
@ -90,6 +92,8 @@ function initReleasesActions(store, router) {
} }
} }
`, { `, {
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,
}); });

View File

@ -228,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',
}, },

View File

@ -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
scene { first: $limit
${releaseFields} offset: $offset
) @include(if: $includeScenes) {
totalCount
scenes: nodes {
comment
scene {
${releaseFields}
}
}
}
moviesConnection: stashesMoviesConnection(
orderBy: CREATED_AT_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,
}); });

View File

@ -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}

36768
docs/names.md Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -842,6 +842,7 @@ const tagMedia = [
['enhanced-boobs', 'september_reign_spizoo', 'September Rain in "September Reign Loves Jessica"', 'spizoo'], ['enhanced-boobs', 'september_reign_spizoo', 'September Rain in "September Reign Loves Jessica"', 'spizoo'],
['enhanced-boobs', 'katrina_moreno_bangbros', 'Katrina Moreno in "Stripper Cream Pie"', 'bangbros'], ['enhanced-boobs', 'katrina_moreno_bangbros', 'Katrina Moreno in "Stripper Cream Pie"', 'bangbros'],
['enhanced-boobs', 'sadie_santana_newsensations', 'Sadie Santana in "Backdoor Beauties"', 'newsensations'], ['enhanced-boobs', 'sadie_santana_newsensations', 'Sadie Santana in "Backdoor Beauties"', 'newsensations'],
['enhanced-boobs', 'kiera_king_puremature', 'Kiera King in "Warming Up"', 'puremature'],
['enhanced-boobs', 'diana_prince_penthouse_2', 'Diana Prince in "It Is What It Seems"', 'penthouse'], ['enhanced-boobs', 'diana_prince_penthouse_2', 'Diana Prince in "It Is What It Seems"', 'penthouse'],
['enhanced-boobs', 'chessie_kay_chelsey_lanette_eurogirlsongirls', 'Chelsey Lanette and Chessie Kay', 'eurogirlsongirls'], ['enhanced-boobs', 'chessie_kay_chelsey_lanette_eurogirlsongirls', 'Chelsey Lanette and Chessie Kay', 'eurogirlsongirls'],
['enhanced-boobs', 'chelsey_lanette_sexart_1', 'Chelsey Lanette in "Tell Me How You Want"', 'sexart'], ['enhanced-boobs', 'chelsey_lanette_sexart_1', 'Chelsey Lanette in "Tell Me How You Want"', 'sexart'],

View File

@ -2,33 +2,46 @@
const { makeExtendSchemaPlugin, gql } = require('graphile-utils'); const { makeExtendSchemaPlugin, gql } = require('graphile-utils');
function isFavorited(parent) {
if (!parent['@stashes'] || (parent['@stashes'].length > 0 && typeof parent['@stashes'][0]['@stash'].primary === 'undefined')) {
return null;
}
return parent['@stashes'].some(({ '@stash': stash }) => stash.primary);
}
function isStashed(parent, args) {
if (!parent['@stashes'] || (parent['@stashes'].length > 0 && typeof parent['@stashes'][0]['@stash'].primary === 'undefined')) {
return null;
}
if (args.includeFavorites) {
return parent['@stashes'].length > 0;
}
return parent['@stashes'].some(({ '@stash': stash }) => !stash.primary);
}
const schemaExtender = makeExtendSchemaPlugin(_build => ({ const schemaExtender = makeExtendSchemaPlugin(_build => ({
typeDefs: gql` typeDefs: gql`
extend type Release { extend type Release {
isFavorited: Boolean @requires(columns: ["stashesScenesBySceneId"]) isFavorited: Boolean @requires(columns: ["stashesScenesBySceneId"])
isStashed(includeFavorites: Boolean = false): Boolean @requires(columns: ["stashesScenesBySceneId"]) isStashed(includeFavorites: Boolean = false): Boolean @requires(columns: ["stashesScenesBySceneId"])
} }
extend type Movie {
isFavorited: Boolean @requires(columns: ["stashesMovies"])
isStashed(includeFavorites: Boolean = false): Boolean @requires(columns: ["stashesMovies"])
}
`, `,
resolvers: { resolvers: {
Release: { Release: {
isFavorited(parent) { isFavorited,
if (!parent['@stashes'] || (parent['@stashes'].length > 0 && typeof parent['@stashes'][0]['@stash'].primary === 'undefined')) { isStashed,
return null; },
} Movie: {
isFavorited,
return parent['@stashes'].some(({ '@stash': stash }) => stash.primary); isStashed,
},
isStashed(parent, args) {
if (!parent['@stashes'] || (parent['@stashes'].length > 0 && typeof parent['@stashes'][0]['@stash'].primary === 'undefined')) {
return null;
}
if (args.includeFavorites) {
return parent['@stashes'].length > 0;
}
return parent['@stashes'].some(({ '@stash': stash }) => !stash.primary);
},
}, },
}, },
})); }));