forked from FoundKeyGang/FoundKey
client: use native Notifications API (#234)
Reviewed-on: FoundKeyGang/FoundKey#234 Changelog: Changed
This commit is contained in:
commit
5b574d40f9
4 changed files with 20 additions and 104 deletions
|
@ -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>
|
|
|
@ -9,7 +9,7 @@ export async function initializeSw() {
|
||||||
navigator.serviceWorker.register('/sw.js', { scope: '/', type: 'classic' });
|
navigator.serviceWorker.register('/sw.js', { scope: '/', type: 'classic' });
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
navigator.serviceWorker.ready.then(registration => {
|
||||||
registration.active?.postMessage({
|
registration.active?.postMessage({
|
||||||
msg: 'initialize',
|
type: 'initialize',
|
||||||
lang,
|
lang,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -33,14 +33,14 @@ export async function initializeSw() {
|
||||||
})
|
})
|
||||||
// When subscribe failed
|
// When subscribe failed
|
||||||
.catch(async (err: Error) => {
|
.catch(async (err: Error) => {
|
||||||
// 通知が許可されていなかったとき
|
// when notifications were not authorized
|
||||||
if (err.name === 'NotAllowedError') {
|
if (err.name === 'NotAllowedError') {
|
||||||
return;
|
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();
|
const subscription = await registration.pushManager.getSubscription();
|
||||||
if (subscription) subscription.unsubscribe();
|
if (subscription) subscription.unsubscribe();
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, Ref, ref } from 'vue';
|
import { defineAsyncComponent, Ref, ref } from 'vue';
|
||||||
import { swInject } from './sw-inject';
|
import { swInject } from './sw-inject';
|
||||||
|
import { instance } from '@/instance';
|
||||||
import { popup as showPopup, popups, pendingApiRequestsCount } from '@/os';
|
import { popup as showPopup, popups, pendingApiRequestsCount } from '@/os';
|
||||||
import { uploads } from '@/scripts/upload';
|
import { uploads } from '@/scripts/upload';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
|
@ -32,14 +33,12 @@ const dev: Ref<boolean> = ref(_DEV_);
|
||||||
const onNotification = (notification: { type: string; id: any; }): void => {
|
const onNotification = (notification: { type: string; id: any; }): void => {
|
||||||
if ($i?.mutingNotificationTypes.includes(notification.type)) return;
|
if ($i?.mutingNotificationTypes.includes(notification.type)) return;
|
||||||
|
|
||||||
if (document.visibilityState === 'visible') {
|
// if push notifications are enabled there is no need to pass the notification along
|
||||||
stream.send('readNotification', {
|
if (!instance.enableServiceWorker) {
|
||||||
id: notification.id,
|
// 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');
|
sound.play('notification');
|
||||||
|
|
|
@ -24,19 +24,13 @@ self.addEventListener('activate', ev => {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('push', ev => {
|
self.addEventListener('push', ev => {
|
||||||
// クライアント取得
|
ev.waitUntil((async <K extends keyof pushNotificationDataMap>() => {
|
||||||
ev.waitUntil(self.clients.matchAll({
|
|
||||||
includeUncontrolled: true,
|
|
||||||
type: 'window'
|
|
||||||
}).then(async <K extends keyof pushNotificationDataMap>(clients: readonly WindowClient[]) => {
|
|
||||||
const data: pushNotificationDataMap[K] = ev.data?.json();
|
const data: pushNotificationDataMap[K] = ev.data?.json();
|
||||||
|
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
// case 'driveFileCreated':
|
// case 'driveFileCreated':
|
||||||
case 'notification':
|
case 'notification':
|
||||||
case 'unreadMessagingMessage':
|
case 'unreadMessagingMessage':
|
||||||
// クライアントがあったらストリームに接続しているということなので通知しない
|
|
||||||
if (clients.length != 0) return;
|
|
||||||
return createNotification(data);
|
return createNotification(data);
|
||||||
case 'readAllNotifications':
|
case 'readAllNotifications':
|
||||||
for (const n of await self.registration.getNotifications()) {
|
for (const n of await self.registration.getNotifications()) {
|
||||||
|
@ -67,7 +61,7 @@ self.addEventListener('push', ev => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}));
|
})());
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('notificationclick', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => {
|
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']) => {
|
self.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => {
|
||||||
ev.waitUntil((async () => {
|
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') {
|
if (typeof ev.data === 'object') {
|
||||||
// E.g. '[object Array]' → 'array'
|
switch (ev.data.type) {
|
||||||
const otype = Object.prototype.toString.call(ev.data).slice(8, -1).toLowerCase();
|
case 'initialize':
|
||||||
|
|
||||||
if (otype === 'object') {
|
|
||||||
if (ev.data.msg === 'initialize') {
|
|
||||||
swLang.setLang(ev.data.lang);
|
swLang.setLang(ev.data.lang);
|
||||||
}
|
break;
|
||||||
|
case 'notification':
|
||||||
|
createNotification(ev.data);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})());
|
})());
|
||||||
|
|
Loading…
Reference in a new issue