Fixed pagination. Added entity page channel tile expand.

This commit is contained in:
2024-01-25 03:07:26 +01:00
parent 0b3f98826b
commit d739975d36
29 changed files with 1056 additions and 128 deletions

View File

@@ -0,0 +1,246 @@
<template>
<div class="page">
<div class="header">
<a
:href="entity.url"
target="_blank"
rel="noopener"
class="link link-child"
>
<template v-if="entity.hasLogo">
<img
v-if="entity.type === 'network'"
class="logo logo-child"
:src="`/logos/${entity.slug}/thumbs/network.png`"
>
<img
v-else-if="entity.parent && !entity.independent"
class="logo logo-child"
:src="`/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`"
>
<img
v-else
class="logo logo-child"
:src="`/logos/${entity.slug}/thumbs/${entity.slug}.png`"
>
</template>
<h2
v-else
class="name"
>{{ entity.name }}</h2>
</a>
<a
v-if="entity.parent"
:href="`/${entity.parent.type}/${entity.parent.slug}`"
class="link link-parent"
>
<img
v-if="entity.parent.hasLogo"
class="logo logo-parent"
:src="`/logos/${entity.parent.slug}/thumbs/network.png`"
>
<img
v-if="entity.parent.hasLogo"
class="favicon"
:src="`/logos/${entity.parent.slug}/favicon.png`"
>
<h3
v-else
class="name parent-name"
>{{ entity.parent.name }}</h3>
</a>
</div>
<div class="content">
<div class="children-container">
<ul
v-if="entity.children.length > 0"
ref="children"
class="children nolist"
:class="{ expanded }"
>
<li
v-for="channel in entity.children"
:key="`channel-${channel.id}`"
:title="channel.name"
>
<EntityTile :entity="channel" />
</li>
</ul>
<div class="expand-container">
<button
v-show="scrollable && !expanded"
class="expand"
@click="expanded = !expanded"
>
<span class="expand-text">Expand channels</span>
<Icon icon="arrow-down3" />
</button>
<button
v-show="expanded"
class="expand"
@click="expanded = !expanded"
>
<span class="expand-text">Collapse channels</span>
<Icon icon="arrow-up3" />
</button>
</div>
</div>
<Scenes />
</div>
</div>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import EntityTile from '#/components/entities/tile.vue';
import Scenes from '#/components/scenes/scenes.vue';
const { pageProps } = inject('pageContext');
const { entity } = pageProps;
const children = ref(null);
const expanded = ref(false);
const scrollable = computed(() => children.value?.scrollWidth > children.value?.clientWidth);
</script>
<style scoped>
.page {
display: flex;
flex-direction: column;
overflow-y: hidden;
}
.content {
display: flex;
flex-direction: column;
overflow-y: auto;
flex-grow: 1;
}
.header {
display: flex;
justify-content: space-between;
align-items: stretch;
color: var(--text-light);
background: var(--grey-dark-50);
}
.logo {
height: 2.5rem;
padding: .75rem 1rem;
}
.link-parent {
display: flex;
align-items: center;
}
.favicon {
display: none;
padding: .75rem 1rem;
}
.children-container {
position: relative;
}
.children {
background: var(--grey-dark-50);
display: flex;
/*
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
*/
gap: .5rem;
padding: .5rem;
overflow-x: auto;
flex-shrink: 0;
&.expanded {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
overflow-x: hidden;
.entity {
width: 100%;
}
}
}
.children::-webkit-scrollbar {
display: none;
}
.expand-container {
width: 100%;
display: flex;
justify-content: center;
position: absolute;
left: 0;
bottom: -.75rem;
z-index: 10;
}
.expand {
display: flex;
align-items: center;
padding: .5rem;
border: none;
background: var(--grey-dark-40);
color: var(--highlight-strong-30);
font-size: .9rem;
font-weight: bold;
border-radius: .25rem;
box-shadow: 0 0 3px var(--shadow);
.icon {
fill: var(--highlight-strong-30);
margin-left: 1rem;
}
&:hover {
color: var(--text-light);
cursor: pointer;
.icon {
fill: var(--text-light);
}
}
}
@media(--small-10) {
.logo-parent {
display: none;
}
.favicon {
display: inline-block;
}
}
@media(--compact) {
.logo {
height: 1.75rem;
}
.expand-text {
display: none;
}
.expand .icon {
margin-right: 1rem;
}
}
</style>

View File

@@ -0,0 +1,51 @@
import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
import { fetchEntitiesById } from '#/src/entities.js';
import { fetchScenes } from '#/src/scenes.js';
import { curateScenesQuery } from '#/src/web/scenes.js';
import redis from '#/src//redis.js';
export async function onBeforeRender(pageContext) {
const entityId = await redis.hGet('traxxx:entities:id_by_slug', pageContext.routeParams.entityType === 'network' ? `_${pageContext.routeParams.entitySlug}` : pageContext.routeParams.entitySlug);
if (!entityId) {
throw render(404, `Cannot find ${pageContext.routeParams.entityType} '${pageContext.routeParams.entitySlug}'.`);
}
const [[entity], entityScenes] = await Promise.all([
fetchEntitiesById([Number(entityId)], { includeChildren: true }),
fetchScenes(await curateScenesQuery({
...pageContext.urlQuery,
scope: pageContext.routeParams.scope || 'latest',
entityId: Number(entityId),
}), {
page: Number(pageContext.routeParams.page) || 1,
limit: Number(pageContext.urlParsed.search.limit) || 30,
aggregate: true,
}),
]);
const {
scenes,
aggActors,
aggTags,
aggChannels,
total,
limit,
} = entityScenes;
return {
pageContext: {
title: entity.name,
pageProps: {
entity,
scenes,
aggActors,
aggTags,
aggChannels,
total,
limit,
},
},
};
}

View File

@@ -0,0 +1,23 @@
import { match } from 'path-to-regexp';
// import { resolveRoute } from 'vike/routing'; // eslint-disable-line import/extensions
const path = '/:entityType(channel|network)/:entitySlug/:scope?/:page?';
const urlMatch = match(path, { decode: decodeURIComponent });
export default (pageContext) => {
const matched = urlMatch(pageContext.urlPathname);
if (matched) {
return {
routeParams: {
entityType: matched.params.entityType,
entitySlug: matched.params.entitySlug,
scope: matched.params.scope || 'latest',
page: matched.params.page || '1',
path,
},
};
}
return false;
};