restyle connection banner as floating pill and fix focus reconnect

This commit is contained in:
Abhinav Raut
2026-08-07 23:57:25 +05:30
parent b5f414471c
commit 2552c8ef53
3 changed files with 35 additions and 28 deletions
@@ -1,35 +1,51 @@
<template>
<BaseBanner
v-if="!isOnline"
:text="$t('globals.messages.noInternetConnection')"
color-class="bg-warning text-warning-foreground"
/>
<BaseBanner
v-else-if="connectionFailed"
:text="$t('globals.messages.connectionFailedRefresh')"
color-class="bg-destructive text-destructive-foreground"
/>
<BaseBanner
v-else-if="showReconnecting"
:text="$t('globals.messages.connecting')"
color-class="bg-warning text-warning-foreground"
/>
<div
v-if="banner"
role="status"
aria-live="polite"
aria-atomic="true"
class="fixed top-3 left-1/2 -translate-x-1/2 z-50 flex items-center gap-2 rounded-full px-4 py-1.5 text-sm shadow-md"
:class="banner.colorClass"
>
<div class="w-2 h-2 rounded-full bg-current animate-pulse"></div>
{{ banner.text }}
</div>
</template>
<script setup>
import { ref, watch, onUnmounted } from 'vue'
import { ref, computed, watch, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useOnline } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { useConnectionStore } from '@/stores/connection'
import BaseBanner from '@shared-ui/components/BaseBanner.vue'
const RECONNECTING_DELAY_MS = 1500
const { t } = useI18n()
const isOnline = useOnline()
const { connecting, connectionFailed } = storeToRefs(useConnectionStore())
const showReconnecting = ref(false)
let reconnectingTimer = null
const banner = computed(() => {
if (!isOnline.value)
return {
text: t('globals.messages.noInternetConnection'),
colorClass: 'bg-warning text-warning-foreground'
}
if (connectionFailed.value)
return {
text: t('globals.messages.connectionFailedRefresh'),
colorClass: 'bg-destructive text-destructive-foreground'
}
if (showReconnecting.value)
return {
text: t('globals.messages.connecting'),
colorClass: 'bg-warning text-warning-foreground'
}
return null
})
// Most drops recover on the first retry, so hold the banner back instead of flashing it.
watch(connecting, (value) => {
clearTimeout(reconnectingTimer)
@@ -2,14 +2,9 @@ import { ref } from 'vue'
import { defineStore } from 'pinia'
export const useConnectionStore = defineStore('connection', () => {
const connected = ref(false)
const connecting = ref(false)
const connectionFailed = ref(false)
const setConnected = (value) => {
connected.value = value
}
const setConnecting = (value) => {
connecting.value = value
}
@@ -19,11 +14,9 @@ export const useConnectionStore = defineStore('connection', () => {
}
return {
connected,
connecting,
connectionFailed,
setConnected,
setConnecting,
setConnectionFailed
}
+2 -4
View File
@@ -52,7 +52,6 @@ export class WebSocketClient {
this.reconnectInterval = 1000
this.reconnectAttempts = 0
this.isReconnecting = false
this.connectionStore.setConnected(true)
this.connectionStore.setConnecting(false)
this.connectionStore.setConnectionFailed(false)
this.lastPong = Date.now()
@@ -155,7 +154,6 @@ export class WebSocketClient {
reconnect () {
if (this.isReconnecting) return
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
this.connectionStore.setConnected(false)
this.connectionStore.setConnecting(false)
this.connectionStore.setConnectionFailed(true)
return
@@ -164,7 +162,6 @@ export class WebSocketClient {
this.isReconnecting = true
this.reconnectAttempts++
this.connectionStore.setConnected(false)
this.connectionStore.setConnecting(true)
// The online listener resets the attempt counter, so a retry can follow a give up.
this.connectionStore.setConnectionFailed(false)
@@ -195,6 +192,8 @@ export class WebSocketClient {
window.addEventListener('focus', () => {
if (this.socket?.readyState !== WebSocket.OPEN) {
this.reconnectAttempts = 0
this.reconnectInterval = 1000
this.reconnect()
}
})
@@ -322,7 +321,6 @@ export class WebSocketClient {
close () {
this.manualClose = true
this.clearPing()
this.connectionStore.setConnected(false)
this.connectionStore.setConnecting(false)
this.connectionStore.setConnectionFailed(false)
if (this.socket) {