161 lines
4.2 KiB
Vue
161 lines
4.2 KiB
Vue
<script setup>
|
|
|
|
import { marked } from 'marked';
|
|
import { eachLimit } from 'async';
|
|
import { ref, onMounted } from 'vue';
|
|
import { Button } from '@cloudron/pankow';
|
|
import { prettyDate } from '@cloudron/pankow/utils';
|
|
import NotificationsModel from '../models/NotificationsModel.js';
|
|
|
|
const notificationsModel = NotificationsModel.create();
|
|
|
|
const busy = ref(true);
|
|
const notifications = ref([]);
|
|
const notificationsAllBusy = ref(false);
|
|
|
|
async function refresh() {
|
|
const [error, result] = await notificationsModel.list();
|
|
if (error) return console.error(error);
|
|
|
|
notifications.value = result;
|
|
}
|
|
|
|
function onToggleActive(notification) {
|
|
notification.active = !notification.active;
|
|
}
|
|
|
|
async function onMarkNotificationRead(notification) {
|
|
notification.busy = true;
|
|
const [error] = await notificationsModel.update(notification.id, true);
|
|
if (error) return console.error(error);
|
|
|
|
await refresh();
|
|
}
|
|
|
|
async function onMarkNotificationUnread(notification) {
|
|
notification.busy = true;
|
|
const [error] = await notificationsModel.update(notification.id, false);
|
|
if (error) return console.error(error);
|
|
|
|
await refresh();
|
|
}
|
|
|
|
async function onMarkAllNotificationRead() {
|
|
notificationsAllBusy.value = true;
|
|
|
|
await eachLimit(notifications.value.filter(n => !n.acknowledged), 5, async (notification) => {
|
|
notification.busy = true;
|
|
const [error] = await notificationsModel.update(notification.id, true);
|
|
if (error) return console.error(error);
|
|
});
|
|
|
|
await refresh();
|
|
|
|
notificationsAllBusy.value = false;
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await refresh();
|
|
|
|
busy.value = false;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div class="content">
|
|
<!-- <h1 class="view-header">{{ $t('notifications.title') }}</h1> -->
|
|
<h1 class="notification-list-header">
|
|
Notifications
|
|
<Button secondary @click="onMarkAllNotificationRead()" :loading="notificationsAllBusy" :disabled="notificationsAllBusy">{{ $t('notifications.markAllAsRead') }}</Button>
|
|
</h1>
|
|
|
|
<div class="notification-list">
|
|
<TransitionGroup name="fade">
|
|
<div v-for="notification in notifications" :key="notification.id" class="notification-item" :class="{ new: !notification.acknowledged, active: notification.active }">
|
|
<div class="notification-item-title" @click="onToggleActive(notification)">
|
|
<div>
|
|
{{ notification.title }}
|
|
<div class="notification-item-date">{{ prettyDate(notification.creationTime) }}</div>
|
|
</div>
|
|
<Button v-if="notification.acknowledged" plain secondary tool :loading="notification.busy && !notificationsAllBusy" :disabled="notification.busy" @click.stop="onMarkNotificationUnread(notification)">Unread</Button>
|
|
<Button v-else plain primary tool :loading="notification.busy && !notificationsAllBusy" :disabled="notification.busy" @click.stop="onMarkNotificationRead(notification)">Dismiss</Button>
|
|
</div>
|
|
<div class="notification-item-message">
|
|
<div style="cursor: auto; overflow: auto;" v-html="marked.parse(notification.message)"></div>
|
|
</div>
|
|
</div>
|
|
</TransitionGroup>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
.notification-list-header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.notification-list {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-direction: column;
|
|
padding-bottom: 20px;
|
|
}
|
|
|
|
.notification-item {
|
|
cursor: pointer;
|
|
border-radius: 10px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.notification-item.new {
|
|
background-color: var(--navbar-background);
|
|
}
|
|
|
|
.notification-item:hover {
|
|
background-color: var(--pankow-color-background-hover);
|
|
}
|
|
|
|
.notification-item.active {
|
|
z-index: 500;
|
|
position: relative;
|
|
}
|
|
|
|
.notification-item-title {
|
|
font-size: 14px;
|
|
padding: 10px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.notification-item-date {
|
|
font-size: 12px;
|
|
padding-top: 5px;
|
|
color: gray;
|
|
}
|
|
|
|
.notification-item-message {
|
|
display: none;
|
|
font-size: 12px;
|
|
padding-bottom: 10px;
|
|
padding-left: 10px;
|
|
padding-right: 10px;
|
|
}
|
|
|
|
.notification-item.active .notification-item-message {
|
|
display: block;
|
|
}
|
|
|
|
.fade-move,
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
|
|
}
|
|
|
|
</style>
|