111 lines
3.0 KiB
Vue
111 lines
3.0 KiB
Vue
<script setup lang="ts">
|
|
import { AnnouncementAPIClient, type Announcement } from '@/api/announcement';
|
|
import { APIError } from '@/api/base';
|
|
import { useAuthStore } from '@/stores/auth';
|
|
import { onMounted, onUnmounted, ref, useTemplateRef, type Ref } from 'vue';
|
|
import ConfirmDialog from './ConfirmDialog.vue';
|
|
|
|
const REFRESH_INTERVAL_MS = 5000
|
|
|
|
const authStore = useAuthStore()
|
|
const client = new AnnouncementAPIClient(authStore)
|
|
const announcements: Ref<Announcement[]> = ref([])
|
|
const dismissedIds: Ref<number[]> = ref([])
|
|
const refreshTimerId: Ref<number | undefined> = ref(undefined)
|
|
const deleteAnnouncementConfirmDialog = useTemplateRef('deleteAnnouncementConfirmDialog')
|
|
|
|
onMounted(() => {
|
|
refreshAnnouncements()
|
|
refreshTimerId.value = setInterval(refreshAnnouncements, REFRESH_INTERVAL_MS)
|
|
})
|
|
onUnmounted(() => clearInterval(refreshTimerId.value))
|
|
|
|
function refreshAnnouncements() {
|
|
client.getAnnouncements().result.then(value => {
|
|
if (value instanceof APIError) {
|
|
console.warn('Failed to get announcements: ', value.message)
|
|
announcements.value = []
|
|
} else {
|
|
announcements.value = value
|
|
}
|
|
})
|
|
}
|
|
|
|
function getVisibleAnnouncements(): Announcement[] {
|
|
return announcements.value.filter(a => !dismissedIds.value.includes(a.id))
|
|
}
|
|
|
|
function getBannerClasses(a: Announcement) {
|
|
return {
|
|
'announcement-banner-info': a.type === 'INFO',
|
|
'announcement-banner-error': a.type === 'ERROR'
|
|
}
|
|
}
|
|
|
|
function dismiss(a: Announcement) {
|
|
dismissedIds.value.push(a.id)
|
|
}
|
|
|
|
async function deleteAnnouncement(a: Announcement) {
|
|
const result = await deleteAnnouncementConfirmDialog.value?.show()
|
|
if (result) {
|
|
await client.deleteAnnouncement(a.id).handleErrorsWithAlert()
|
|
}
|
|
}
|
|
</script>
|
|
<template>
|
|
<div>
|
|
<div v-for="a in getVisibleAnnouncements()" :key="a.id" class="announcement-banner" :class="getBannerClasses(a)">
|
|
<p class="announcement-banner-message">{{ a.message }}</p>
|
|
<button class="announcement-banner-button" @click="dismiss(a)">Dismiss</button>
|
|
<button v-if="authStore.admin" class="announcement-banner-button" @click="deleteAnnouncement(a)">Delete</button>
|
|
</div>
|
|
|
|
<ConfirmDialog ref="deleteAnnouncementConfirmDialog">
|
|
<p>
|
|
Are you sure you want to delete this announcement? It will take a few
|
|
seconds for the announcement to disappear from users' screens.
|
|
</p>
|
|
</ConfirmDialog>
|
|
</div>
|
|
</template>
|
|
<style scoped>
|
|
.announcement-banner {
|
|
padding: 1rem;
|
|
margin: 1rem 1rem 0 1rem;
|
|
border-radius: 15px;
|
|
border-width: 2px;
|
|
border-style: solid;
|
|
display: flex;
|
|
color: rgb(207, 207, 207);
|
|
}
|
|
|
|
.announcement-banner-message {
|
|
margin: 0;
|
|
flex-grow: 1;
|
|
color: inherit;
|
|
}
|
|
|
|
.announcement-banner-button {
|
|
background-color: inherit;
|
|
border: none;
|
|
font-size: small;
|
|
color: inherit;
|
|
}
|
|
|
|
.announcement-banner-button:hover {
|
|
color: white;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.announcement-banner-info {
|
|
background-color: rgb(19, 19, 85);
|
|
border-color: rgb(70, 70, 201);
|
|
}
|
|
|
|
.announcement-banner-error {
|
|
background-color: rgb(146, 14, 14);
|
|
border-color: rgb(228, 110, 106);
|
|
}
|
|
</style>
|