Added generic entity page.

This commit is contained in:
ThePendulum 2020-06-27 04:50:13 +02:00
parent af56378ee2
commit 0e8b4caac3
19 changed files with 668 additions and 44 deletions

View File

@ -84,7 +84,6 @@ export default {
.content-inner {
flex-grow: 1;
padding: 1rem;
border-top: solid 1px var(--crease);
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,185 @@
<template>
<div
v-if="entity"
class="entity content"
>
<div class="info">
<img
v-if="$route.name === 'network'"
class="logo"
:src="`/img/logos/${entity.slug}/thumbs/network.png`"
>
<img
v-else
class="logo"
:src="`/img/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`"
>
<router-link
v-if="entity.parent"
:to="{ name: `${entity.parent.type}Base`, params: { entitySlug: entity.parent.slug } }"
>
<img
class="logo"
:src="`/img/logos/${entity.parent.slug}/thumbs/network.png`"
>
</router-link>
</div>
<div class="content-inner">
<div
v-if="entity.children.length > 0"
class="children"
:class="{ expanded }"
>
<EntityTile
v-for="child in entity.children"
:key="`child-${child.id}`"
:entity="child"
/>
</div>
<div
v-if="entity.children.length > 1"
class="expand noselect"
@click="expanded = !expanded"
>
<Icon
v-show="expanded"
icon="arrow-up3"
/>
<Icon
v-show="!expanded"
icon="arrow-down3"
/>
</div>
<FilterBar
:fetch-releases="fetchEntity"
:items-total="totalCount"
:items-per-page="limit"
/>
<div class="releases">
<Releases :releases="entity.releases" />
</div>
</div>
</div>
</template>
<script>
import EntityTile from './tile.vue';
import FilterBar from '../header/filter-bar.vue';
import Releases from '../releases/releases.vue';
async function fetchEntity() {
const { entity, totalCount } = await this.$store.dispatch('fetchEntityBySlugAndType', {
entitySlug: this.$route.params.entitySlug,
entityType: this.$route.name,
limit: this.limit,
range: this.$route.params.range,
pageNumber: Number(this.$route.params.pageNumber),
});
this.entity = entity;
this.totalCount = totalCount;
this.pageTitle = entity.name;
}
async function mounted() {
await this.fetchEntity();
}
async function route() {
this.expanded = false;
await this.fetchEntity();
}
export default {
components: {
EntityTile,
FilterBar,
Releases,
},
data() {
return {
entity: null,
totalCount: null,
limit: 10,
expanded: false,
};
},
watch: {
$route: route,
},
mounted,
methods: {
fetchEntity,
},
};
</script>
<style lang="scss" scoped>
.info {
height: 2.5rem;
display: flex;
justify-content: space-between;
padding: 1rem;
background: var(--profile);
border-bottom: solid 1px var(--lighten-hint);
}
.logo {
height: 100%;
max-width: 20rem;
object-fit: contain;
}
.children {
display: flex;
box-sizing: border-box;
overflow-x: auto;
padding: 1rem;
border-bottom: solid 1px var(--darken-hint);
background: var(--profile);
scrollbar-width: none;
.tile {
width: 15rem;
margin: 0 1rem 0 0;
}
&.expanded {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
grid-gap: 1rem;
.tile {
width: 100%;
}
}
&::-webkit-scrollbar {
display: none;
}
}
.expand {
background: var(--profile);
.icon {
fill: var(--lighten-weak);
}
&:hover {
background: var(--darken-strong);
.icon {
fill: var(--lighten-weak);
}
}
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<router-link
:to="{ name: entity.type, params: { entitySlug: entity.slug } }"
:title="entity.name"
class="tile"
>
<img
v-if="entity.type === 'channel'"
:src="`/img/logos/${entity.parent.slug}/thumbs/${entity.slug}.png`"
:alt="entity.name"
class="logo"
>
<img
v-else
:src="`/img/logos/${entity.slug}/thumbs/network.png`"
:alt="entity.name"
class="logo"
>
</router-link>
</template>
<script>
export default {
props: {
entity: {
type: Object,
default: null,
},
},
};
</script>
<style lang="scss" scoped>
@import 'theme';
.tile {
height: 6rem;
background: $tile;
display: flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
box-sizing: border-box;
padding: .5rem 1rem;
border-radius: .25rem;
box-shadow: 0 0 3px rgba(0, 0, 0, .25);
text-align: center;
}
.link {
text-decoration: none;
}
.logo {
max-width: 100%;
max-height: 100%;
color: $text-contrast;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
font-weight: bold;
filter: $logo-highlight;
}
.title {
color: $text;
height: 100%;
display: flex;
align-items: center;
margin: 0;
}
</style>

View File

@ -30,7 +30,7 @@
</router-link>
<router-link
:to="{ params: { tags: selectedTags.length === 1 && selectedTags.includes(tag) ? 'all-tags' : tag } }"
:to="{ params: { tags: selectedTags.length === 1 && selectedTags.includes(tag) ? 'all' : tag } }"
class="name"
>{{ tag }}</router-link>
</li>
@ -56,7 +56,7 @@ const tags = [
function getNewRange(tag) {
if (this.selectedTags.includes(tag)) {
if (this.selectedTags.length === 1) {
return 'all-tags';
return 'all';
}
return this.selectedTags.filter(selectedTag => selectedTag !== tag).join('+');
@ -66,7 +66,7 @@ function getNewRange(tag) {
}
function selectedTags() {
return this.$route.params.tags.split('+').filter(selectedTag => selectedTag !== 'all-tags');
return this.$route.params.tags.split('+').filter(selectedTag => selectedTag !== 'all');
}
export default {

View File

@ -43,7 +43,7 @@
:href="href"
:class="{ active: isActive }"
@click="navigate"
>Sites</a>
>Channels</a>
</router-link>
</li>

View File

@ -1,5 +1,6 @@
<template>
<div class="content">
<div class="content-inner">
<FilterBar
:fetch-releases="fetchReleases"
:is-home="true"
@ -7,7 +8,6 @@
:items-per-page="limit"
/>
<div class="content-inner">
<Releases :releases="releases" />
<Pagination

View File

@ -87,9 +87,13 @@ export default {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
grid-gap: 1rem;
box-sizing: border-box;
padding: 1rem;
border-top: solid 1px var(--crease);
}
.empty {
padding: 1rem;
color: var(--shadow-strong);
font-weight: bold;
}

View File

@ -0,0 +1,361 @@
<template>
<div
:id="`${release.type}-${release.id}`"
:class="{ [release.type]: true }"
class="tile"
>
<span class="poster">
<span class="details">
<router-link
v-if="release.site && release.site.independent"
:to="`/network/${release.network.slug}`"
class="site site-link"
><img
:src="`/img/logos/${release.network.slug}/favicon.png`"
class="favicon"
>{{ release.network.name }}</router-link>
<span
v-else-if="release.network"
class="site"
>
<router-link
v-tooltip.bottom="`Part of ${release.network.name}`"
:title="`Part of ${release.network.name}`"
:to="`/network/${release.network.slug}`"
class="site-link"
><img
:src="`/img/logos/${release.network.slug}/favicon.png`"
class="favicon"
></router-link>
<router-link
v-tooltip.bottom="`More from ${release.site.name}`"
:title="`More from ${release.site.name}`"
:to="`/site/${release.site.slug}`"
class="site-link"
>{{ release.site.name }}</router-link>
</span>
<span v-else />
<a
v-if="release.date"
v-tooltip.bottom="release.url && `View scene on ${release.site.name}`"
:title="release.url && `View scene on ${release.site.name}`"
:href="release.url"
:class="{ upcoming: isAfter(release.date, new Date()), new: release.isNew }"
target="_blank"
rel="noopener noreferrer"
class="date"
>{{ formatDate(release.date, 'MMM D, YYYY') }}</a>
<a
v-else
:href="release.url"
:class="{ upcoming: isAfter(release.date, new Date()), new: release.isNew }"
title="Scene date N/A, showing date added"
target="_blank"
rel="noopener noreferrer"
class="date"
>{{ `(${formatDate(release.dateAdded, 'MMM D, YYYY')})` }}</a>
</span>
<a
:href="`/${release.type || 'scene'}/${release.id}/${release.slug}`"
target="_blank"
rel="noopener noreferrer"
class="link"
>
<img
v-if="release.poster"
:data-src="sfw ? `/img/${release.poster.sfw.thumbnail}` : `/media/${release.poster.thumbnail}`"
:data-loading="sfw ? `/img/${release.poster.sfw.lazy}` : `/media/${release.poster.lazy}`"
:alt="release.title"
class="thumbnail"
>
<span
v-else-if="release.covers && release.covers.length > 0"
class="covers"
>
<img
v-for="cover in release.covers"
:key="cover.id"
:data-src="sfw ? `/img/${cover.sfw.thumbnail}` : `/media/${cover.thumbnail}`"
:data-loading="sfw ? `/img/${cover.sfw.lazy}` : `/media/${cover.lazy}`"
:alt="release.title"
class="thumbnail cover"
>
</span>
<div
v-else
:title="release.title"
class="thumbnail"
>No thumbnail available</div>
</a>
</span>
<div class="info">
<a
:href="`/${release.type || 'scene'}/${release.id}/${release.slug}`"
target="_blank"
rel="noopener noreferrer"
class="row link"
>
<h3
v-tooltip.top="release.title"
:title="release.title"
class="title"
>
<Icon
v-if="release.type === 'movie'"
icon="film"
/>{{ release.title }}
</h3>
</a>
<span class="row">
<ul class="actors nolist">
<li
v-for="actor in release.actors"
:key="actor.id"
class="actor"
>
<router-link
:to="{ name: 'actor', params: { actorId: actor.id, actorSlug: actor.slug } }"
class="actor-link"
>{{ actor.name }}</router-link>
</li>
</ul>
</span>
<ul
v-if="release.tags.length > 0"
:title="release.tags.map(tag => tag.name).join(', ')"
class="tags nolist"
>
<li
v-for="tag in release.tags"
:key="`tag-${tag.slug}`"
class="tag"
>
<router-link
:to="`/tag/${tag.slug}`"
class="tag-link"
>{{ tag.name }}</router-link>
</li>
</ul>
</div>
</div>
</template>
<script>
function sfw() {
return this.$store.state.ui.sfw;
}
export default {
props: {
release: {
type: Object,
default: null,
},
referer: {
type: String,
default: null,
},
},
computed: {
sfw,
},
};
</script>
<style lang="scss" scoped>
@import 'theme';
.tile {
background: var(--background);
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 0 0 .5rem 0;
overflow: hidden;
box-shadow: 0 0 3px var(--darken-weak);
height: 100%;
}
.poster {
position: relative;
margin: 0 0 .5rem 0;
}
.covers {
background: var(--profile);
display: flex;
.cover {
width: 50%;
}
}
.thumbnail {
width: 100%;
height: 14rem;
display: flex;
justify-content: center;
align-items: center;
object-fit: cover;
background-position: center;
background-size: cover;
background-color: var(--shadow-hint);
color: var(--shadow);
text-shadow: 1px 1px 0 var(--highlight);
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
padding: 0 .5rem;
margin: 0 0 .25rem 0;
}
.details {
width: 100%;
display: flex;
justify-content: space-between;
position: absolute;
font-size: 0;
.favicon {
height: 1rem;
margin: 0 .25rem 0 0;
}
}
.site,
.date {
color: var(--text-light);
display: flex;
align-items: center;
background: var(--darken);
position: relative;
font-size: .8rem;
padding: .25rem;
text-decoration: none;
}
.date {
&.upcoming:before {
content: '';
background: var(--primary);
width: .5rem;
display: inline-block;
position: absolute;
top: 0;
bottom: 0;
left: -.5rem;
}
&.new {
font-weight: bold;
}
}
.site {
font-weight: bold;
}
.site-link {
display: flex;
color: var(--text-light);
text-decoration: none;
}
.info {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.link {
text-decoration: none;
}
.title {
margin: 0 .25rem .25rem 0;
color: var(--text);
max-height: 2.75rem;
font-size: 1rem;
line-height: 1.5;
text-overflow: ellipsis;
overflow: hidden;
.icon {
margin: 0 .25rem 0 0;
}
}
.network {
color: #555;
margin: 0 .25rem 0 0;
font-size: .8rem;
}
.actors {
word-wrap: break-word;
overflow: hidden;
max-height: 2.75rem;
line-height: 1.5rem;
}
.tags {
max-height: .5rem;
padding: .25rem .5rem 1rem .5rem;
word-wrap: break-word;
overflow-y: hidden;
}
.actor {
margin: 0 .25rem 0 0;
}
.tag {
margin: 0 .25rem .25rem 0;
}
.actor:not(:last-of-type)::after {
content: ",";
}
.actor-link {
text-decoration: none;
&:hover {
color: var(--primary);
}
}
.actor-link {
color: var(--link);
}
.tag-link {
color: var(--shadow);
display: inline-block;
padding: .25rem;
font-size: .75rem;
font-weight: bold;
text-decoration: none;
line-height: 1;
border: solid 1px var(--shadow-hint);
&:hover {
color: var(--primary);
}
}
</style>

View File

@ -51,7 +51,7 @@ $female: #f0a;
--lighten-strong: rgba(255, 255, 255, .7);
--lighten-extreme: rgba(255, 255, 255, .9);
--lighten-weak: rgba(255, 255, 255, .2);
--lighten-hint: rgba(255, 255, 255, .1);
--lighten-hint: rgba(255, 255, 255, .05);
--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);

View File

@ -4,7 +4,7 @@ import VueRouter from 'vue-router';
import Home from '../components/home/home.vue';
import Release from '../components/releases/release.vue';
import Site from '../components/sites/site.vue';
import Network from '../components/networks/network.vue';
import Entity from '../components/entities/entity.vue';
import Networks from '../components/networks/networks.vue';
import Actor from '../components/actors/actor.vue';
import Actors from '../components/actors/actors.vue';
@ -22,7 +22,7 @@ const routes = [
name: 'updates',
params: {
range: 'latest',
tags: 'all-tags',
tags: 'all',
pageNumber: 1,
},
},
@ -33,7 +33,7 @@ const routes = [
name: 'updates',
params: {
range: 'latest',
tags: 'all-tags',
tags: 'all',
pageNumber: 1,
},
},
@ -61,7 +61,7 @@ const routes = [
params: {
...from.params,
range: 'latest',
tags: 'all-tags',
tags: 'all',
pageNumber: 1,
},
}),
@ -72,42 +72,41 @@ const routes = [
name: 'actorRange',
},
{
path: '/site/:siteSlug',
path: '/channel/:entitySlug',
component: Site,
name: 'site',
name: 'channelBase',
redirect: from => ({
name: 'siteRange',
name: 'channel',
params: {
...from.params,
range: 'latest',
tags: 'all-tags',
tags: 'all',
pageNumber: 1,
},
}),
},
{
path: '/site/:siteSlug/:tags/:range/:pageNumber',
component: Site,
name: 'siteRange',
path: '/channel/:entitySlug/:tags/:range/:pageNumber',
component: Entity,
name: 'channel',
},
{
path: '/network/:networkSlug',
component: Network,
path: '/network/:entitySlug',
name: 'networkBase',
redirect: from => ({
name: 'network',
redirect: from => ({
name: 'networkRange',
params: {
...from.params,
range: 'latest',
tags: 'all-tags',
tags: 'all',
pageNumber: 1,
},
}),
},
{
path: '/network/:networkSlug/:tags/:range/:pageNumber',
component: Network,
name: 'networkRange',
path: '/network/:entitySlug/:tags/:range/:pageNumber',
component: Entity,
name: 'network',
},
{
path: '/tag/:tagSlug',
@ -116,7 +115,7 @@ const routes = [
params: {
...from.params,
range: 'latest',
tags: 'all-tags',
tags: 'all',
},
}),
},
@ -133,7 +132,7 @@ const routes = [
...from.params,
gender: 'female',
letter: 'all',
tags: 'all-tags',
tags: 'all',
range: 'latest',
pageNumber: 1,
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -120,6 +120,7 @@ const networks = [
name: 'Brazzers',
url: 'https://www.brazzers.com',
description: 'Brazzers homepage is updated daily with official HD porn scenes. Our hottest videos and sex series are filled with big tits, sexy milf, top pornstars and special events.',
parent: 'mindgeek',
},
{
slug: 'boobpedia',

View File

@ -3089,6 +3089,7 @@ const sites = [
url: 'https://www.pornhub.com',
description: '',
network: 'mindgeek',
type: 'info',
},
{
slug: 'tube8vip',