233 lines
6.9 KiB
Vue
233 lines
6.9 KiB
Vue
<template>
|
|
<q-layout view="hHh Lpr lff">
|
|
<q-header elevated class="bg-primary text-white">
|
|
<q-toolbar>
|
|
<q-btn dense flat round icon="mdi-menu" @click="openMenu" />
|
|
|
|
<q-toolbar-title>
|
|
<router-link :to="{ name: 'dashboard' }" style="text-decoration: none; color: inherit">
|
|
<q-avatar rounded>
|
|
<img src="flaschengeist-logo-white.svg" />
|
|
</q-avatar>
|
|
<span class="gt-xs"> Flaschengeist </span>
|
|
</router-link>
|
|
</q-toolbar-title>
|
|
|
|
<!-- Hier kommen die Shortlinks hin -->
|
|
<q-btn icon="mdi-message-bulleted" flat dense round>
|
|
<q-badge color="negative" floating>
|
|
{{ notifications.length }}
|
|
</q-badge>
|
|
<q-menu max-height="400px" style="min-width: 290px" class="q-pa-xs">
|
|
<q-btn
|
|
v-if="useNative && noPermission"
|
|
label="Benachrichtigungen erlauben"
|
|
@click="requestPermission"
|
|
/>
|
|
<template v-if="notifications.length > 0">
|
|
<Notification
|
|
v-for="(notification, index) in notifications"
|
|
:key="index"
|
|
:model-value="notification"
|
|
@remove="remove"
|
|
/>
|
|
</template>
|
|
<div v-else class="q-pa-sm">Keine neuen Benachrichtigungen</div>
|
|
</q-menu>
|
|
</q-btn>
|
|
<drag v-model="shortCuts" item-key="link" ghost-class="ghost">
|
|
<template #item="{ element }">
|
|
<shortcut-link :shortcut="element" context @delete-shortcut="deleteShortcut" />
|
|
</template>
|
|
</drag>
|
|
<q-btn
|
|
v-if="!platform.is.capacitor"
|
|
flat
|
|
round
|
|
dense
|
|
icon="mdi-exit-to-app"
|
|
@click="logout()"
|
|
/>
|
|
</q-toolbar>
|
|
</q-header>
|
|
|
|
<q-drawer
|
|
v-model="leftDrawer"
|
|
side="left"
|
|
bordered
|
|
:mini="leftDrawerMini"
|
|
@click.capture="openMenu"
|
|
>
|
|
<!-- Plugins -->
|
|
<essential-expansion-link
|
|
v-for="(entry, index) in mainLinks"
|
|
:key="'plugin' + index"
|
|
:entry="entry"
|
|
@add-short-cut="addShortcut"
|
|
/>
|
|
<q-separator />
|
|
<essential-link
|
|
v-for="(entry, index) in essentials"
|
|
:key="'essential' + index"
|
|
:entry="entry"
|
|
/>
|
|
<div v-if="platform.is.capacitor">
|
|
<q-separator />
|
|
<q-item clickable tag="a" target="self" @click="logout">
|
|
<q-item-section avatar>
|
|
<q-icon name="mdi-exit-to-app" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label>Logout</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
</div>
|
|
</q-drawer>
|
|
<q-page-container>
|
|
<router-view />
|
|
</q-page-container>
|
|
</q-layout>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import EssentialExpansionLink from 'components/navigation/EssentialExpansionLink.vue';
|
|
import EssentialLink from 'src/components/navigation/EssentialLink.vue';
|
|
import ShortcutLink from 'src/components/navigation/ShortcutLink.vue';
|
|
import Notification from 'src/components/Notification.vue';
|
|
import { defineComponent, ref, inject, computed, onBeforeMount, onBeforeUnmount } from 'vue';
|
|
import { Screen, Platform } from 'quasar';
|
|
import config from 'src/config';
|
|
import { useRouter } from 'vue-router';
|
|
import { useMainStore } from '@flaschengeist/api';
|
|
import { FG_Plugin } from '@flaschengeist/types';
|
|
import drag from 'vuedraggable';
|
|
|
|
const essentials: FG_Plugin.MenuLink[] = [
|
|
{
|
|
title: 'Über Flaschengeist',
|
|
link: 'about',
|
|
icon: 'mdi-information',
|
|
},
|
|
];
|
|
|
|
export default defineComponent({
|
|
name: 'MainLayout',
|
|
components: {
|
|
EssentialExpansionLink,
|
|
EssentialLink,
|
|
ShortcutLink,
|
|
Notification,
|
|
drag,
|
|
},
|
|
setup() {
|
|
const router = useRouter();
|
|
const mainStore = useMainStore();
|
|
const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist');
|
|
const leftDrawer = ref(!Platform.is.mobile);
|
|
const leftDrawerMini = ref(false);
|
|
const mainLinks = flaschengeist?.menuLinks || [];
|
|
const notifications = computed(() => mainStore.notifications.slice().reverse());
|
|
const polling = ref(NaN);
|
|
const useNative = 'Notification' in window && window.Notification !== undefined;
|
|
const noPermission = ref(!useNative || window.Notification.permission !== 'granted');
|
|
|
|
onBeforeMount(() => {
|
|
polling.value = window.setInterval(() => pollNotification(), config.pollingInterval);
|
|
pollNotification();
|
|
void mainStore.getShortcuts();
|
|
});
|
|
onBeforeUnmount(() => window.clearInterval(polling.value));
|
|
|
|
function openMenu(event: { target: HTMLInputElement }) {
|
|
if (event.target.nodeName === 'DIV') leftDrawerMini.value = false;
|
|
else {
|
|
if (!leftDrawer.value || leftDrawerMini.value) {
|
|
leftDrawer.value = true;
|
|
leftDrawerMini.value = false;
|
|
} else {
|
|
leftDrawerMini.value = Screen.gt.sm && !leftDrawerMini.value;
|
|
leftDrawer.value = leftDrawerMini.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
function logout() {
|
|
void router.push({ name: 'login', params: { logout: 'logout' } });
|
|
void mainStore.logout();
|
|
}
|
|
|
|
async function remove(id: number) {
|
|
await mainStore.removeNotification(id);
|
|
}
|
|
|
|
function requestPermission() {
|
|
void window.Notification.requestPermission().then(
|
|
(p) => (noPermission.value = p !== 'granted')
|
|
);
|
|
}
|
|
|
|
function pollNotification() {
|
|
void mainStore
|
|
.loadNotifications(<FG_Plugin.Flaschengeist>flaschengeist)
|
|
.then((notifications) => {
|
|
if (useNative && !noPermission.value)
|
|
notifications.forEach(
|
|
(notif) =>
|
|
new window.Notification(notif.text, {
|
|
timestamp: notif.time.getTime(),
|
|
})
|
|
);
|
|
});
|
|
}
|
|
|
|
const shortCuts = computed({
|
|
get: () => mainStore.shortcuts,
|
|
set: (val: Array<FG_Plugin.MenuLink>) => {
|
|
mainStore.shortcuts = val;
|
|
void mainStore.setShortcuts();
|
|
},
|
|
});
|
|
|
|
function addShortcut(val: FG_Plugin.MenuLink) {
|
|
const idx = shortCuts.value.findIndex((a: FG_Plugin.MenuLink) => a.link === val.link);
|
|
if (idx < 0) {
|
|
shortCuts.value.push(val);
|
|
void mainStore.setShortcuts();
|
|
}
|
|
}
|
|
|
|
function deleteShortcut(val: FG_Plugin.MenuLink) {
|
|
const idx = shortCuts.value.findIndex((a: FG_Plugin.MenuLink) => a.link === val.link);
|
|
if (idx > -1) {
|
|
shortCuts.value.splice(idx, 1);
|
|
void mainStore.setShortcuts();
|
|
}
|
|
}
|
|
|
|
return {
|
|
essentials,
|
|
leftDrawer,
|
|
leftDrawerMini,
|
|
logout,
|
|
mainLinks,
|
|
notifications,
|
|
noPermission,
|
|
openMenu,
|
|
remove,
|
|
requestPermission,
|
|
useNative,
|
|
shortCuts,
|
|
addShortcut,
|
|
deleteShortcut,
|
|
platform: Platform,
|
|
};
|
|
},
|
|
});
|
|
</script>
|
|
<style scoped lang="sass">
|
|
.ghost
|
|
opacity: 0.5
|
|
background: $accent
|
|
</style>
|