NotificationLatch/frontend/src/views/Notification.vue

342 lines
7.9 KiB
Vue

<template>
<div>
<p class="loading" v-if="tokenValid === null">
{{ $t('notification.loading') }}
</p>
<p class="invalid" v-if="tokenValid === false">
{{ $t('notification.tokenInvalid') }}
</p>
<div class="notifications" v-if="tokenValid === true">
<div class="header">{{ $t('notification.listHeader') }}</div>
<div class="list">
<div class="notification" v-for="notification in orderedNotifications" :key="notification.id">
<div class="title">{{ notification.title }}</div>
<div class="content">
<div class="info">
<p class="latchTime">{{ $t('notification.latchTime', { latchTime: formatDateTime(notification.latchTime) }) }}</p>
<p class="latched">{{ $t('notification.latched') }}</p>
</div>
<div class="actions">
<a href="#" @click.prevent="enableNotification(notification)" class="enable" :class="{ active: !notification.enabling }">{{ $t('notification.enableNotification') }}</a>
</div>
<div class="reminders" v-if="reminders.enabled">
<p class="interval">{{ $t('notification.reminders', { interval: formattedReminderInterval })}}</p>
<div class="actions">
<a href="#" @click.prevent="enableReminders(notification)" class="reminders-enabled" :class="{ active: !notification.updatingReminders && notification.reminders }">{{ $t('notification.remindersEnabled') }}</a>
<a href="#" @click.prevent="disableReminders(notification)" class="reminders-disabled" :class="{ active: !notification.updatingReminders && !notification.reminders }">{{ $t('notification.remindersDisabled') }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import axios from 'axios';
import { DateTime, Duration } from 'luxon';
import { ILatchedNotifications, INotificationReminders, INotification } from '../model/notifications';
interface INotificationViewModel extends INotification
{
enabling: boolean;
updatingReminders: boolean;
}
export default defineComponent({
props: [
'token'
],
data()
{
return {
tokenValid: null as null | boolean,
reminders: null as null | INotificationReminders,
notifications: null as null | Array<INotificationViewModel>
};
},
mounted()
{
this.refreshToken();
},
watch: {
token()
{
this.refreshToken();
}
},
computed: {
formattedReminderInterval(): string
{
if (this.reminders === null || !this.reminders.enabled)
return '';
const duration = Duration.fromObject(this.reminders.interval);
const interval = duration.shiftTo('days', 'hours', 'minutes', 'seconds');
const units = [];
if (interval.days)
units.push(this.$tc('duration.days', interval.days));
if (interval.hours)
units.push(this.$tc('duration.hours', interval.hours));
if (interval.minutes)
units.push(this.$tc('duration.minutes', interval.minutes));
if (interval.seconds)
units.push(this.$tc('duration.seconds', interval.seconds));
if (units.length == 0)
return '<error>';
if (units.length == 1)
return units[0];
const lastUnit = units.pop();
return units.join(this.$t('duration.glue')) + this.$t('duration.lastGlue') + lastUnit;
},
orderedNotifications(): Array<INotificationViewModel>
{
if (this.notifications === null)
return [];
return [...this.notifications].sort((a, b) =>
{
if (a.token === this.token)
return -1;
if (b.token === this.token)
return 1;
return a.title.localeCompare(b.title);
});
}
},
methods: {
async refreshToken()
{
try
{
const response = await axios.get<ILatchedNotifications>('/api/notification/latched', {
headers: {
'Authorization': 'Bearer ' + this.token
}
});
this.reminders = response.data.reminders;
this.notifications = response.data.notifications.map(notification =>
{
return {
token: notification.token,
id: notification.id,
title: notification.title,
latched: notification.latched,
latchTime: notification.latchTime,
resetTime: notification.resetTime,
reminders: notification.reminders,
remindTime: notification.remindTime,
enabling: false,
updatingReminders: false
} as INotificationViewModel;
});
this.tokenValid = true;
}
catch
{
this.tokenValid = false;
}
},
async enableNotification(notification: INotificationViewModel)
{
if (this.notifications == null || notification.enabling)
return;
notification.enabling = true;
try
{
await axios.post('/api/notification/' + notification.token + '/reset');
this.notifications = this.notifications.filter((n: INotification) => n !== notification);
}
finally
{
notification.enabling = false;
}
},
async enableReminders(notification: INotificationViewModel)
{
if (notification.updatingReminders)
return;
notification.updatingReminders = true;
try
{
await axios.post('/api/notification/' + notification.token + '/reminders/enable');
notification.reminders = true;
}
finally
{
notification.updatingReminders = false;
}
},
async disableReminders(notification: INotificationViewModel)
{
if (notification.updatingReminders)
return;
notification.updatingReminders = true;
try
{
await axios.post('/api/notification/' + notification.token + '/reminders/disable');
notification.reminders = false;
}
finally
{
notification.updatingReminders = false;
}
},
formatDateTime(unixTimestamp: number)
{
return DateTime.fromSeconds(unixTimestamp).toLocaleString(DateTime.DATETIME_FULL);
}
}
});
</script>
<style lang="scss">
.notifications
{
margin: 1rem;
.header
{
font-size: 1.25rem;
font-weight: bold;
margin-bottom: 2rem;
text-align: center;
}
.list
{
color: black;
.notification
{
background: #fcfcfc;
border: solid 1px #2b5074;
border-radius: 5px;
margin-bottom: 3rem;
.title
{
background: #d4e1ee;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
font-weight: bold;
padding: .5rem;
}
.content
{
margin: .5rem;
}
.info
{
font-size: .75rem;
.latchTime
{
color: gray;
margin-top: 0;
}
}
.actions
{
margin-top: .25rem;
margin-bottom: 1rem;
}
.enable,
.reminders-disabled,
.reminders-enabled
{
display: inline-block;
padding: .5em;
text-decoration: none;
border-radius: 5px;
margin-right: .5rem;
color: gray;
background: white;
border: solid 1px darkgray;
cursor: pointer;
}
.active
{
cursor: default;
&.enable,
&.reminders-enabled
{
color: white;
background: #248f24;
border: solid 1px green;
}
&.enable
{
cursor: pointer;
}
&.reminders-disabled
{
color: white;
background: darkred;
border: solid 1px red;
}
}
.reminders
{
.interval
{
font-size: .75rem;
}
}
}
}
}
</style>