diff --git a/app/Services/GameBetBroadcastService.php b/app/Services/GameBetBroadcastService.php index 4e23bdd..c839988 100644 --- a/app/Services/GameBetBroadcastService.php +++ b/app/Services/GameBetBroadcastService.php @@ -94,6 +94,8 @@ class GameBetBroadcastService toastMessage: "{$username} 抢到 {$formattedAmount} {$typeLabel}礼包", toastIcon: '🧧', toastColor: $toastColor, + toastActorUsername: $username, + skipToastForActor: true, ); } @@ -109,7 +111,23 @@ class GameBetBroadcastService string $toastIcon, string $toastColor, string $action = '', + ?string $toastActorUsername = null, + bool $skipToastForActor = false, ): void { + $toastNotification = [ + 'title' => $toastTitle, + 'message' => $toastMessage, + 'icon' => $toastIcon, + 'color' => $toastColor, + 'duration' => 8000, + ]; + + if ($toastActorUsername !== null) { + // 记录触发人用于前端去重,避免本人同时看到本地到账提示和公屏领取提示。 + $toastNotification['actor_username'] = $toastActorUsername; + $toastNotification['skip_for_actor'] = $skipToastForActor; + } + $message = [ 'id' => $this->chatState->nextMessageId($roomId), 'room_id' => $roomId, @@ -120,13 +138,7 @@ class GameBetBroadcastService 'font_color' => $fontColor, 'action' => $action, 'sent_at' => now()->toDateTimeString(), - 'toast_notification' => [ - 'title' => $toastTitle, - 'message' => $toastMessage, - 'icon' => $toastIcon, - 'color' => $toastColor, - 'duration' => 8000, - ], + 'toast_notification' => $toastNotification, ]; // 下注通知必须进房间 Presence 频道,确保当前房间所有在线人员都能看到右下角提示。 diff --git a/resources/js/chat-room/chat-events.js b/resources/js/chat-room/chat-events.js index 5dab2f3..90d449b 100644 --- a/resources/js/chat-room/chat-events.js +++ b/resources/js/chat-room/chat-events.js @@ -70,6 +70,20 @@ function runWhenDomReady(callback) { callback(); } +/** + * 判断 Toast 通知是否需要对当前用户隐藏。 + * + * @param {Record} toastNotification 右下角通知载荷 + * @returns {boolean} + */ +function shouldSkipToastForCurrentUser(toastNotification) { + if (!toastNotification?.skip_for_actor) { + return false; + } + + return String(toastNotification.actor_username || "") === String(window.chatContext?.username || ""); +} + // ── 禁言逻辑 ── function handleMutedEvent(e) { const state = getState(); @@ -417,6 +431,10 @@ export function bindChatEvents() { // 若消息携带 toast_notification 字段且当前用户是接收者或为公屏广播或为欢迎动作,弹右下角小卡片 if (msg.toast_notification && (msg.to_user === window.chatContext?.username || msg.to_user === '大家' || msg.action === '欢迎')) { const t = msg.toast_notification; + if (shouldSkipToastForCurrentUser(t)) { + return; + } + window.chatToast?.show({ title: t.title || "通知", message: t.message || "", diff --git a/tests/Feature/RedPacketControllerTest.php b/tests/Feature/RedPacketControllerTest.php index 6952644..44c8d66 100644 --- a/tests/Feature/RedPacketControllerTest.php +++ b/tests/Feature/RedPacketControllerTest.php @@ -280,6 +280,8 @@ class RedPacketControllerTest extends TestCase $this->assertFalse((bool) ($publicMessage['is_secret'] ?? true)); $this->assertStringContainsString('金币礼包', (string) ($publicMessage['toast_notification']['message'] ?? '')); $this->assertSame('🧧', $publicMessage['toast_notification']['icon'] ?? null); + $this->assertSame($user->username, $publicMessage['toast_notification']['actor_username'] ?? null); + $this->assertTrue((bool) ($publicMessage['toast_notification']['skip_for_actor'] ?? false)); } /**