client: use native Notifications API (#234)

Reviewed-on: FoundKeyGang/FoundKey#234
Changelog: Changed
This commit is contained in:
Norm 2022-11-29 12:35:36 -05:00
commit 5b574d40f9
Signed by untrusted user: norm
GPG key ID: 7123E30E441E80DE
4 changed files with 20 additions and 104 deletions

View file

@ -1,67 +0,0 @@
<template>
<div class="mk-notification-toast" :style="{ zIndex }">
<transition :name="$store.state.animation ? 'notification-toast' : ''" appear @after-leave="emit('closed')">
<XNotification v-if="showing" :notification="notification" class="notification _panel"/>
</transition>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue';
import XNotification from './notification.vue';
import * as os from '@/os';
defineProps<{
notification: any; // TODO
}>();
const emit = defineEmits<{
(ev: 'closed'): void;
}>();
const zIndex = os.claimZIndex('high');
let showing = $ref(true);
onMounted(() => {
window.setTimeout(() => {
showing = false;
}, 6000);
});
</script>
<style lang="scss" scoped>
.notification-toast-enter-active, .notification-toast-leave-active {
transition: opacity 0.3s, transform 0.3s !important;
}
.notification-toast-enter-from, .notification-toast-leave-to {
opacity: 0;
transform: translateX(-250px);
}
.mk-notification-toast {
position: fixed;
left: 0;
width: 250px;
top: 32px;
padding: 0 32px;
pointer-events: none;
@media (max-width: 700px) {
top: initial;
bottom: 112px;
padding: 0 16px;
}
@media (max-width: 500px) {
bottom: calc(env(safe-area-inset-bottom, 0px) + 92px);
padding: 0 8px;
}
> .notification {
height: 100%;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
border-radius: 8px;
overflow: hidden;
}
}
</style>

View file

@ -9,7 +9,7 @@ export async function initializeSw() {
navigator.serviceWorker.register('/sw.js', { scope: '/', type: 'classic' });
navigator.serviceWorker.ready.then(registration => {
registration.active?.postMessage({
msg: 'initialize',
type: 'initialize',
lang,
});
@ -33,14 +33,14 @@ export async function initializeSw() {
})
// When subscribe failed
.catch(async (err: Error) => {
// 通知が許可されていなかったとき
// when notifications were not authorized
if (err.name === 'NotAllowedError') {
return;
}
// 違うapplicationServerKey (または gcm_sender_id)のサブスクリプションが
// 既に存在していることが原因でエラーになった可能性があるので、
// そのサブスクリプションを解除しておく
// The error may have been caused by the fact that a subscription to a
// different applicationServerKey (or gcm_sender_id) already exists, so
// unsubscribe to it.
const subscription = await registration.pushManager.getSubscription();
if (subscription) subscription.unsubscribe();
});

View file

@ -19,6 +19,7 @@
<script lang="ts" setup>
import { defineAsyncComponent, Ref, ref } from 'vue';
import { swInject } from './sw-inject';
import { instance } from '@/instance';
import { popup as showPopup, popups, pendingApiRequestsCount } from '@/os';
import { uploads } from '@/scripts/upload';
import * as sound from '@/scripts/sound';
@ -32,14 +33,12 @@ const dev: Ref<boolean> = ref(_DEV_);
const onNotification = (notification: { type: string; id: any; }): void => {
if ($i?.mutingNotificationTypes.includes(notification.type)) return;
if (document.visibilityState === 'visible') {
stream.send('readNotification', {
id: notification.id,
// if push notifications are enabled there is no need to pass the notification along
if (!instance.enableServiceWorker) {
// service worker is not enabled or set up on the server, pass the notification along
navigator.serviceWorker.ready.then(registration => {
registration.active.postMessage({ type: 'notification', body: notification });
});
showPopup(defineAsyncComponent(() => import('@/components/notification-toast.vue')), {
notification,
}, {}, 'closed');
}
sound.play('notification');

View file

@ -24,19 +24,13 @@ self.addEventListener('activate', ev => {
});
self.addEventListener('push', ev => {
// クライアント取得
ev.waitUntil(self.clients.matchAll({
includeUncontrolled: true,
type: 'window'
}).then(async <K extends keyof pushNotificationDataMap>(clients: readonly WindowClient[]) => {
ev.waitUntil((async <K extends keyof pushNotificationDataMap>() => {
const data: pushNotificationDataMap[K] = ev.data?.json();
switch (data.type) {
// case 'driveFileCreated':
case 'notification':
case 'unreadMessagingMessage':
// クライアントがあったらストリームに接続しているということなので通知しない
if (clients.length != 0) return;
return createNotification(data);
case 'readAllNotifications':
for (const n of await self.registration.getNotifications()) {
@ -67,7 +61,7 @@ self.addEventListener('push', ev => {
}
break;
}
}));
})());
});
self.addEventListener('notificationclick', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => {
@ -167,24 +161,14 @@ self.addEventListener('notificationclose', <K extends keyof pushNotificationData
self.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => {
ev.waitUntil((async () => {
switch (ev.data) {
case 'clear':
// Cache Storage全削除
await caches.keys()
.then(cacheNames => Promise.all(
cacheNames.map(name => caches.delete(name))
));
return; // TODO
}
if (typeof ev.data === 'object') {
// E.g. '[object Array]' → 'array'
const otype = Object.prototype.toString.call(ev.data).slice(8, -1).toLowerCase();
if (otype === 'object') {
if (ev.data.msg === 'initialize') {
switch (ev.data.type) {
case 'initialize':
swLang.setLang(ev.data.lang);
}
break;
case 'notification':
createNotification(ev.data);
break;
}
}
})());