From 8f4533a1f8ece0c9e94f0049ebe26c8ed1bbd93c Mon Sep 17 00:00:00 2001 From: DebaucheryLibrarian Date: Fri, 7 Jun 2024 03:15:37 +0200 Subject: [PATCH] Improved actor bio responsiveness, showing secondary actor photos. --- assets/css/breakpoints.css | 1 + components/actors/bio.vue | 626 +++++++++++++++------------ pages/actors/@actorId/+Page.vue | 66 ++- pages/entities/@entitySlug/+Page.vue | 4 +- src/actors.js | 191 +------- 5 files changed, 440 insertions(+), 448 deletions(-) diff --git a/assets/css/breakpoints.css b/assets/css/breakpoints.css index d5382cc..fdde6dd 100644 --- a/assets/css/breakpoints.css +++ b/assets/css/breakpoints.css @@ -3,6 +3,7 @@ @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); diff --git a/components/actors/bio.vue b/components/actors/bio.vue index eb1a943..41b4e01 100644 --- a/components/actors/bio.vue +++ b/components/actors/bio.vue @@ -1,274 +1,292 @@ @@ -278,7 +296,7 @@ import { ref } from 'vue'; import { getMediaPath } from '#/utils/media-path.js'; import { formatDate } from '#/utils/format.js'; -const expanded = ref(true); +const expanded = ref(false); defineProps({ actor: { @@ -289,9 +307,17 @@ defineProps({ @@ -304,12 +330,13 @@ defineProps({ display: flex; flex-direction: row; flex-shrink: 0; + position: relative; &.with-avatar { height: 18rem; /* profile overlaps avatar in chrome */ } - .avatar-link { + .avatar-container { padding: 0 0 1rem 1rem; flex-shrink: 0; } @@ -320,6 +347,11 @@ defineProps({ border: solid 3px var(--highlight-hint); margin: 0 .5rem 0 0; } + + &.expanded { + padding-bottom: 1.5rem; + margin-bottom: .75rem; + } } .bio { @@ -404,10 +436,14 @@ defineProps({ display: block; } +.birthdate-short { + display: none; +} + .age { font-weight: bold; padding: 0 0 0 .5rem; - border-left: solid 1px var(--highlight-weak); + border-left: solid 1px var(--highlight-weak-20); margin: 0 0 0 .5rem; } @@ -416,6 +452,10 @@ defineProps({ justify-content: flex-end; } +.country-alpha2 { + display: none; +} + .figure .bio-label .icon { margin: -.5rem .5rem 0 0; } @@ -451,8 +491,8 @@ defineProps({ content: ',\00a0'; } -.scraped { - color: var(--highlight-weak); +.updated { + color: var(--highlight-weak-20); font-size: .8rem; } @@ -519,8 +559,33 @@ defineProps({ display: none; } -.expand { +.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); + } } .scroll { @@ -544,15 +609,14 @@ defineProps({ } } -/* -@media(max-width: $breakpoint4) { +@media(--big) { .descriptions-container { display: none; } } -@media(max-width: $breakpoint3) { - .profile .avatar-link { +@media(--compact) { + .profile .avatar-container { display: none; } @@ -561,7 +625,7 @@ defineProps({ } } -@media(max-width: $breakpoint) { +@media(--small-15) { .profile { height: auto; max-height: none; @@ -593,8 +657,8 @@ defineProps({ white-space: normal; } - .expand { - display: block; + .expand-container { + display: flex; } .actor-stash { @@ -602,7 +666,7 @@ defineProps({ } } -@media(max-width: $breakpoint0) { +@media(--small-30) { .header-social { display: none; } @@ -625,5 +689,23 @@ 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; + } +} diff --git a/pages/actors/@actorId/+Page.vue b/pages/actors/@actorId/+Page.vue index 2d5f722..61410af 100644 --- a/pages/actors/@actorId/+Page.vue +++ b/pages/actors/@actorId/+Page.vue @@ -35,6 +35,21 @@
+
+ +
+
@@ -43,6 +58,8 @@ diff --git a/pages/entities/@entitySlug/+Page.vue b/pages/entities/@entitySlug/+Page.vue index 737b5a8..1315c91 100644 --- a/pages/entities/@entitySlug/+Page.vue +++ b/pages/entities/@entitySlug/+Page.vue @@ -200,11 +200,11 @@ const scrollable = computed(() => children.value?.scrollWidth > children.value?. align-items: center; padding: .5rem; border: none; - background: var(--grey-dark-40); + background: var(--grey-dark-50); color: var(--highlight-strong-30); font-size: .9rem; font-weight: bold; - border-radius: .25rem; + border-radius: .5rem; box-shadow: 0 0 3px var(--shadow); .icon { diff --git a/src/actors.js b/src/actors.js index b296c82..789c3ba 100644 --- a/src/actors.js +++ b/src/actors.js @@ -58,8 +58,21 @@ 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, @@ -96,7 +109,7 @@ export function sortActorsByGender(actors, context = {}) { } export async function fetchActorsById(actorIds, options = {}, reqUser) { - const [actors, stashes] = await Promise.all([ + const [actors, photos, stashes] = await Promise.all([ knex('actors') .select( 'actors.*', @@ -115,6 +128,14 @@ 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') @@ -140,6 +161,7 @@ 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); @@ -160,173 +182,6 @@ 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;