forked from DebaucheryLibrarian/traxxx
791 lines
16 KiB
Vue
791 lines
16 KiB
Vue
<template>
|
|
<div
|
|
v-if="actor"
|
|
class="content actor"
|
|
>
|
|
<div class="actor-header">
|
|
<h2 class="header-name">
|
|
<span v-if="actor.entity">{{ actor.name }} ({{ actor.entity.name }})</span>
|
|
<span v-else="">{{ actor.name }}</span>
|
|
|
|
<Gender
|
|
:gender="actor.gender"
|
|
class="header-gender"
|
|
/>
|
|
</h2>
|
|
|
|
<li
|
|
v-if="actor.aliases.length"
|
|
class="bio-item"
|
|
>
|
|
<dfn class="bio-label">Also known as</dfn>
|
|
<span>{{ actor.aliases.join(', ') }}</span>
|
|
</li>
|
|
|
|
<Social
|
|
v-if="actor.social && actor.social.length > 0"
|
|
:actor="actor"
|
|
class="header-social"
|
|
/>
|
|
</div>
|
|
|
|
<div class="content-inner actor-inner">
|
|
<div
|
|
class="profile"
|
|
:class="{ expanded: bioExpanded, 'with-avatar': !!actor.avatar }"
|
|
>
|
|
<a
|
|
v-if="actor.avatar"
|
|
:href="`/media/${actor.avatar.path}`"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="avatar-link"
|
|
>
|
|
<img
|
|
:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`"
|
|
:title="actor.avatar.credit && `© ${actor.avatar.credit}`"
|
|
class="avatar"
|
|
>
|
|
</a>
|
|
|
|
<Expand
|
|
v-if="bioExpanded"
|
|
:expanded="bioExpanded"
|
|
class="expand expand-light"
|
|
@expand="(state) => bioExpanded = state"
|
|
/>
|
|
|
|
<ul class="bio nolist">
|
|
<li
|
|
v-if="actor.realName"
|
|
class="bio-item"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="vcard" />Real name</dfn>
|
|
<span class="bio-value">{{ actor.realName }}</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.dateOfBirth"
|
|
class="bio-item"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="cake" />Date of birth</dfn>
|
|
|
|
<span class="birthdate">{{ formatDate(actor.dateOfBirth, 'MMMM D, YYYY') }}<span
|
|
v-if="!actor.dateOfDeath"
|
|
class="age"
|
|
>{{ actor.ageFromBirth }}</span></span>
|
|
</li>
|
|
|
|
<li
|
|
v-else-if="actor.age && !actor.dateOfDeath"
|
|
class="bio-item"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="cake" />Age</dfn>
|
|
|
|
<span
|
|
v-tooltip="'Exact date of birth or age unknown'"
|
|
class="birthdate"
|
|
>> {{ actor.age }}</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.dateOfDeath"
|
|
class="bio-item"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="tombstone" />Date of death</dfn>
|
|
|
|
<span class="birthdate">{{ formatDate(actor.dateOfDeath, 'MMMM D, YYYY') }}<span
|
|
v-if="actor.ageAtDeath"
|
|
class="age"
|
|
>{{ actor.ageAtDeath }}</span></span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.origin"
|
|
class="bio-item birth"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="home2" />Born in</dfn>
|
|
|
|
<span>
|
|
<span
|
|
v-if="actor.origin.city"
|
|
class="city"
|
|
>{{ actor.origin.city }}</span><span
|
|
v-if="actor.origin.state && (!actor.origin.city || (actor.origin.country && actor.origin.country.alpha2 === 'US'))"
|
|
class="state"
|
|
>{{ actor.origin.city ? `, ${actor.origin.state}` : actor.origin.state }}</span>
|
|
|
|
<span
|
|
v-if="actor.origin.country"
|
|
class="country birthcountry"
|
|
>
|
|
<img
|
|
class="flag"
|
|
:src="`/img/flags/${actor.origin.country.alpha2.toLowerCase()}.svg`"
|
|
>{{ actor.origin.country.alias || actor.origin.country.name }}
|
|
</span>
|
|
</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.residence"
|
|
class="bio-item residence hideable"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="location" />Lives in</dfn>
|
|
|
|
<span>
|
|
<span
|
|
v-if="actor.residence.city"
|
|
class="city"
|
|
>{{ actor.residence.city }}</span><span
|
|
v-if="actor.residence.state && actor.residence.country && actor.residence.country.alpha2 === 'US'"
|
|
class="state"
|
|
>{{ actor.residence.city ? `, ${actor.residence.state}` : actor.residence.state }}</span>
|
|
|
|
<span
|
|
v-if="actor.residence.country"
|
|
class="country"
|
|
>
|
|
<img
|
|
class="flag"
|
|
:src="`/img/flags/${actor.residence.country.alpha2.toLowerCase()}.svg`"
|
|
>{{ actor.residence.country.alias || actor.residence.country.name }}
|
|
</span>
|
|
</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.ethnicity"
|
|
class="bio-item ethnicity hideable"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="earth2" />Ethnicity</dfn>
|
|
<span>{{ actor.ethnicity }}</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.bust || actor.waist || actor.hip"
|
|
title="bust-waist-hip"
|
|
class="bio-item figure"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="ruler" />Figure</dfn>
|
|
<span class="bio-value">
|
|
<Icon
|
|
v-if="actor.naturalBoobs === false"
|
|
v-tooltip="'Enhanced boobs'"
|
|
icon="magic-wand2"
|
|
class="enhanced"
|
|
/>{{ actor.bust || '??' }}{{ actor.cup || '?' }}-{{ actor.waist || '??' }}-{{ actor.hip || '??' }}
|
|
</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.penisLength || actor.penisGirth || actor.circumcised"
|
|
class="bio-item penis"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="pencil-ruler" />Dick</dfn>
|
|
|
|
<span>
|
|
<Icon
|
|
v-if="actor.circumcised"
|
|
v-tooltip="'Circumcised'"
|
|
icon="page-break"
|
|
class="circumcised"
|
|
/>
|
|
|
|
<template v-if="actor.penisLengthMetric && actor.penisGirthMetric">
|
|
<span>{{ actor.penisLengthMetric }} * {{ actor.penisGirthMetric }} cm</span>
|
|
<span class="bio-segment">{{ actor.penisLengthImperial }}" * {{ actor.penisGirthImperial }}"</span>
|
|
</template>
|
|
|
|
<template v-else-if="actor.penisLengthMetric">
|
|
<span>{{ actor.penisLengthMetric }} cm</span>
|
|
<span class="bio-segment">{{ actor.penisLengthImperial }}"</span>
|
|
</template>
|
|
</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.height"
|
|
class="bio-item height"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="height" />Height</dfn>
|
|
<span>
|
|
<span class="height-metric">{{ actor.height.metric }} cm</span>
|
|
<span class="height-imperial">{{ actor.height.imperial }}</span>
|
|
</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.weight"
|
|
class="bio-item weight hideable"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="scale" />Weight</dfn>
|
|
|
|
<span>
|
|
<span class="weight-metric">{{ actor.weight.metric }} kg</span>
|
|
<span class="weight-imperial">{{ actor.weight.imperial }} lbs</span>
|
|
</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.eyes"
|
|
class="bio-item eyes hideable"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="eye" />Eyes</dfn>
|
|
<span>{{ actor.eyes }}</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.hairColor"
|
|
class="bio-item hair hideable"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="haircut" />Hair</dfn>
|
|
<span><span v-if="actor.hairLength">{{ actor.hairLength }}, </span>{{ actor.hairColor }}</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.hasTattoos"
|
|
class="bio-item tattoos hideable"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="lotus" />Tattoos</dfn>
|
|
|
|
<span
|
|
v-if="actor.tattoos"
|
|
v-tooltip="actor.tattoos"
|
|
class="bio-value"
|
|
>{{ actor.tattoos }}</span>
|
|
<span v-else>Yes</span>
|
|
</li>
|
|
|
|
<li
|
|
v-if="actor.hasPiercings"
|
|
class="bio-item piercings hideable"
|
|
>
|
|
<dfn class="bio-label"><Icon icon="trophy4" />Piercings</dfn>
|
|
|
|
<span
|
|
v-if="actor.piercings"
|
|
v-tooltip="actor.piercings"
|
|
class="bio-value"
|
|
>{{ actor.piercings }}</span>
|
|
<span v-else>Yes</span>
|
|
</li>
|
|
|
|
<li class="bio-item scraped hideable">Updated {{ formatDate(actor.updatedAt, 'YYYY-MM-DD HH:mm') }}, ID: {{ actor.id }}</li>
|
|
</ul>
|
|
|
|
<div class="descriptions-container">
|
|
<div
|
|
v-if="actor.descriptions && actor.descriptions.length > 0"
|
|
class="descriptions"
|
|
>
|
|
<p
|
|
v-for="description in actor.descriptions"
|
|
:key="`description-${description.entity.id}`"
|
|
class="description"
|
|
>
|
|
{{ description.text }}
|
|
<router-link :to="`/${description.entity.type}/${description.entity.slug}`">
|
|
<img
|
|
v-if="description.entity.type === 'network' || !description.entity.parent || description.entity.independent"
|
|
:src="`/img/logos/${description.entity.slug}/thumbs/network.png`"
|
|
class="description-logo"
|
|
>
|
|
|
|
<img
|
|
v-else
|
|
:src="`/img/logos/${description.entity.parent.slug}/thumbs/${description.entity.slug}.png`"
|
|
class="description-logo"
|
|
>
|
|
</router-link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Social
|
|
v-if="actor.social && actor.social.length > 0"
|
|
:actor="actor"
|
|
class="profile-social"
|
|
/>
|
|
|
|
<Expand
|
|
:expanded="bioExpanded"
|
|
class="expand expand-light"
|
|
@expand="(state) => bioExpanded = state"
|
|
/>
|
|
</div>
|
|
|
|
<Album
|
|
v-if="showAlbum"
|
|
:items="actor.photos"
|
|
:title="actor.name"
|
|
:portrait="true"
|
|
@close="$router.go(-1)"
|
|
/>
|
|
|
|
<div class="actor-content">
|
|
<Scroll
|
|
v-if="actor.avatar || (actor.photos && actor.photos.length > 0)"
|
|
v-slot="scroll"
|
|
>
|
|
<Photos
|
|
:actor="actor"
|
|
:class="{ expanded: photosExpanded }"
|
|
@load="scroll.loaded"
|
|
/>
|
|
</Scroll>
|
|
|
|
<button
|
|
v-if="actor.photos && actor.photos.length > 2"
|
|
class="album-toggle"
|
|
@click="$router.push({ hash: '#album' })"
|
|
><Icon icon="grid3" />View album</button>
|
|
|
|
<FilterBar
|
|
ref="filter"
|
|
:fetch-releases="fetchActor"
|
|
:items-total="totalCount"
|
|
:items-per-page="limit"
|
|
:available-tags="actor.tags"
|
|
:available-channels="actor.channels"
|
|
:available-actors="actor.actors"
|
|
/>
|
|
|
|
<Releases :releases="releases" />
|
|
|
|
<Pagination
|
|
:items-total="totalCount"
|
|
:items-per-page="limit"
|
|
class="pagination-top"
|
|
/>
|
|
</div>
|
|
|
|
<Footer />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Pagination from '../pagination/pagination.vue';
|
|
import FilterBar from '../filters/filter-bar.vue';
|
|
import Releases from '../releases/releases.vue';
|
|
import Photos from './photos.vue';
|
|
import Album from '../album/album.vue';
|
|
import Expand from '../expand/expand.vue';
|
|
import Scroll from '../scroll/scroll.vue';
|
|
import Gender from './gender.vue';
|
|
import Social from './social.vue';
|
|
|
|
async function fetchActor() {
|
|
const { actor, releases, totalCount } = await this.$store.dispatch('fetchActorById', {
|
|
actorId: Number(this.$route.params.actorId),
|
|
limit: this.limit,
|
|
pageNumber: Number(this.$route.params.pageNumber),
|
|
range: this.$route.params.range,
|
|
});
|
|
|
|
this.actor = actor;
|
|
this.releases = releases;
|
|
this.totalCount = totalCount;
|
|
|
|
if (this.$refs.filter) {
|
|
this.$refs.filter.$el.scrollIntoView();
|
|
}
|
|
}
|
|
|
|
function sfw() {
|
|
return this.$store.state.ui.sfw;
|
|
}
|
|
|
|
function showAlbum() {
|
|
return this.actor.photos?.length > 0 && this.$route.hash === '#album';
|
|
}
|
|
|
|
async function watchRoute(to, from) {
|
|
if (to.params.pageNumber !== from.params.pageNumber) {
|
|
await this.fetchActor();
|
|
}
|
|
}
|
|
|
|
async function mounted() {
|
|
await this.fetchActor();
|
|
|
|
if (this.actor) {
|
|
this.pageTitle = this.actor.name;
|
|
}
|
|
}
|
|
|
|
export default {
|
|
components: {
|
|
Album,
|
|
FilterBar,
|
|
Pagination,
|
|
Photos,
|
|
Scroll,
|
|
Expand,
|
|
Releases,
|
|
Gender,
|
|
Social,
|
|
},
|
|
data() {
|
|
return {
|
|
actor: null,
|
|
releases: null,
|
|
totalCount: 0,
|
|
limit: 20,
|
|
pageTitle: null,
|
|
bioExpanded: false,
|
|
photosExpanded: false,
|
|
};
|
|
},
|
|
computed: {
|
|
sfw,
|
|
showAlbum,
|
|
},
|
|
watch: {
|
|
$route: watchRoute,
|
|
'$store.state.ui.tagFilter': fetchActor,
|
|
},
|
|
mounted,
|
|
methods: {
|
|
fetchActor,
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.header-gender .icon {
|
|
width: 1.25rem;
|
|
height: 1.25rem;
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss" scoped>
|
|
@import 'theme';
|
|
|
|
.actor-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
color: var(--lighten-extreme);
|
|
background: var(--profile);
|
|
padding: .5rem 1rem;
|
|
}
|
|
|
|
.header-name {
|
|
padding: 0;
|
|
margin: 0;
|
|
display: inline-flex;
|
|
justify-content: space-between;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.header-gender {
|
|
display: inline-block;
|
|
margin: 0 0 0 .5rem;
|
|
transform: translate(0, .1rem);
|
|
}
|
|
|
|
.header-social {
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
margin: 0 1rem 0 0;
|
|
}
|
|
|
|
.profile {
|
|
background: var(--profile);
|
|
color: var(--lighten-extreme);
|
|
width: 100%;
|
|
max-height: 18rem;
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-shrink: 0;
|
|
|
|
&.with-avatar {
|
|
height: 18rem; /* profile overlaps avatar in chrome */
|
|
}
|
|
|
|
.avatar-link {
|
|
padding: 0 0 1rem 1rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.avatar {
|
|
height: 100%;
|
|
flex-shrink: 0;
|
|
border: solid 3px var(--lighten-hint);
|
|
margin: 0 .5rem 0 0;
|
|
}
|
|
}
|
|
|
|
.bio {
|
|
flex-grow: 1;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex-wrap: wrap;
|
|
box-sizing: border-box;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.bio-header {
|
|
width: calc(50% - 2rem);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0 .5rem .5rem 0;
|
|
margin: 0 0 0 1rem;
|
|
}
|
|
|
|
.bio-item {
|
|
width: calc(50% - 4rem);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
box-sizing: border-box;
|
|
padding: .25rem 0 ;
|
|
margin: 0 0 .25rem 1rem;
|
|
line-height: 1.75;
|
|
text-align: right;
|
|
font-size: .9rem;
|
|
font-weight: 600;
|
|
overflow: hidden;
|
|
|
|
&:not(:last-of-type) {
|
|
border-bottom: solid 1px var(--lighten-hint);
|
|
}
|
|
}
|
|
|
|
.bio-label,
|
|
.bio-value {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.bio-label {
|
|
color: var(--lighten);
|
|
margin: 0 1rem 0 0;
|
|
flex-shrink: 0;
|
|
font-style: normal;
|
|
font-weight: 400;
|
|
|
|
.icon {
|
|
fill: var(--lighten);
|
|
margin: -.25rem .5rem 0 0;
|
|
}
|
|
}
|
|
|
|
.bio-value {
|
|
margin: 0 0 0 2rem;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
|
|
.icon {
|
|
margin: -.25rem 0 0 0;
|
|
}
|
|
}
|
|
|
|
.flag {
|
|
height: 1rem;
|
|
margin: .25rem .25rem 0 0;
|
|
}
|
|
|
|
.bio-name {
|
|
display: inline-block;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.birthdate {
|
|
display: block;
|
|
}
|
|
|
|
.age {
|
|
font-weight: bold;
|
|
padding: 0 0 0 .5rem;
|
|
border-left: solid 1px var(--lighten-weak);
|
|
margin: 0 0 0 .5rem;
|
|
}
|
|
|
|
.country {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.figure .bio-label .icon {
|
|
margin: -.5rem .5rem 0 0;
|
|
}
|
|
|
|
.height-imperial,
|
|
.weight-imperial,
|
|
.penis-girth-imperial,
|
|
.penis-length-imperial,
|
|
.bio-segment {
|
|
padding: 0 0 0 .5rem;
|
|
border-left: solid 1px var(--lighten-weak);
|
|
margin: 0 0 0 .5rem;
|
|
}
|
|
|
|
.enhanced.icon,
|
|
.circumcised.icon {
|
|
fill: var(--primary);
|
|
padding: 0 .5rem;
|
|
}
|
|
|
|
.enhanced.icon {
|
|
transform: scaleX(-1);
|
|
}
|
|
|
|
.ethnicity,
|
|
.hair,
|
|
.eyes {
|
|
text-transform: capitalize;
|
|
}
|
|
|
|
.scraped {
|
|
color: var(--lighten-weak);
|
|
font-size: .8rem;
|
|
}
|
|
|
|
.descriptions-container {
|
|
max-width: 30rem;
|
|
max-height: 100%;
|
|
position: relative;
|
|
display: block;
|
|
flex-grow: 1;
|
|
box-sizing: border-box;
|
|
overflow: hidden;
|
|
|
|
&::after {
|
|
content: '';
|
|
width: 100%;
|
|
height: 1.5rem;
|
|
position: absolute;
|
|
bottom: 0;
|
|
background: linear-gradient(transparent, 25%, var(--profile) 75%);
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
.descriptions {
|
|
height: 100%;
|
|
overflow: auto;
|
|
scrollbar-width: none;
|
|
|
|
&::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.description {
|
|
margin: 0;
|
|
padding: 0 1rem;
|
|
border-left: solid 3px var(--lighten-hint);
|
|
line-height: 1.5;
|
|
font-size: .9rem;
|
|
}
|
|
|
|
.description-logo {
|
|
display: block;
|
|
width: 12rem;
|
|
max-height: 1.5rem;
|
|
margin: .5rem 0 1.5rem 0;
|
|
object-fit: contain;
|
|
object-position: 0 50%;
|
|
}
|
|
|
|
.actor-content {
|
|
display: flex;
|
|
flex-grow: 1;
|
|
flex-direction: column;
|
|
background: var(--background-soft);
|
|
}
|
|
|
|
.heading {
|
|
padding: 0;
|
|
margin: 0 0 1rem 0;
|
|
}
|
|
|
|
.profile-social {
|
|
display: none;
|
|
}
|
|
|
|
.expand {
|
|
display: none;
|
|
}
|
|
|
|
.scroll {
|
|
background: var(--background-dim);
|
|
border-bottom: solid 1px var(--shadow-hint);
|
|
}
|
|
|
|
@media(max-width: $breakpoint4) {
|
|
.descriptions-container {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
@media(max-width: $breakpoint3) {
|
|
.profile .avatar-link {
|
|
display: none;
|
|
}
|
|
|
|
.actor-content {
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
@media(max-width: $breakpoint) {
|
|
.profile {
|
|
height: auto;
|
|
max-height: none;
|
|
flex-direction: column;
|
|
|
|
&.with-avatar {
|
|
height: auto;
|
|
max-height: none;
|
|
}
|
|
|
|
&:not(.expanded) .hideable {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.bio {
|
|
width: 100%;
|
|
height: auto;
|
|
padding: 0 1rem;
|
|
margin: 0;
|
|
}
|
|
|
|
.bio-item {
|
|
width: 100%;
|
|
margin: 0;
|
|
}
|
|
|
|
.expanded .bio-value {
|
|
white-space: normal;
|
|
}
|
|
|
|
.expand {
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
@media(max-width: $breakpoint0) {
|
|
.header-social {
|
|
display: none;
|
|
}
|
|
|
|
.expanded .profile-social {
|
|
display: block;
|
|
margin: 1rem 0 0 0;
|
|
}
|
|
|
|
.header-name {
|
|
flex-grow: 1;
|
|
font-size: 1.3rem;
|
|
}
|
|
}
|
|
</style>
|