From 5ba598c78f8ceb2febda2d95b4e22e96d2eb8323 Mon Sep 17 00:00:00 2001 From: Hank Date: Thu, 28 May 2026 18:29:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=AE=A4=E9=9B=99=E5=90=91=E5=8D=B3=E6=99=82=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=A4=B1=E6=95=88=E5=95=8F=E9=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - echo.js:還原 disconnect()/connect(),確保 Reverb 重連後重新授權 presence channel - BookingChat.vue:移除 handleNotificationFallback(會覆蓋樂觀更新訊息導致重複) - BookingChat.vue:改用 state_change 事件偵測重連('reconnected' 並非合法 Pusher.js 事件) - BookingChat.vue:移除未使用的 useAuthStore / useCoachAuthStore / currentUserId 依賴 Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/BookingChat.vue | 37 +++++++------------------ frontend/src/plugins/echo.js | 6 ++-- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/frontend/src/components/BookingChat.vue b/frontend/src/components/BookingChat.vue index 5d6ccaa..569e8c8 100644 --- a/frontend/src/components/BookingChat.vue +++ b/frontend/src/components/BookingChat.vue @@ -4,8 +4,6 @@ import api from '../api/axios' import coachApi from '../api/coachAxios' import echo from '../plugins/echo' import { useNotificationStore } from '../stores/notifications' -import { useAuthStore } from '../stores/auth' -import { useCoachAuthStore } from '../stores/coachAuth' const props = defineProps({ bookingId: { type: Number, required: true }, @@ -23,12 +21,6 @@ const otherUserOnline = ref(false) const channel = ref(null) const notificationStore = useNotificationStore() -const authStore = useAuthStore() -const coachAuthStore = useCoachAuthStore() - -const currentUserId = computed(() => - props.currentUserType === 'provider' ? coachAuthStore.user?.id : authStore.user?.id -) const isConfirmed = computed(() => props.bookingStatus === 'confirmed') const isCompleted = computed(() => props.bookingStatus === 'completed') @@ -103,7 +95,7 @@ async function sendText() { const tempId = `_temp_${Date.now()}` messages.value.push({ id: tempId, - sender_id: currentUserId.value, + sender_id: null, sender_type: props.currentUserType, type: 'text', content, @@ -201,39 +193,30 @@ function subscribeChannel() { }) } -function handleReconnected() { +// updateEchoToken() 會 disconnect/connect,connection 重建後需重訂 presence channel +// Pusher.js 的重連事件走 state_change,用 previous 判斷是否為重連而非初次連線 +let everConnected = false +function onConnectionStateChange({ current }) { + if (current !== 'connected') return + if (!everConnected) { everConnected = true; return } + // 重連:重訂 presence channel if (!isConfirmed.value) return echo.leave(`booking.${props.bookingId}`) channel.value = null subscribeChannel() } -// Fallback:presence channel 失效時,收到 bell 通知也補拉訊息 -async function handleNotificationFallback() { - const lastId = messages.value[messages.value.length - 1]?.id - if (lastId && typeof lastId === 'string') return // 還在樂觀更新中,不拉 - await loadHistory() -} - onMounted(async () => { await requestBrowserNotificationPermission() await loadHistory() if (isConfirmed.value) { subscribeChannel() - echo.connector.pusher.connection.bind('reconnected', handleReconnected) - if (currentUserId.value) { - echo.private(`App.Models.User.${currentUserId.value}`) - .listen('.notification.created', handleNotificationFallback) - } + echo.connector.pusher.connection.bind('state_change', onConnectionStateChange) } }) onUnmounted(() => { - echo.connector.pusher.connection.unbind('reconnected', handleReconnected) - if (currentUserId.value) { - echo.private(`App.Models.User.${currentUserId.value}`) - .stopListening('.notification.created', handleNotificationFallback) - } + echo.connector.pusher.connection.unbind('state_change', onConnectionStateChange) if (channel.value) { channel.value.whisper('presence', { user_type: props.currentUserType, online: false }) echo.leave(`booking.${props.bookingId}`) diff --git a/frontend/src/plugins/echo.js b/frontend/src/plugins/echo.js index 3a06c56..adba795 100644 --- a/frontend/src/plugins/echo.js +++ b/frontend/src/plugins/echo.js @@ -24,16 +24,16 @@ const echo = new Echo({ }, }) -// 登入後更新 auth header,讓 presence channel 授權帶正確 token +// 登入後更新 auth header 並強制重連,確保 Reverb 用新 token 授權 channel export function updateEchoToken() { const token = getAuthToken() const bearer = `Bearer ${token}` echo.options.auth.headers.Authorization = bearer - // 同步更新 Pusher 內部 config(避免 Pusher.js 持有舊的 header copy) if (echo.connector?.pusher?.config?.auth?.headers) { echo.connector.pusher.config.auth.headers.Authorization = bearer } - // 不 disconnect/connect:避免中斷 BookingChat 已訂閱的 presence channel + echo.disconnect() + echo.connect() } export default echo -- 2.52.0