Added basic actor page with scenes and co-star filtering.
This commit is contained in:
@@ -1,60 +1,213 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<nav class="scopes">
|
||||
<Link
|
||||
href="/updates/latest"
|
||||
class="scope nolink"
|
||||
:active="scope === 'latest'"
|
||||
>Latest</Link>
|
||||
<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 }}
|
||||
|
||||
<Link
|
||||
href="/updates/upcoming"
|
||||
class="scope nolink"
|
||||
:active="scope === 'upcoming'"
|
||||
>Upcoming</Link>
|
||||
<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>
|
||||
|
||||
<Link
|
||||
href="/updates/new"
|
||||
class="scope nolink"
|
||||
:active="scope === 'new'"
|
||||
>New</Link>
|
||||
</nav>
|
||||
<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>
|
||||
|
||||
<ul class="scenes nolist">
|
||||
<li
|
||||
v-for="scene in scenes"
|
||||
:key="scene.id"
|
||||
>
|
||||
<Scene :scene="scene" />
|
||||
</li>
|
||||
</ul>
|
||||
<div class="scenes-container">
|
||||
<div class="scenes-header">
|
||||
<div class="meta">{{ total }} results</div>
|
||||
</div>
|
||||
|
||||
<Pagination />
|
||||
<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 { inject } from 'vue';
|
||||
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';
|
||||
|
||||
const { routeParams } = inject('pageContext');
|
||||
const { scope } = routeParams;
|
||||
|
||||
defineProps({
|
||||
scenes: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
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));
|
||||
@@ -78,4 +231,39 @@ defineProps({
|
||||
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>
|
||||
|
||||
@@ -141,6 +141,7 @@ defineProps({
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
margin-right: .5rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.row {
|
||||
|
||||
Reference in New Issue
Block a user