Added quick alert buttons to entity and tag headers.

This commit is contained in:
DebaucheryLibrarian 2025-03-09 06:00:18 +01:00
parent 61ed171c9d
commit c6c4dcad7c
9 changed files with 118 additions and 61 deletions

View File

@ -32,7 +32,7 @@
</div> </div>
<VDropdown <VDropdown
v-if="showSecondary || (hasSecondaryStash && pageStash?.user.id !== user.id)" v-if="itemStashes && (showSecondary || (hasSecondaryStash && pageStash?.user.id !== user.id))"
:shown="showStashes" :shown="showStashes"
@hide="showStashes = false" @hide="showStashes = false"
> >
@ -55,7 +55,7 @@
</VDropdown> </VDropdown>
<VDropdown <VDropdown
v-if="showSecondary || !hasSecondaryStash || pageStash?.user.id === user.id" v-if="itemStashes && (showSecondary || !hasSecondaryStash || pageStash?.user.id === user.id)"
:triggers="[]" :triggers="[]"
:shown="showStashes" :shown="showStashes"
:disabled="showSecondary" :disabled="showSecondary"
@ -147,8 +147,6 @@ const pageContext = inject('pageContext');
const pageStash = pageContext.pageProps.stash; const pageStash = pageContext.pageProps.stash;
const user = pageContext.user; const user = pageContext.user;
console.log(props.item, props.item.alerts);
const stashes = ref(pageContext.assets?.stashes); const stashes = ref(pageContext.assets?.stashes);
const primaryStash = pageContext.assets?.primaryStash; const primaryStash = pageContext.assets?.primaryStash;
const itemStashes = ref(props.item.stashes); const itemStashes = ref(props.item.stashes);
@ -156,10 +154,6 @@ const itemAlerts = ref(props.item.alerts);
const itemAlerted = ref(props.item.alerts?.only.length > 0); const itemAlerted = ref(props.item.alerts?.only.length > 0);
const hasSecondaryStash = computed(() => itemStashes.value.some((itemStash) => !itemStash.isPrimary)); const hasSecondaryStash = computed(() => itemStashes.value.some((itemStash) => !itemStash.isPrimary));
if (props.domain === 'actors') {
console.log(itemAlerts.value);
}
const done = ref(true); const done = ref(true);
const showStashes = ref(false); const showStashes = ref(false);
const showStashDialog = ref(false); const showStashDialog = ref(false);

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="page"> <div class="page">
<div class="header"> <div class="header">
<div class="entity-container">
<a <a
:href="entityUrl" :href="entityUrl"
target="_blank" target="_blank"
@ -42,6 +43,13 @@
>{{ entity.name }}</h2> >{{ entity.name }}</h2>
</a> </a>
<Heart
domain="entities"
:item="entity"
class="light"
/>
</div>
<Domains <Domains
:domain="domain" :domain="domain"
:path="`/${entity.type}/${entity.slug}`" :path="`/${entity.type}/${entity.slug}`"
@ -144,6 +152,7 @@ import EntityTile from '#/components/entities/tile.vue';
import Scenes from '#/components/scenes/scenes.vue'; import Scenes from '#/components/scenes/scenes.vue';
import Movies from '#/components/movies/movies.vue'; import Movies from '#/components/movies/movies.vue';
import Domains from '#/components/domains/domains.vue'; import Domains from '#/components/domains/domains.vue';
import Heart from '#/components/stashes/heart.vue';
const { pageProps, routeParams } = inject('pageContext'); const { pageProps, routeParams } = inject('pageContext');
const { entity } = pageProps; const { entity } = pageProps;
@ -201,6 +210,11 @@ const entityUrl = (() => {
} }
} }
.entity-container {
display: flex;
align-items: center;
}
.name { .name {
display: block; display: block;
padding: 1rem; padding: 1rem;
@ -229,6 +243,10 @@ const entityUrl = (() => {
position: relative; position: relative;
} }
.bookmarks {
margin: -.25rem 0 0 .25rem;
}
.children { .children {
background: var(--grey-dark-50); background: var(--grey-dark-50);
display: flex; display: flex;

View File

@ -47,7 +47,7 @@ export async function onBeforeRender(pageContext) {
[entity], [entity],
entityReleases, entityReleases,
] = await Promise.all([ ] = await Promise.all([
fetchEntitiesById([Number(entityId)], { includeChildren: true }), fetchEntitiesById([Number(entityId)], { includeChildren: true }, pageContext.user),
fetchReleases(pageContext, entityId), fetchReleases(pageContext, entityId),
]); ]);

View File

@ -2,6 +2,12 @@
<div class="page"> <div class="page">
<div class="header"> <div class="header">
<h2 class="title">{{ tag.name }}</h2> <h2 class="title">{{ tag.name }}</h2>
<Heart
domain="tags"
:item="tag"
class="light"
/>
</div> </div>
<div class="content"> <div class="content">
@ -30,6 +36,7 @@ import { inject } from 'vue';
import Scenes from '#/components/scenes/scenes.vue'; import Scenes from '#/components/scenes/scenes.vue';
import Movies from '#/components/movies/movies.vue'; import Movies from '#/components/movies/movies.vue';
import Domains from '#/components/domains/domains.vue'; import Domains from '#/components/domains/domains.vue';
import Heart from '#/components/stashes/heart.vue';
const pageContext = inject('pageContext'); const pageContext = inject('pageContext');
const { tag, description } = pageContext.pageProps; const { tag, description } = pageContext.pageProps;
@ -59,11 +66,23 @@ const domain = pageContext.routeParams.domain;
} }
.header { .header {
padding: .5rem 1rem; display: flex;
align-items: center;
padding: .25rem 1rem;
color: var(--text-light); color: var(--text-light);
background: var(--grey-dark-40); background: var(--grey-dark-40);
} }
.bookmarks {
margin-left: .5rem;
:deep(.icon.alert) {
height: 1.25rem;
padding: .5rem .5rem .25rem .5rem;
overflow: hidden;
}
}
.title { .title {
margin: 0; margin: 0;
text-transform: capitalize; text-transform: capitalize;

View File

@ -40,7 +40,7 @@ export async function onBeforeRender(pageContext) {
const tagSlug = pageContext.routeParams.tagSlug; const tagSlug = pageContext.routeParams.tagSlug;
const [[tag], tagReleases, campaigns] = await Promise.all([ const [[tag], tagReleases, campaigns] = await Promise.all([
fetchTagsById([tagSlug]), fetchTagsById([tagSlug], {}, pageContext.user),
fetchReleases(pageContext), fetchReleases(pageContext),
getRandomCampaigns([ getRandomCampaigns([
{ tagSlugs: [tagSlug], minRatio: 1.5 }, { tagSlugs: [tagSlug], minRatio: 1.5 },

View File

@ -485,10 +485,6 @@ async function queryManticoreSql(filters, options, _reqUser) {
export async function fetchActors(filters, rawOptions, reqUser) { export async function fetchActors(filters, rawOptions, reqUser) {
const options = curateOptions(rawOptions); const options = curateOptions(rawOptions);
console.log('filters', filters);
console.log('options', options);
const result = await queryManticoreSql(filters, options, reqUser); const result = await queryManticoreSql(filters, options, reqUser);
// console.log('result', result); // console.log('result', result);

View File

@ -175,8 +175,6 @@ export async function removeAlert(alertId, reqUser) {
.groupBy('alerts.id') .groupBy('alerts.id')
.first(); .first();
console.log(alertId, alert);
await knex('alerts') await knex('alerts')
.where('id', alertId) .where('id', alertId)
.where('user_id', reqUser.id) .where('user_id', reqUser.id)

View File

@ -26,6 +26,10 @@ export function curateEntity(entity, context) {
parameters: entity.affiliate.parameters, parameters: entity.affiliate.parameters,
} : null, } : null,
...context?.append?.[entity.id], ...context?.append?.[entity.id],
alerts: {
only: context?.alerts?.filter((alert) => alert.is_only).flatMap((alert) => alert.alert_ids) || [],
multi: context?.alerts?.filter((alert) => !alert.is_only).flatMap((alert) => alert.alert_ids) || [],
},
}; };
} }
@ -74,8 +78,8 @@ export async function fetchEntities(options = {}) {
return entities.map((entityEntry) => curateEntity(entityEntry)); return entities.map((entityEntry) => curateEntity(entityEntry));
} }
export async function fetchEntitiesById(entityIds, options = {}) { export async function fetchEntitiesById(entityIds, options = {}, reqUser) {
const [entities, children] = await Promise.all([ const [entities, children, alerts] = await Promise.all([
knex('entities') knex('entities')
.select( .select(
'entities.*', 'entities.*',
@ -95,12 +99,18 @@ export async function fetchEntitiesById(entityIds, options = {}) {
.whereIn('entities.parent_id', entityIds) .whereIn('entities.parent_id', entityIds)
.whereNot('type', 'info') .whereNot('type', 'info')
.orderBy('slug') : [], .orderBy('slug') : [],
reqUser
? knex('alerts_users_entities')
.where('user_id', reqUser.id)
.whereIn('entity_id', entityIds)
: [],
]); ]);
if (options.order) { if (options.order) {
return entities.map((entityEntry) => curateEntity(entityEntry, { return entities.map((entityEntry) => curateEntity(entityEntry, {
append: options.append, append: options.append,
children, children: children.filter((channel) => channel.parent_id === entityEntry.id),
alerts: alerts.filter((alert) => alert.entity_id === entityEntry.id),
})); }));
} }
@ -114,7 +124,8 @@ export async function fetchEntitiesById(entityIds, options = {}) {
return curateEntity(entity, { return curateEntity(entity, {
append: options.append, append: options.append,
children, children: children.filter((channel) => channel.parent_id === entity.id),
alerts: alerts.filter((alert) => alert.entity_id === entity.id),
}); });
}).filter(Boolean); }).filter(Boolean);

View File

@ -25,6 +25,10 @@ function curateTag(tag, context) {
parent: tag.poster.entity_parent, parent: tag.poster.entity_parent,
}), }),
}, },
alerts: {
only: context?.alerts?.filter((alert) => alert.is_only).flatMap((alert) => alert.alert_ids) || [],
multi: context?.alerts?.filter((alert) => !alert.is_only).flatMap((alert) => alert.alert_ids) || [],
},
...context?.append?.[tag.id], ...context?.append?.[tag.id],
}; };
} }
@ -75,8 +79,8 @@ export async function fetchTags(options = {}) {
})); }));
} }
export async function fetchTagsById(tagIds, options = {}) { export async function fetchTagsById(tagIds, options = {}, reqUser) {
const [tags, posters] = await Promise.all([ const [tags, posters, alerts] = await Promise.all([
knex('tags') knex('tags')
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number')) .whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string')) .orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string'))
@ -93,6 +97,17 @@ export async function fetchTagsById(tagIds, options = {}) {
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id') .leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number')) .whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string')), .orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string')),
reqUser
? knex('alerts_users_tags')
.select('alerts_users_tags.*')
.leftJoin('tags', 'tags.id', 'alerts_users_tags.tag_id')
.where('user_id', reqUser.id)
.where((whereBuilder) => {
whereBuilder
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string'));
})
: [],
]); ]);
const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster])); const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster]));
@ -101,7 +116,10 @@ export async function fetchTagsById(tagIds, options = {}) {
return tags.map((tagEntry) => curateTag({ return tags.map((tagEntry) => curateTag({
...tagEntry, ...tagEntry,
poster: postersByTagId[tagEntry.id], poster: postersByTagId[tagEntry.id],
}, { append: options.append })); }, {
alerts: alerts.filter((alert) => alert.tag_id === tagEntry.id),
append: options.append,
}));
} }
const curatedTags = tagIds.map((tagId) => { const curatedTags = tagIds.map((tagId) => {
@ -115,7 +133,10 @@ export async function fetchTagsById(tagIds, options = {}) {
return curateTag({ return curateTag({
...tag, ...tag,
poster: postersByTagId[tag.id], poster: postersByTagId[tag.id],
}, { append: options.append }); }, {
alerts: alerts.filter((alert) => alert.tag_id === tag.id),
append: options.append,
});
}).filter(Boolean); }).filter(Boolean);
return curatedTags; return curatedTags;