|
|
@@ -5,11 +5,32 @@ import { getClients } from '@/api/client'
|
|
|
import type { PortState, StatusMessage } from '@/types'
|
|
|
import PortStatusCard from '@/components/PortStatusCard.vue'
|
|
|
|
|
|
-const { connected, onMessage } = useWebSocket(`${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/ws`)
|
|
|
+const { connected, onMessage, onDisconnect } = useWebSocket(`${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/ws`)
|
|
|
|
|
|
const ports = reactive<Map<number, PortState>>(new Map())
|
|
|
|
|
|
+let notified = new Set<number>()
|
|
|
+
|
|
|
+function sendNotification(port: number, status: string) {
|
|
|
+ if (!('Notification' in window)) return
|
|
|
+ if (Notification.permission === 'granted') {
|
|
|
+ new Notification(`端口 ${port}`, { body: status })
|
|
|
+ } else if (Notification.permission === 'default') {
|
|
|
+ Notification.requestPermission().then((perm) => {
|
|
|
+ if (perm === 'granted') new Notification(`端口 ${port}`, { body: status })
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const removeListener = onMessage((msg: StatusMessage) => {
|
|
|
+ const prev = ports.get(msg.port)
|
|
|
+ if (prev && msg.code === 'session_completed' && prev.code !== 'session_completed' && !notified.has(msg.port)) {
|
|
|
+ notified.add(msg.port)
|
|
|
+ sendNotification(msg.port, msg.status)
|
|
|
+ }
|
|
|
+ if (msg.code === 'idle') {
|
|
|
+ notified.delete(msg.port)
|
|
|
+ }
|
|
|
ports.set(msg.port, {
|
|
|
port: msg.port,
|
|
|
status: msg.status,
|
|
|
@@ -31,6 +52,10 @@ onMounted(async () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
+const removeDisconnectListener = onDisconnect(() => {
|
|
|
+ ports.clear()
|
|
|
+})
|
|
|
+
|
|
|
const portList = computed(() =>
|
|
|
Array.from(ports.values()).sort((a, b) => a.port - b.port),
|
|
|
)
|
|
|
@@ -46,6 +71,7 @@ const errorCount = computed(() => portList.value.filter((p) => p.code === 'error
|
|
|
const permissionCount = computed(() => portList.value.filter((p) => p.code === 'permission').length)
|
|
|
|
|
|
onUnmounted(removeListener)
|
|
|
+onUnmounted(removeDisconnectListener)
|
|
|
</script>
|
|
|
|
|
|
<template>
|