Compare commits

..

No commits in common. "170eae1b097acdcadacd98860ae366e38ab1c246" and "af88455c6b50e372b5fd84ccd1777cf9ebb88e5f" have entirely different histories.

7 changed files with 452 additions and 444 deletions

View File

@ -3,7 +3,6 @@
@custom-media --small-40 (max-width: 480px);
@custom-media --small-30 (max-width: 540px);
@custom-media --small-20 (max-width: 650px);
@custom-media --small-15 (max-width: 720px);
@custom-media --small-10 (max-width: 768px);
@custom-media --small (max-width: 900px);
@custom-media --compact (max-width: 1200px);

View File

@ -1,18 +1,25 @@
<template>
<div
class="content-inner actor-inner"
@scroll="events.emit('scroll', $event)"
>
<div
class="profile"
:class="{ expanded, 'with-avatar': !!actor.avatar }"
>
<div
<a
v-if="actor.avatar"
class="avatar-container"
:href="getMediaPath(actor.avatar)"
target="_blank"
rel="noopener noreferrer"
class="avatar-link"
>
<img
:src="getMediaPath(actor.avatar, 'thumbnail')"
:title="actor.avatar.credit && `© ${actor.avatar.credit}`"
class="avatar"
>
</div>
</a>
<ul class="bio nolist">
<li
@ -21,15 +28,10 @@
>
<dfn class="bio-label"><Icon icon="cake" />Date of birth</dfn>
<span class="birthdate">
<span class="birthdate-long">{{ formatDate(actor.dateOfBirth, 'MMMM d, yyyy') }}</span>
<span class="birthdate-short">{{ formatDate(actor.dateOfBirth, 'MMM d, yyyy') }}</span>
<span
<span class="birthdate">{{ formatDate(actor.dateOfBirth, 'MMMM d, yyyy') }}<span
v-if="!actor.dateOfDeath"
class="age"
>{{ actor.ageFromBirth }}</span>
</span>
>{{ actor.ageFromBirth }}</span></span>
</li>
<li
@ -87,10 +89,7 @@
<img
class="flag"
:src="`/img/flags/${actor.origin.country.alpha2.toLowerCase()}.svg`"
>
<span class="country-name">{{ actor.origin.country.alias || actor.origin.country.name }}</span>
<span class="country-alpha2">{{ actor.origin.country.alpha2 }}</span>
>{{ actor.origin.country.alias || actor.origin.country.name }}
</span>
</span>
</li>
@ -239,7 +238,7 @@
<span v-else>Yes</span>
</li>
<li class="bio-item updated hideable">Updated {{ formatDate(actor.updatedAt, 'yyyy-MM-dd hh:mm') }}, ID: {{ actor.id }}</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">
@ -269,23 +268,6 @@
</p>
</div>
</div>
<div class="expand-container">
<button
type="button"
class="expand"
@click="expanded = !expanded"
>
<Icon
v-show="expanded"
icon="arrow-up3"
/>
<Icon
v-show="!expanded"
icon="arrow-down3"
/>
</button>
</div>
</div>
</template>
@ -296,7 +278,7 @@ import { ref } from 'vue';
import { getMediaPath } from '#/utils/media-path.js';
import { formatDate } from '#/utils/format.js';
const expanded = ref(false);
const expanded = ref(true);
defineProps({
actor: {
@ -307,17 +289,9 @@ defineProps({
</script>
<style>
.header-gender {
display: inline-flex;
align-items: center;
margin: 0 0 0 .5rem;
transform: translate(0, .125rem);
font-size: 0;
.icon {
.header-gender .icon {
width: 1.25rem;
height: 1.25rem;
}
}
</style>
@ -330,13 +304,12 @@ defineProps({
display: flex;
flex-direction: row;
flex-shrink: 0;
position: relative;
&.with-avatar {
height: 18rem; /* profile overlaps avatar in chrome */
}
.avatar-container {
.avatar-link {
padding: 0 0 1rem 1rem;
flex-shrink: 0;
}
@ -347,11 +320,6 @@ defineProps({
border: solid 3px var(--highlight-hint);
margin: 0 .5rem 0 0;
}
&.expanded {
padding-bottom: 1.5rem;
margin-bottom: .75rem;
}
}
.bio {
@ -436,14 +404,10 @@ defineProps({
display: block;
}
.birthdate-short {
display: none;
}
.age {
font-weight: bold;
padding: 0 0 0 .5rem;
border-left: solid 1px var(--highlight-weak-20);
border-left: solid 1px var(--highlight-weak);
margin: 0 0 0 .5rem;
}
@ -452,10 +416,6 @@ defineProps({
justify-content: flex-end;
}
.country-alpha2 {
display: none;
}
.figure .bio-label .icon {
margin: -.5rem .5rem 0 0;
}
@ -491,8 +451,8 @@ defineProps({
content: ',\00a0';
}
.updated {
color: var(--highlight-weak-20);
.scraped {
color: var(--highlight-weak);
font-size: .8rem;
}
@ -559,33 +519,8 @@ defineProps({
display: none;
}
.expand-container {
width: 100%;
display: none;
justify-content: center;
position: absolute;
bottom: -.75rem;
}
.expand {
width: 4rem;
height: 2rem;
display: inline-flex;
justify-content: center;
align-items: center;
border: none;
border-radius: .5rem;
background: var(--grey-dark-50);
box-shadow: 0 0 3px var(--shadow);
.icon {
fill: var(--text-light);
}
&:hover {
cursor: pointer;
background: var(--primary);
}
display: none;
}
.scroll {
@ -609,14 +544,15 @@ defineProps({
}
}
@media(--big) {
/*
@media(max-width: $breakpoint4) {
.descriptions-container {
display: none;
}
}
@media(--compact) {
.profile .avatar-container {
@media(max-width: $breakpoint3) {
.profile .avatar-link {
display: none;
}
@ -625,7 +561,7 @@ defineProps({
}
}
@media(--small-15) {
@media(max-width: $breakpoint) {
.profile {
height: auto;
max-height: none;
@ -657,8 +593,8 @@ defineProps({
white-space: normal;
}
.expand-container {
display: flex;
.expand {
display: block;
}
.actor-stash {
@ -666,7 +602,7 @@ defineProps({
}
}
@media(--small-30) {
@media(max-width: $breakpoint0) {
.header-social {
display: none;
}
@ -689,23 +625,5 @@ defineProps({
transform: translate(0, -.1rem);
}
}
@media(--small-60) {
.birthdate-long,
.age {
display: none;
}
.birthdate-short {
display: inline-block;
}
.country-name {
display: none;
}
.country-alpha2 {
display: inline-block;
}
}
*/
</style>

4
package-lock.json generated
View File

@ -1,11 +1,11 @@
{
"name": "traxxx-web",
"version": "0.23.6",
"version": "0.23.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "0.23.6",
"version": "0.23.5",
"dependencies": {
"@brillout/json-serializer": "^0.5.8",
"@dicebear/collection": "^7.0.5",

View File

@ -77,5 +77,5 @@
"postcss-custom-media": "^10.0.2",
"postcss-nesting": "^12.0.2"
},
"version": "0.23.6"
"version": "0.23.5"
}

View File

@ -35,21 +35,6 @@
<div class="content">
<Bio :actor="actor" />
<div class="photos nobar">
<img
v-for="photo in photos"
:key="`photo-${photo.id}`"
:src="getPath(photo, 'thumbnail')"
:width="photo.width"
:height="photo.height"
:style="{ 'background-image': `url('${getPath(photo, 'lazy')}')` }"
:title="photo.credit && `© ${photo.credit}`"
loading="lazy"
class="photo"
:class="{ avatar: photo.isAvatar }"
>
</div>
<Scenes />
</div>
</div>
@ -58,8 +43,6 @@
<script setup>
import { inject } from 'vue';
import getPath from '#/src/get-path.js';
import Bio from '#/components/actors/bio.vue';
import Gender from '#/components/actors/gender.vue';
import Scenes from '#/components/scenes/scenes.vue';
@ -68,14 +51,6 @@ import Heart from '#/components/stashes/heart.vue';
const pageContext = inject('pageContext');
const { pageProps } = pageContext;
const { actor } = pageProps;
const photos = [
actor.avatar && {
...actor.avatar,
isAvatar: true,
},
...actor.photos,
].filter(Boolean);
</script>
<style scoped>
@ -106,6 +81,12 @@ const photos = [
flex-shrink: 0;
}
.header-gender {
display: inline-block;
margin: 0 0 0 .5rem;
transform: translate(0, .125rem);
}
.header-social {
overflow: hidden;
white-space: nowrap;
@ -122,39 +103,4 @@ const photos = [
.bookmarks {
margin-right: .5rem;
}
.photos {
display: flex;
gap: 0.5rem;
padding: .5rem;
border-bottom: solid 1px var(--shadow-weak-40);
background: var(--background-base-10);
overflow-x: auto;
}
.photo {
height: 14rem;
width: auto;
object-fit: cover;
object-position: 50% 0;
background-size: cover;
background-position: center;
box-shadow: 0 0px 3px var(--shadow-weak-30);
&.avatar {
display: none;
}
}
@media(--compact) {
.photo.avatar {
display: inline-block;
}
}
@media(--small) {
.photo {
height: 10rem;
}
}
</style>

View File

@ -200,11 +200,11 @@ const scrollable = computed(() => children.value?.scrollWidth > children.value?.
align-items: center;
padding: .5rem;
border: none;
background: var(--grey-dark-50);
background: var(--grey-dark-40);
color: var(--highlight-strong-30);
font-size: .9rem;
font-weight: bold;
border-radius: .5rem;
border-radius: .25rem;
box-shadow: 0 0 3px var(--shadow);
.icon {

View File

@ -58,21 +58,8 @@ export function curateActor(actor, context = {}) {
path: actor.avatar.path,
thumbnail: actor.avatar.thumbnail,
lazy: actor.avatar.lazy,
width: actor.avatar.width,
height: actor.avatar.height,
isS3: actor.avatar.is_s3,
credit: actor.avatar.credit,
},
photos: context.photos?.map((photo) => ({
id: photo.id,
path: photo.path,
thumbnail: photo.thumbnail,
lazy: photo.lazy,
width: photo.width,
height: photo.height,
isS3: photo.is_s3,
credit: photo.credit,
})),
createdAt: actor.created_at,
updatedAt: actor.updated_at,
likes: actor.stashed,
@ -109,7 +96,7 @@ export function sortActorsByGender(actors, context = {}) {
}
export async function fetchActorsById(actorIds, options = {}, reqUser) {
const [actors, photos, stashes] = await Promise.all([
const [actors, stashes] = await Promise.all([
knex('actors')
.select(
'actors.*',
@ -128,14 +115,6 @@ export async function fetchActorsById(actorIds, options = {}, reqUser) {
builder.orderBy(...options.order);
}
}),
knex('actors_profiles')
.select('actors_profiles.actor_id', 'media.*')
.leftJoin('actors', 'actors.id', 'actors_profiles.actor_id')
.leftJoin('media', 'media.id', 'actors_profiles.avatar_media_id')
.whereIn('actor_id', actorIds)
.whereNotNull('actors_profiles.avatar_media_id')
.whereNot('actors_profiles.avatar_media_id', knex.raw('actors.avatar_media_id')) // don't include main avatar as photo
.groupBy('actors_profiles.actor_id', 'media.id', 'media.hash'),
reqUser
? knex('stashes_actors')
.leftJoin('stashes', 'stashes.id', 'stashes_actors.stash_id')
@ -161,7 +140,6 @@ export async function fetchActorsById(actorIds, options = {}, reqUser) {
return curateActor(actor, {
stashes: stashes.filter((stash) => stash.actor_id === actor.id),
photos: photos.filter((photo) => photo.actor_id === actor.id),
append: options.append,
});
}).filter(Boolean);
@ -182,6 +160,173 @@ function curateOptions(options) {
};
}
/*
const sortMap = {
likes: 'stashed',
scenes: 'scenes',
relevance: '_score',
};
function getSort(order) {
if (order[0] === 'name') {
return [{
slug: order[1],
}];
}
return [
{
[sortMap[order[0]]]: order[1],
},
{
slug: 'asc', // sort by name where primary order is equal
},
];
}
function buildQuery(filters) {
const query = {
bool: {
must: [],
},
};
const expressions = {
age: 'if(date_of_birth, floor((now() - date_of_birth) / 31556952), 0)',
};
if (filters.query) {
query.bool.must.push({
match: {
name: filters.query,
},
});
}
['gender', 'country'].forEach((attribute) => {
if (filters[attribute]) {
query.bool.must.push({
equals: {
[attribute]: filters[attribute],
},
});
}
});
['age', 'height', 'weight'].forEach((attribute) => {
if (filters[attribute]) {
query.bool.must.push({
range: {
[attribute]: {
gte: filters[attribute][0],
lte: filters[attribute][1],
},
},
});
}
});
if (filters.dateOfBirth && filters.dobType === 'dateOfBirth') {
query.bool.must.push({
equals: {
date_of_birth: Math.floor(filters.dateOfBirth.getTime() / 1000),
},
});
}
if (filters.dateOfBirth && filters.dobType === 'birthday') {
expressions.month_of_birth = 'month(date_of_birth)';
expressions.day_of_birth = 'day(date_of_birth)';
const month = filters.dateOfBirth.getMonth() + 1;
const day = filters.dateOfBirth.getDate();
query.bool.must.push({
bool: {
must: [
{
equals: {
month_of_birth: month,
},
},
{
equals: {
day_of_birth: day,
},
},
],
},
});
}
if (filters.cup) {
expressions.cup_in_range = `regex(cup, '^[${filters.cup[0]}-${filters.cup[1]}]')`;
query.bool.must.push({
equals: {
cup_in_range: 1,
},
});
}
if (typeof filters.naturalBoobs === 'boolean') {
query.bool.must.push({
equals: {
natural_boobs: filters.naturalBoobs ? 2 : 1, // manticore boolean does not support null, so 0 = null, 1 = false (enhanced), 2 = true (natural)
},
});
}
if (filters.requireAvatar) {
query.bool.must.push({
equals: {
has_avatar: 1,
},
});
}
return { query, expressions };
}
async function queryManticoreJson(filters, options) {
const { query, expressions } = buildQuery(filters);
const result = await searchApi.search({
index: 'actors',
query,
expressions,
limit: options.limit,
offset: (options.page - 1) * options.limit,
sort: getSort(options.order, filters),
aggs: {
countries: {
terms: {
field: 'country',
size: 300,
},
sort: [{ country: { order: 'asc' } }],
},
},
options: {
max_matches: config.database.manticore.maxMatches,
max_query_time: config.database.manticore.maxQueryTime,
},
});
const actors = result.hits.hits.map((hit) => ({
id: hit._id,
...hit._source,
_score: hit._score,
}));
return {
actors,
total: result.hits.total,
aggregations: result.aggregations && Object.fromEntries(Object.entries(result.aggregations).map(([key, { buckets }]) => [key, buckets])),
};
}
*/
async function queryManticoreSql(filters, options, _reqUser) {
const aggSize = config.database.manticore.maxAggregateSize;