traxxx-web/components/scenes/scenes.vue

270 lines
5.2 KiB
Vue
Raw Normal View History

<template>
<div class="page">
<Filters v-if="showFilters">
<ul class="filter-actors selected nolist">
<li
v-for="aggActor in selectedActors"
:key="`filter-actor-${aggActor.id}`"
class="filter-actor"
@click="updateFilter('actors', [...filters.actors, aggActor.id])"
>
{{ aggActor.name }}
<button
v-if="filters.actors.includes(aggActor.id)"
class="filter-actor-remove"
@click.stop="updateFilter('actors', filters.actors.filter((actorId) => actorId !== aggActor.id))"
><Icon icon="cross2" /></button>
</li>
</ul>
<ul class="filter-actors available nolist">
<li
v-for="aggActor in actors"
:key="`filter-actor-${aggActor.id}`"
class="filter-actor"
@click="updateFilter('actors', [...filters.actors, aggActor.id])"
>{{ aggActor.name }}</li>
</ul>
</Filters>
<div class="scenes-container">
<div class="scenes-header">
<div class="meta">{{ total }} results</div>
</div>
<nav class="scopes">
<Link
:href="getPath('latest')"
class="scope nolink"
:active="scope === 'latest'"
>Latest</Link>
<Link
:href="getPath('upcoming')"
class="scope nolink"
:active="scope === 'upcoming'"
>Upcoming</Link>
<Link
:href="getPath('new')"
class="scope nolink"
:active="scope === 'new'"
>New</Link>
</nav>
<ul class="scenes nolist">
<li
v-for="scene in scenes"
:key="scene.id"
>
<Scene :scene="scene" />
</li>
</ul>
<Pagination />
</div>
</div>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import { parse } from 'path-to-regexp';
import navigate from '#/src/navigate.js';
import { get } from '#/src/api.js';
import events from '#/src/events.js';
import Filters from '#/components/filters/filters.vue';
import Scene from './tile.vue';
import Pagination from '../pagination/pagination.vue';
defineProps({
showFilters: {
type: Boolean,
default: true,
},
});
const { pageProps, routeParams, urlParsed } = inject('pageContext');
const { scope } = routeParams;
console.log(scope);
const {
actor,
} = pageProps;
const scenes = ref(pageProps.scenes);
const actors = ref(pageProps.actors);
const currentPage = ref(Number(routeParams.page));
const total = ref(Number(pageProps.total));
const filters = ref({
actors: urlParsed.search.actors?.split(',').filter(Boolean).map((actorId) => Number(actorId)) || [],
});
/*
const sortedActors = computed(() => Array.from(actors.value).sort((actorA, actorB) => {
if (filters.value.actors.includes(actorA.id) && !filters.value.actors.includes(actorB.id)) {
return -1;
}
if (filters.value.actors.includes(actorB.id) && !filters.value.actors.includes(actorA.id)) {
return 1;
}
return 0;
}));
*/
const selectedActors = computed(() => actors.value.filter((filterActor) => filters.value.actors.includes(filterActor.id)));
console.log(filters.value.actors);
function getPath(targetScope, preserveQuery) {
const path = parse(routeParams.path)
.map((segment) => {
if (segment.name === 'scope') {
return targetScope;
}
if (segment.name === 'page') {
return 1;
}
return routeParams[segment.name] || segment;
})
.join('/');
if (preserveQuery && urlParsed.searchOriginal) {
return `${path}${urlParsed.searchOriginal}`;
}
return path;
}
async function search(resetPage = true) {
if (resetPage) {
currentPage.value = 1;
}
const query = {};
console.log(query);
const res = await get('/scenes', {
...query,
actors: [actor?.id, filters.value.actors].filter(Boolean).join(','), // if we're on an actor page, actor ID needs to be included
scope,
page: currentPage.value, // client uses param rather than query pagination
});
scenes.value = res.scenes;
actors.value = res.actors;
total.value = res.total;
console.log(scenes.value);
events.emit('scrollUp');
navigate(getPath(scope, false), {
...query,
actors: filters.value.actors.join(',') || undefined, // don't include actor page ID in query, already a parameter
}, { redirect: false });
}
function updateFilter(prop, value, reload = true) {
filters.value[prop] = value;
if (reload) {
search();
}
}
</script>
<style scoped>
.page {
display: flex;
background: var(--background-base-10);
}
.scenes-header {
display: flex;
align-items: center;
padding: .5rem 0 .25rem 2rem;
}
.scenes-container {
display: flex;
flex-direction: column;
}
.meta {
display: flex;
flex-grow: 1;
justify-content: space-between;
align-items: center;
}
.scenes {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
gap: .75rem .5rem;
padding: 1rem;
}
.scopes {
margin-left: 1rem;
}
.scope {
box-sizing: border-box;
padding: 1rem;
color: var(--shadow);
font-size: .9rem;
font-weight: bold;
&.active {
color: var(--primary);
font-weight: bold;
}
}
.filter-actors {
max-height: 15rem;
&.available {
overflow-y: auto;
}
}
.filter-actor {
display: flex;
justify-content: space-between;
&:hover {
color: var(--primary);
cursor: pointer;
}
}
.filter-actor-remove {
background: none;
border: none;
.icon {
fill: var(--shadow);
}
&:hover {
cursor: pointer;
.icon {
fill: var(--error);
}
}
}
</style>