traxxx/assets/components/actors/actor.vue

870 lines
18 KiB
Vue
Raw Normal View History

<template>
<div
v-if="actor"
class="content actor"
>
<div class="actor-header">
<h2 class="header-name">
2020-06-28 02:22:19 +00:00
<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 class="bio-value">
<span
v-for="alias in actor.aliases"
:key="`alias-${alias.id}`"
class="alias"
>{{ alias.name }}</span>
</span>
</li>
<Social
v-if="actor.social && actor.social.length > 0"
:actor="actor"
class="header-social"
/>
2021-03-15 02:30:47 +00:00
<StashButton
:stashed-by="stashedBy"
class="actor-stash light"
@stash="(stash) => stashActor(stash)"
@unstash="(stash) => unstashActor(stash)"
2021-03-15 02:30:47 +00:00
/>
</div>
<div
class="content-inner actor-inner"
@scroll="events.emit('scroll', $event)"
>
<div
class="profile"
:class="{ expanded: bioExpanded, 'with-avatar': !!actor.avatar }"
>
<a
v-if="actor.avatar"
:href="getPath(actor.avatar)"
target="_blank"
rel="noopener noreferrer"
class="avatar-link"
>
<img
:src="getPath(actor.avatar, 'thumbnail')"
:title="actor.avatar.credit && `© ${actor.avatar.credit}`"
class="avatar"
>
</a>
<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"
>&gt; {{ actor.age }}</span>
</li>
<li
v-if="actor.dateOfDeath"
class="bio-item"
>
<dfn class="bio-label"><Icon icon="tombstone" />Date of death</dfn>
2020-05-17 03:08:41 +00:00
<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"
2020-05-17 01:00:44 +00:00
: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"
2020-05-17 01:00:44 +00:00
: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"
2020-05-17 01:00:44 +00:00
class="bio-item figure"
>
<dfn class="bio-label"><Icon icon="ruler" />Figure</dfn>
2020-05-17 01:00:44 +00:00
<span class="bio-value">
<Icon
v-if="actor.naturalBoobs === false"
2020-05-17 01:00:44 +00:00
v-tooltip="'Enhanced boobs'"
2020-05-17 23:22:56 +00:00
icon="magic-wand2"
class="enhanced"
2020-05-17 01:00:44 +00:00
/>{{ 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"
>
2020-05-19 23:11:32 +00:00
<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"
2020-06-28 02:22:19 +00:00
:key="`description-${description.entity.id}`"
class="description"
>
{{ description.text }}
2020-06-28 02:22:19 +00:00
<router-link :to="`/${description.entity.type}/${description.entity.slug}`">
<img
2020-09-03 20:22:12 +00:00
v-if="description.entity.type === 'network' || !description.entity.parent || description.entity.independent"
2020-06-28 02:22:19 +00:00
:src="`/img/logos/${description.entity.slug}/thumbs/network.png`"
class="description-logo"
>
<img
v-else
2020-06-28 02:22:19 +00:00
: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.avatar, ...actor.photos]"
:title="actor.name"
:portrait="true"
:comments="true"
@close="$router.replace({ hash: undefined })"
/>
<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>
2020-05-26 23:40:10 +00:00
<FilterBar
ref="filter"
2020-05-26 23:40:10 +00:00
:fetch-releases="fetchActor"
:items-total="totalCount"
:items-per-page="limit"
:available-tags="actor.tags"
2020-07-20 02:20:33 +00:00
:available-channels="actor.channels"
:available-actors="actor.actors"
2020-05-26 23:40:10 +00:00
/>
<Releases
:releases="releases"
:done="done"
/>
2020-05-26 23:40:10 +00:00
<Pagination
:items-total="totalCount"
:items-per-page="limit"
name="actorRange"
2020-05-26 23:40:10 +00:00
class="pagination-top"
/>
</div>
2020-08-15 17:04:33 +00:00
<Footer />
</div>
</div>
</template>
<script>
2020-05-26 23:40:10 +00:00
import Pagination from '../pagination/pagination.vue';
2020-07-20 02:20:33 +00:00
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';
2020-01-24 22:36:06 +00:00
import Social from './social.vue';
import StashButton from '../stashes/button.vue';
2021-03-15 02:30:47 +00:00
async function fetchActor(scroll = true) {
this.done = false;
2020-05-26 23:40:10 +00:00
const { actor, releases, totalCount } = await this.$store.dispatch('fetchActorById', {
2020-05-25 02:39:58 +00:00
actorId: Number(this.$route.params.actorId),
limit: this.limit,
2020-05-26 23:40:10 +00:00
pageNumber: Number(this.$route.params.pageNumber),
range: this.$route.params.range,
});
2020-05-26 23:40:10 +00:00
this.actor = actor;
this.releases = releases;
this.totalCount = totalCount;
this.stashedBy = actor.stashes;
this.done = true;
2021-03-15 02:30:47 +00:00
if (this.$refs.filter && scroll) {
this.$refs.filter.$el.scrollIntoView();
}
}
async function stashActor(stashId) {
this.stashedBy = await this.$store.dispatch('stashActor', {
actorId: this.actor.id,
stashId,
});
2021-03-15 02:30:47 +00:00
}
async function unstashActor(stashId) {
this.stashedBy = await this.$store.dispatch('unstashActor', {
actorId: this.actor.id,
stashId,
});
2021-03-15 02:30:47 +00:00
}
function me() {
return this.$store.state.auth.user;
}
2020-05-17 23:22:56 +00:00
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.name === 'actor' || to.name === 'actorRange') && to.hash !== '#album' && from.hash !== '#album') {
await this.fetchActor();
}
}
async function mounted() {
await this.fetchActor();
if (this.actor) {
this.pageTitle = this.actor.name;
}
}
export default {
components: {
Album,
FilterBar,
2020-05-26 23:40:10 +00:00
Pagination,
Photos,
Scroll,
Expand,
Releases,
Gender,
Social,
StashButton,
},
data() {
return {
actor: null,
releases: null,
done: false,
2020-05-26 23:40:10 +00:00
totalCount: 0,
2021-01-17 20:24:20 +00:00
limit: 20,
pageTitle: null,
bioExpanded: false,
photosExpanded: false,
stashed: false,
stashedBy: [],
};
},
2020-05-17 23:22:56 +00:00
computed: {
2021-03-15 02:30:47 +00:00
me,
2020-05-17 23:22:56 +00:00
sfw,
showAlbum,
2020-05-17 23:22:56 +00:00
},
watch: {
$route: watchRoute,
'$store.state.ui.tagFilter': fetchActor,
},
mounted,
methods: {
fetchActor,
2021-03-15 02:30:47 +00:00
stashActor,
unstashActor,
},
};
</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;
2020-07-20 02:20:33 +00:00
color: var(--lighten-extreme);
2020-07-05 02:27:46 +00:00
background: var(--profile);
}
.actor-stash {
margin: 0 1rem 0 0;
}
.header-name {
2021-03-15 02:30:47 +00:00
padding: .5rem 1rem;
margin: 0;
display: inline-flex;
justify-content: space-between;
flex-shrink: 0;
}
.header-gender {
display: inline-block;
margin: 0 0 0 .5rem;
2021-03-15 02:30:47 +00:00
transform: translate(0, .125rem);
}
.header-social {
overflow: hidden;
white-space: nowrap;
margin: 0 1rem 0 0;
}
.profile {
2020-07-05 02:27:46 +00:00
background: var(--profile);
2020-07-20 02:20:33 +00:00
color: var(--lighten-extreme);
width: 100%;
2020-01-24 22:46:44 +00:00
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) {
2020-07-20 02:20:33 +00:00
border-bottom: solid 1px var(--lighten-hint);
}
}
2020-05-17 01:00:44 +00:00
.bio-label,
.bio-value {
display: flex;
align-items: center;
}
.bio-label {
2020-07-20 02:20:33 +00:00
color: var(--lighten);
margin: 0 1rem 0 0;
flex-shrink: 0;
font-style: normal;
font-weight: 400;
.icon {
2020-07-20 02:20:33 +00:00
fill: var(--lighten);
2020-05-17 01:00:44 +00:00
margin: -.25rem .5rem 0 0;
}
}
.bio-value {
margin: 0 0 0 2rem;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
2020-05-17 01:00:44 +00:00
.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;
2020-07-20 02:20:33 +00:00
border-left: solid 1px var(--lighten-weak);
margin: 0 0 0 .5rem;
}
.country {
display: flex;
2020-05-17 01:00:44 +00:00
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;
2020-07-20 02:20:33 +00:00
border-left: solid 1px var(--lighten-weak);
margin: 0 0 0 .5rem;
}
.enhanced.icon,
.circumcised.icon {
2020-07-05 02:27:46 +00:00
fill: var(--primary);
padding: 0 .5rem;
}
.enhanced.icon {
2020-05-17 23:22:56 +00:00
transform: scaleX(-1);
}
.ethnicity,
.hair,
.eyes {
2020-01-24 22:36:06 +00:00
text-transform: capitalize;
}
.alias:not(:last-child)::after {
content: ',\00a0';
}
.scraped {
2020-07-20 02:20:33 +00:00
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;
2020-05-19 23:11:32 +00:00
padding: 0 1rem;
border-left: solid 3px var(--lighten-hint);
line-height: 1.5;
font-size: .9rem;
}
.description-logo {
display: block;
2020-05-19 23:11:32 +00:00
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;
2020-05-25 00:02:28 +00:00
background: var(--background-soft);
}
.heading {
padding: 0;
margin: 0 0 1rem 0;
}
2020-01-24 22:36:06 +00:00
.profile-social {
display: none;
}
.expand {
display: none;
}
.scroll {
background: var(--background-dim);
border-bottom: solid 1px var(--shadow-hint);
2020-01-24 22:36:06 +00:00
}
2021-03-15 02:30:47 +00:00
.stash.icon {
width: 1.5rem;
height: 1.5rem;
padding: 0 1rem;
fill: var(--lighten);
&.stashed {
fill: var(--primary);
}
&:hover {
fill: var(--primary);
cursor: pointer;
}
}
@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;
2020-01-24 22:36:06 +00:00
&.with-avatar {
height: auto;
max-height: none;
}
2020-01-24 22:36:06 +00:00
&:not(.expanded) .hideable {
display: none;
}
}
.bio {
width: 100%;
2020-01-24 22:36:06 +00:00
height: auto;
padding: 0 1rem;
margin: 0;
}
.bio-item {
2020-01-24 22:36:06 +00:00
width: 100%;
margin: 0;
}
.expanded .bio-value {
white-space: normal;
2020-01-24 22:36:06 +00:00
}
.expand {
display: block;
}
.actor-stash {
margin: 0 .5rem 0 0;
}
}
@media(max-width: $breakpoint0) {
.header-social {
display: none;
}
2020-01-24 22:36:06 +00:00
.expanded .profile-social {
display: block;
margin: 1rem 0 0 0;
}
.header-name {
2021-03-15 02:30:47 +00:00
flex-grow: 1;
font-size: 1.3rem;
2021-03-15 02:30:47 +00:00
padding: .5rem .5rem .5rem 1rem;
}
2021-03-15 02:30:47 +00:00
.stash.icon {
width: 1.25rem;
height: 1.25rem;
padding: 0 1rem 0 .25rem;
transform: translate(0, -.1rem);
}
}
</style>