Compare commits

..

No commits in common. "8d364b1cbd3bcb8e939ce88f41bb88630f68a318" and "cb5d931309df5588c4fbfa020fa0b45ad8cee22c" have entirely different histories.

6 changed files with 67 additions and 260 deletions

View File

@ -2,7 +2,6 @@
--primary-dark-10: #e54485; --primary-dark-10: #e54485;
--primary: #f65596; --primary: #f65596;
--primary-light-10: #f075a6; --primary-light-10: #f075a6;
--primary-light-20: #f2a6c4;
--primary-light-30: #f7c9dc; --primary-light-30: #f7c9dc;
--grey-dark-50: #111; --grey-dark-50: #111;
@ -30,7 +29,6 @@
--background-level-30: #eee; --background-level-30: #eee;
--background-dim: var(--shadow-weak-10); --background-dim: var(--shadow-weak-10);
--shadow-weak-50: rgba(0, 0, 0, .02);
--shadow-weak-40: rgba(0, 0, 0, .05); --shadow-weak-40: rgba(0, 0, 0, .05);
--shadow-weak-30: rgba(0, 0, 0, .1); --shadow-weak-30: rgba(0, 0, 0, .1);
--shadow-weak-20: rgba(0, 0, 0, .2); --shadow-weak-20: rgba(0, 0, 0, .2);

View File

@ -19,10 +19,7 @@
</div> </div>
</div> </div>
<ul <ul class="notifs nolist">
v-if="done"
class="notifs nolist"
>
<li <li
v-for="notif in notifications" v-for="notif in notifications"
:key="notif.id" :key="notif.id"
@ -35,34 +32,30 @@
target="_blank" target="_blank"
class="notif-body notif-link nolink" class="notif-body notif-link nolink"
> >
<span class="notif-trigger">
<span class="notif-details"> <span class="notif-details">
New New
<template <span
v-if="notif.matchedTags.length > 0" v-if="notif.matchedTags.length > 0"
>{{ notif.matchedTags.map((tag) => tag.name).join(', ') }}</template> class="notif-tags"
>{{ notif.matchedTags.map((tag) => tag.name).join(', ') }}</span>
scene scene
<template <span
v-if="notif.matchedActors.length > 0" v-if="notif.matchedActors.length > 0"
>with {{ notif.matchedActors.map((actor) => actor.name).join(', ') }}&nbsp;</template> class="notif-actors"
>with {{ notif.matchedActors.map((actor) => actor.name).join(', ') }}&nbsp;</span>
<template
v-if="notif.matchedEntity"
>for {{ notif.matchedEntity.name }}&nbsp;</template>
<template
v-if="notif.matchedExpressions.length > 0"
>matching {{ notif.matchedExpressions.map((match) => match.expression).join(', ') }}</template>
</span>
<span <span
v-if="notif.alertId" v-if="notif.matchedEntity"
class="notif-id" class="notif-entities"
title="Alert ID" >for {{ notif.matchedEntity.name }}&nbsp;</span>
>#{{ notif.alertId }}</span>
<span
v-if="notif.matchedExpressions.length > 0"
class="notif-entities"
>matching {{ notif.matchedExpressions.map((match) => match.expression).join(', ') }}</span>
</span> </span>
<span class="notif-scene"> <span class="notif-scene">
@ -72,11 +65,6 @@
</a> </a>
</li> </li>
</ul> </ul>
<Ellipsis
v-else
class="notifs loading"
/>
</div> </div>
</template> </template>
@ -91,8 +79,6 @@ import {
import { get, patch } from '#/src/api.js'; import { get, patch } from '#/src/api.js';
import { formatDate } from '#/utils/format.js'; import { formatDate } from '#/utils/format.js';
import Ellipsis from '#/components/loading/ellipsis.vue';
const pageContext = inject('pageContext'); const pageContext = inject('pageContext');
const user = pageContext.user; const user = pageContext.user;
@ -102,12 +88,9 @@ const done = ref(true);
const emit = defineEmits(['unseen', 'addAlert']); const emit = defineEmits(['unseen', 'addAlert']);
async function fetchNotifications() { async function fetchNotifications() {
done.value = false;
const res = await get(`/users/${user?.id}/notifications`); const res = await get(`/users/${user?.id}/notifications`);
notifications.value = res.notifications;
done.value = true; notifications.value = res.notifications;
emit('unseen', res.unseen); emit('unseen', res.unseen);
} }
@ -148,18 +131,13 @@ onMounted(async () => {
<style scoped> <style scoped>
.notifs { .notifs {
width: 30rem; width: 30rem;
max-height: 20rem; height: 20rem;
max-width: 100%; max-width: 100%;
height: 100%; max-height: 100%;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
.loading {
display: flex;
justify-content: center;
}
.notifs-header { .notifs-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -231,19 +209,13 @@ onMounted(async () => {
font-size: .9rem; font-size: .9rem;
} }
.notif-trigger { .notif-details {
display: flex; padding: .5rem 0 .15rem 0;
justify-content: space-between;
padding: .5rem 0 .1rem 0;
box-sizing: border-box; box-sizing: border-box;
color: var(--shadow-strong); color: var(--shadow-strong);
font-weight: bold; font-weight: bold;
} }
.notif-details {
line-height: 1.25;
}
.notif-link { .notif-link {
overflow: hidden; overflow: hidden;
} }
@ -270,10 +242,4 @@ onMounted(async () => {
object-fit: cover; object-fit: cover;
} }
.notif-id {
padding: 0 .5rem;
color: var(--shadow-weak-10);
font-size: .9rem;
font-weight: normal;
}
</style> </style>

4
package-lock.json generated
View File

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

View File

@ -75,5 +75,5 @@
"postcss-custom-media": "^10.0.2", "postcss-custom-media": "^10.0.2",
"postcss-nesting": "^12.0.2" "postcss-nesting": "^12.0.2"
}, },
"version": "0.19.3" "version": "0.19.2"
} }

View File

@ -89,129 +89,44 @@
:key="`alert-${alert.id}`" :key="`alert-${alert.id}`"
class="alert" class="alert"
> >
<div class="alert-triggers"> <div class="alert-details">
<Icon
icon="bell2"
:title="alert.notify ? 'Notify in traxxx' : undefined"
:class="{ trigger: alert.notify }"
/>
<Icon
v-if="alert.stashes.some((stash) => !stash.isPrimary)"
icon="folder-heart"
:title="`Add to ${alert.stashes.map((stash) => stash.name).join(', ')}`"
class="trigger"
/>
<Icon
v-else
icon="heart7"
:title="alert.stashes.length > 0 ? 'Add to Favorites' : undefined"
:class="{ trigger: alert.stashes.length > 0 }"
/>
<!--
<Icon
icon="envelop5"
title="E-mail me"
:class="{ trigger: alert.email }"
/>
-->
</div>
<div
class="alert-details"
:class="{ and: alert.and.fields, or: !alert.and.fields }"
>
<span <span
v-if="alert.tags.length > 0" v-if="alert.tags.length > 0"
class="alert-detail alert-tags" class="alert-tags"
:class="{ and: alert.and.tags, or: !alert.and.tags }" ><a
>
<span class="alert-values">
<span
v-for="tag in alert.tags" v-for="tag in alert.tags"
:key="`tag-${alert.id}-${tag.id}`" :key="`tag-${alert.id}-${tag.id}`"
class="alert-key"
>
<a
:href="`/tag/${tag.slug}`" :href="`/tag/${tag.slug}`"
class="alert-value link" class="link"
>{{ tag.name }}</a> >{{ tag.name }}</a>&nbsp;</span>
</span>
</span>
</span>
<span <span
v-if="alert.actors.length > 0" v-if="alert.actors.length > 0"
class="alert-detail alert-actors" class="alert-actors"
:class="{ and: alert.and.actors, or: !alert.and.actors }" >with <a
>
<span class="alert-values">with
<span
v-for="actor in alert.actors" v-for="actor in alert.actors"
:key="`actor-${alert.id}-${actor.id}`" :key="`actor-${alert.id}-${actor.id}`"
class="alert-key"
>
<a
:href="`/actor/${actor.id}/${actor.slug}`" :href="`/actor/${actor.id}/${actor.slug}`"
class="alert-value link" class="link"
>{{ actor.name }}</a> >{{ actor.name }}</a>&nbsp;</span>
</span>
</span>
</span>
<span <span
v-if="alert.entities.length > 0" v-if="alert.entities.length > 0"
class="alert-detail alert-entities or" class="alert-entities"
> >for <a
<span class="alert-values">for
<span
v-for="entity in alert.entities" v-for="entity in alert.entities"
:key="`entity-${alert.id}-${entity.id}`" :key="`entity-${alert.id}-${entity.id}`"
class="alert-key"
>
<a
:href="`/${entity.type}/${entity.slug}`" :href="`/${entity.type}/${entity.slug}`"
class="alert-value link" class="link"
> >{{ entity.name }}</a>&nbsp;</span>
<Icon
v-if="entity.type === 'network'"
icon="device_hub"
/>{{ entity.name }}
</a>
</span>
</span>
</span>
<span <span
v-if="alert.matches.length > 0" v-if="alert.matches.length > 0"
class="alert-detail alert-matches" class="alert-entities"
:class="{ and: alert.and.matches, or: !alert.and.matches }" >matching {{ alert.matches.map((match) => match.expression).join(', ') }}</span>
>
<span class="alert-values">matching
<span
v-for="match in alert.matches"
:key="`match-${alert.id}-${match.id}`"
class="alert-key"
>
<span class="alert-value">{{ match.property }}:
<span
class="alert-regex"
title="If your original expression was not a /regular expression/, it was converted, and new characters may have been added for syntactical purposes. These characters do not alter the function of the expression; they ensure it."
>{{ match.expression }}</span>
</span>
</span>
</span>
</span>
</div> </div>
<div class="alert-actions"> <div class="alert-actions">
<span
class="alert-id"
title="Alert ID"
>#{{ alert.id }}</span>
<Icon <Icon
icon="bin" icon="bin"
@click="removeAlert(alert)" @click="removeAlert(alert)"
@ -395,90 +310,36 @@ async function removeAlert(alert) {
} }
.alert { .alert {
padding: 0 0 0 .5rem;
display: flex; display: flex;
align-items: stretch; align-items: stretch;
&:not(:last-child) {
border-bottom: solid 1px var(--shadow-weak-40); border-bottom: solid 1px var(--shadow-weak-40);
}
&:hover { &:hover {
border-color: var(--shadow-weak-30); background: var(--shadow-weak-40);
}
} }
.alert-triggers { .link:not(:last-child):after {
display: flex; content: ', ';
align-items: center;
gap: .5rem;
margin-right: 1rem;
.icon {
fill: var(--shadow-weak-40);
}
.icon.trigger {
fill: var(--shadow-weak-10);
} }
} }
.alert-details { .alert-details {
padding: .75rem 0; padding: .5rem 1rem;
flex-grow: 1; flex-grow: 1;
line-height: 1.5; line-height: 1.5;
color: var(--shadow);
&.and .alert-detail:not(:last-child):after { .link {
content: ' and '; color: inherit;
}
&.or .alert-detail:not(:last-child):after {
content: ' or ';
}
}
.alert-value {
color: var(--text);
.icon {
margin-right: .25rem;
fill: var(--shadow);
transform: translateY(2px);
}
}
.alert-values {
padding: .5rem .5rem;
border-bottom: solid 1px var(--primary-light-20);
border-radius: .3rem;
}
.alert-detail {
&.and .alert-key:not(:last-child):after {
content: ' and ';
}
&.or .alert-key:not(:last-child):after {
content: ' or ';
}
}
.alert-regex {
&:before,
&:after {
content: '';
padding: 0 .1rem;
color: var(--primary-light-20);
} }
} }
.alert-actions { .alert-actions {
display: flex;
align-items: center;
font-size: .9rem;
color: var(--shadow-weak-10);
.icon { .icon {
height: 100%; height: 100%;
padding: 0 .75rem; padding: 0 .5rem;
fill: var(--shadow); fill: var(--shadow);
&:hover { &:hover {
@ -508,10 +369,6 @@ async function removeAlert(alert) {
.stashes { .stashes {
padding: 0 1rem 1rem 1rem; padding: 0 1rem 1rem 1rem;
} }
.alert {
padding: 0 .5rem 0 1rem;
}
} }
@media(--small-30) { @media(--small-30) {

View File

@ -39,12 +39,6 @@ function curateAlert(alert, context = {}) {
property: match.property, property: match.property,
expression: match.expression, expression: match.expression,
})) || [], })) || [],
stashes: context.stashes?.map((stash) => ({
id: stash.stash_id,
name: stash.stash_name,
slug: stash.stash_slug,
isPrimary: stash.stash_primary,
})) || [],
}; };
} }
@ -55,11 +49,9 @@ export async function fetchAlerts(user) {
tags, tags,
entities, entities,
matches, matches,
stashes,
} = await promiseProps({ } = await promiseProps({
alerts: knex('alerts') alerts: knex('alerts')
.where('user_id', user.id) .where('user_id', user.id),
.orderBy('created_at', 'desc'),
actors: knex('alerts_actors') actors: knex('alerts_actors')
.select('alerts_actors.*', 'actors.name as actor_name', 'actors.slug as actor_slug') .select('alerts_actors.*', 'actors.name as actor_name', 'actors.slug as actor_slug')
.leftJoin('alerts', 'alerts.id', 'alerts_actors.alert_id') .leftJoin('alerts', 'alerts.id', 'alerts_actors.alert_id')
@ -79,11 +71,6 @@ export async function fetchAlerts(user) {
.select('alerts_matches.*') .select('alerts_matches.*')
.leftJoin('alerts', 'alerts.id', 'alerts_matches.alert_id') .leftJoin('alerts', 'alerts.id', 'alerts_matches.alert_id')
.where('alerts.user_id', user.id), .where('alerts.user_id', user.id),
stashes: knex('alerts_stashes')
.select('alerts_stashes.*', 'stashes.id as stash_id', 'stashes.name as stash_name', 'stashes.slug as stash_slug', 'stashes.primary as stash_primary')
.leftJoin('alerts', 'alerts.id', 'alerts_stashes.alert_id')
.leftJoin('stashes', 'stashes.id', 'alerts_stashes.stash_id')
.where('alerts.user_id', user.id),
}); });
const curatedAlerts = alerts.map((alert) => curateAlert(alert, { const curatedAlerts = alerts.map((alert) => curateAlert(alert, {
@ -91,7 +78,6 @@ export async function fetchAlerts(user) {
tags: tags.filter((tag) => tag.alert_id === alert.id), tags: tags.filter((tag) => tag.alert_id === alert.id),
entities: entities.filter((entity) => entity.alert_id === alert.id), entities: entities.filter((entity) => entity.alert_id === alert.id),
matches: matches.filter((match) => match.alert_id === alert.id), matches: matches.filter((match) => match.alert_id === alert.id),
stashes: stashes.filter((stash) => stash.alert_id === alert.id),
})); }));
return curatedAlerts; return curatedAlerts;