diff --git a/app/Broadcasting/BookingPresenceChannel.php b/app/Broadcasting/BookingPresenceChannel.php index 5238301..ede36cc 100644 --- a/app/Broadcasting/BookingPresenceChannel.php +++ b/app/Broadcasting/BookingPresenceChannel.php @@ -16,7 +16,9 @@ class BookingPresenceChannel $booking->loadMissing('schedule'); $isMember = $user->role === 'member' && $booking->member_id === $user->id; - $isProvider = $user->role === 'provider' && $booking->schedule->provider_id === $user->id; + $isProvider = $user->role === 'provider' + && $booking->schedule !== null + && $booking->schedule->provider_id === $user->id; if (!$isMember && !$isProvider) { return false; diff --git a/frontend/src/components/BookingChat.vue b/frontend/src/components/BookingChat.vue index f6b6437..b6ce904 100644 --- a/frontend/src/components/BookingChat.vue +++ b/frontend/src/components/BookingChat.vue @@ -170,15 +170,24 @@ function subscribeChannel() { }) } +function handleReconnected() { + if (!isConfirmed.value) return + echo.leave(`booking.${props.bookingId}`) + channel.value = null + subscribeChannel() +} + onMounted(async () => { await requestBrowserNotificationPermission() await loadHistory() if (isConfirmed.value) { subscribeChannel() + echo.connector.pusher.connection.bind('reconnected', handleReconnected) } }) onUnmounted(() => { + echo.connector.pusher.connection.unbind('reconnected', handleReconnected) 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 969a2b6..3a06c56 100644 --- a/frontend/src/plugins/echo.js +++ b/frontend/src/plugins/echo.js @@ -27,10 +27,13 @@ const echo = new Echo({ // 登入後更新 auth header,讓 presence channel 授權帶正確 token export function updateEchoToken() { const token = getAuthToken() - echo.options.auth.headers.Authorization = `Bearer ${token}` - // 重新連線讓新 token 生效 - echo.disconnect() - echo.connect() + 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 } export default echo diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js index 35e4fc7..5079603 100644 --- a/frontend/src/stores/auth.js +++ b/frontend/src/stores/auth.js @@ -29,8 +29,8 @@ export const useAuthStore = defineStore('auth', () => { localStorage.setItem('user', JSON.stringify(userData)) const ns = useNotificationStore() ns.startPolling() - ns.startRealtime(userData.id) updateEchoToken() + ns.startRealtime(userData.id) } async function logout() { diff --git a/frontend/src/stores/coachAuth.js b/frontend/src/stores/coachAuth.js index dc99e3b..1f1dcad 100644 --- a/frontend/src/stores/coachAuth.js +++ b/frontend/src/stores/coachAuth.js @@ -29,8 +29,8 @@ export const useCoachAuthStore = defineStore('coachAuth', () => { localStorage.setItem('coach_user', JSON.stringify(userData)) const ns = useNotificationStore() ns.startPolling() - ns.startRealtime(userData.id) updateEchoToken() + ns.startRealtime(userData.id) } async function logout() {