Added notification bell, WIP.

This commit is contained in:
DebaucheryLibrarian 2024-05-20 06:29:10 +02:00
parent dc5affb4cf
commit 342ba6191e
7 changed files with 141 additions and 53 deletions

View File

@ -91,7 +91,7 @@
<div class="result-label">
{{ actor.name }}
<template v-if="actor.ageFromBirth || actor.origin?.country">({{ [actor.ageFromBirth, actor.origin?.country?.alpha2].join(', ') }})</template>
<template v-if="actor.ageFromBirth || actor.origin?.country">({{ [actor.ageFromBirth, actor.origin?.country?.alpha2].filter(Boolean).join(', ') }})</template>
</div>
</li>
</ul>

View File

@ -78,69 +78,92 @@
</button>
</form>
<VDropdown
<div
v-if="user"
:triggers="['click']"
:prevent-overflow="true"
class="userpanel"
:class="{ searching: searchFocused }"
>
<div
class="userpanel"
:class="{ searching: searchFocused }"
<VDropdown
:triggers="['click']"
:prevent-overflow="true"
>
<Icon
icon="bell2"
class="notifs-bell"
:class="{ unread: notifications.some((notif) => !notif.isSeen) }"
/>
<template #popper>
<div class="menu">
<ul class="notifs nolist">
<li
v-for="notif in notifications"
:key="notif.id"
class="notif"
>{{ notif.scene.title }}</li>
</ul>
</div>
</template>
</VDropdown>
<VDropdown
:triggers="['click']"
:prevent-overflow="true"
>
<img
:src="user.avatar"
class="avatar"
>
</div>
<template #popper>
<div class="menu">
<a
:href="`/user/${user.username}`"
class="menu-header ellipsis"
>{{ user.username }}</a>
<template #popper>
<div class="menu">
<a
:href="`/user/${user.username}`"
class="menu-header ellipsis"
>{{ user.username }}</a>
<ul class="menu-list nolist">
<li class="menu-item">
<a
:href="`/user/${user.username}`"
class="menu-button nolink"
<ul class="menu-list nolist">
<li class="menu-item">
<a
:href="`/user/${user.username}`"
class="menu-button nolink"
>
<Icon icon="user7" />
Profile
</a>
</li>
<li
v-if="user.primaryStash"
class="menu-item"
>
<Icon icon="user7" />
Profile
</a>
</li>
<a
:href="`/stash/${user.username}/${user.primaryStash.slug}`"
class="menu-button nolink favorites"
>
<Icon icon="heart7" />
Favorites
</a>
</li>
<li
v-if="user.primaryStash"
class="menu-item"
>
<a
:href="`/stash/${user.username}/${user.primaryStash.slug}`"
class="menu-button nolink favorites"
<li
v-close-popper
class="menu-item menu-button"
@click="showSettings = true"
>
<Icon icon="heart7" />
Favorites
</a>
</li>
<Icon icon="equalizer" />
Settings
</li>
<li
v-close-popper
class="menu-item menu-button"
@click="showSettings = true"
>
<Icon icon="equalizer" />
Settings
</li>
<li
class="menu-button menu-item logout"
@click="logout"
><Icon icon="exit2" />Log out</li>
</ul>
</div>
</template>
</VDropdown>
<li
class="menu-button menu-item logout"
@click="logout"
><Icon icon="exit2" />Log out</li>
</ul>
</div>
</template>
</VDropdown>
</div>
<div
v-else-if="allowLogin"
@ -181,11 +204,14 @@ import logo from '../../assets/img/logo.svg?raw'; // eslint-disable-line import/
const pageContext = inject('pageContext');
const user = pageContext.user;
const notifications = pageContext.notifications;
const query = ref(pageContext.urlParsed.search.q || '');
const allowLogin = pageContext.env.allowLogin;
const searchFocused = ref(false);
const showSettings = ref(false);
console.log(notifications);
const activePage = computed(() => pageContext.urlParsed.pathname.split('/')[1]);
const currentPath = `${pageContext.urlParsed.pathnameOriginal}${pageContext.urlParsed.searchOriginal || ''}`;
@ -316,7 +342,7 @@ function blurSearch(event) {
height: 100%;
display: flex;
align-items: center;
padding: 0 1rem 0 1.5rem;
padding: 0 1rem 0 0;
font-size: 0;
cursor: pointer;
@ -332,6 +358,31 @@ function blurSearch(event) {
object-fit: cover;
}
.notifs-bell {
padding: 0 1.25rem;
height: 100%;
fill: var(--shadow);
&:hover,
&.unread {
cursor: pointer;
fill: var(--primary);
}
}
.notifs {
overflow: auto;
}
.notif {
display: flex;
padding: .5rem 1rem;
&:not(:last-child) {
border-bottom: solid 1px var(--shadow-weak-30);
}
}
.login {
display: flex;
align-items: center;

View File

@ -11,7 +11,7 @@ export async function onBeforeRender(pageContext) {
: [],
]);
console.log('out alerts', alerts);
console.log(pageContext.notifications);
if (!profile) {
throw render(404, `Cannot find user '${pageContext.routeParams.username}'.`);

View File

@ -6,5 +6,6 @@ export default {
'urlParsed',
'env',
'user',
'notifications',
],
};

View File

@ -140,6 +140,28 @@ export async function removeAlert(alertId, reqUser) {
.delete();
}
export async function fetchNotifications(reqUser, options = {}) {
const notifications = await knex('notifications')
.select('notifications.*', 'alerts.id as alert_id', 'scenes.title as scene_title')
.leftJoin('releases as scenes', 'scenes.id', 'notifications.scene_id')
.leftJoin('alerts', 'alerts.id', 'notifications.alert_id')
.where('notifications.user_id', reqUser.id)
.limit(options.limit || 10)
.orderBy('created_at', 'desc');
return notifications.map((notification) => ({
id: notification.id,
sceneId: notification.scene_id,
scene: {
id: notification.scene_id,
title: notification.scene_title,
},
alertId: notification.alert_id,
isSeen: notification.seen,
createdAt: notification.created_at,
}));
}
export async function updateNotification(notificationId, updatedNotification, reqUser) {
await knex('notifications')
.where('id', notificationId)

View File

@ -2,6 +2,7 @@ import {
fetchAlerts,
createAlert,
removeAlert,
fetchNotifications,
updateNotifications,
updateNotification,
} from '../alerts.js';
@ -24,6 +25,14 @@ export async function removeAlertApi(req, res) {
res.status(204).send();
}
export async function fetchNotificationsApi(req, res) {
const notifications = await fetchNotifications(req.user, {
limit: req.query.limit || 10,
});
res.send(notifications);
}
export async function updateNotificationsApi(req, res) {
await updateNotifications(req.body, req.user);

View File

@ -51,6 +51,8 @@ import {
updateNotificationsApi,
} from './alerts.js';
import { fetchNotifications } from '../alerts.js';
import initLogger from '../logger.js';
const logger = initLogger();
@ -164,6 +166,8 @@ export default async function initServer() {
router.get('/api/tags', fetchTagsApi);
router.get('*', async (req, res, next) => {
const notifications = await fetchNotifications(req.user, { limit: 20 });
const pageContextInit = {
urlOriginal: req.originalUrl,
urlQuery: req.query, // vike's own query does not apply boolean parser
@ -185,6 +189,7 @@ export default async function initServer() {
maxAggregateSize: config.database.manticore.maxAggregateSize,
media: config.media,
},
notifications,
};
const pageContext = await renderPage(pageContextInit);