-
Notifications
You must be signed in to change notification settings - Fork 754
Lobby Notification System (Audible Ping on Match) #2666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ryanbarlow97
wants to merge
69
commits into
main
Choose a base branch
from
lobbynotify
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,654
−7
Open
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
ef96bfe
notification system
ryanbarlow97 caa98bb
prettier + eslint fix
ryanbarlow97 9008211
followed coderabbit suggestion
ryanbarlow97 99250cd
added more logs
ryanbarlow97 72a92b2
drop the hardcoded /w0/ path and connect via the host root instead
ryanbarlow97 d854204
drop the trailing slash
ryanbarlow97 dbe7a7c
coderabbit suggestions
ryanbarlow97 eeffa1d
now using 'notificationCleanupInterval'
ryanbarlow97 ba8cf6f
temp fix for localdev
ryanbarlow97 05bd7f5
prettier
ryanbarlow97 dd926f5
port 3000
ryanbarlow97 bd831da
persist throughout the session and handles registration/unregistratio…
ryanbarlow97 440e85d
create notification worker
ryanbarlow97 4c8fc4a
should trigger for back to back ffas/teams
ryanbarlow97 ee7f10e
Merge branch 'main' into lobbynotify
ryanbarlow97 c99095c
changed to en.json text
ryanbarlow97 1955532
added /w0 fallback
ryanbarlow97 b0f7de5
hijack polling already done instead of websockets
ryanbarlow97 a318a42
revert changes
ryanbarlow97 3a8572d
remove websocketness
ryanbarlow97 15a4675
revert change
ryanbarlow97 d62a231
spacing
ryanbarlow97 9e73769
spacing
ryanbarlow97 d76201d
remove blank line
ryanbarlow97 52d0af8
remove clientregisternotifications
ryanbarlow97 cb21fcf
removed schema stuff
ryanbarlow97 d20f828
replaced with en.json
ryanbarlow97 deabb94
updated to use lang
ryanbarlow97 a1c295d
remove popup
ryanbarlow97 bac4b56
remove unused import
ryanbarlow97 8e3a2ba
remove unused
ryanbarlow97 a8cf332
reuse some language
ryanbarlow97 8c0f8bb
stripped back as we dont need to track
ryanbarlow97 bba8da0
prettier fix
ryanbarlow97 c98a8c2
Merge branch 'main' into lobbynotify
ryanbarlow97 62328e2
prettier fix
ryanbarlow97 92fd8f2
added tests
ryanbarlow97 76def68
fixed prettier+eslint
ryanbarlow97 8447c43
calling this.lobbyNotificationManager?.destroy() in the beforeunload …
ryanbarlow97 001bccd
fixed whitespace issue
ryanbarlow97 bcd2eb3
Extracting to a shared constant
ryanbarlow97 bd0f9c0
comment
ryanbarlow97 02bef33
Remove duplicate beforeunload listener
ryanbarlow97 12ff2d4
Use bg-transparent instead
ryanbarlow97 9f09677
add back gamestop
ryanbarlow97 faa0f86
relocate gamestop
ryanbarlow97 c2398af
Use border-0 instead.
ryanbarlow97 91b7dfa
change setting types
ryanbarlow97 d62f24a
update modal
ryanbarlow97 cbb90b3
Update tests/client/LobbyNotificationManager.test.ts
ryanbarlow97 b3dbb59
added humans v nations
ryanbarlow97 d63797b
updated tests
ryanbarlow97 fa152ec
updated test
ryanbarlow97 71686c6
Update tests/client/LobbyNotificationManager.test.ts
ryanbarlow97 30096c6
Merge branch 'main' into lobbynotify
ryanbarlow97 bf42b40
Merge branch 'main' into lobbynotify
ryanbarlow97 1c1ac04
Merge remote-tracking branch 'origin/main' into lobbynotify
ryanbarlow97 ffea152
migrated tests away from jest
ryanbarlow97 3bc1bd6
Merge branch 'main' into lobbynotify
ryanbarlow97 56b54a3
Merge branch 'main' into lobbynotify
ryanbarlow97 feda16a
fix handleBeforeUnload issue
ryanbarlow97 a962dcc
Merge branch 'main' into lobbynotify
ryanbarlow97 9e0a430
Merge branch 'main' into lobbynotify
ryanbarlow97 46abaae
Merge branch 'main' into lobbynotify
ryanbarlow97 e74cb12
conflict resolution
ryanbarlow97 ee2b2ce
Merge branch 'main' into lobbynotify
ryanbarlow97 1f42c80
conflict resolution
ryanbarlow97 b40dd86
Merge branch 'main' into lobbynotify
ryanbarlow97 2d31581
Merge branch 'main' into lobbynotify
iiamlewis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import { LitElement, html } from "lit"; | ||
| import { customElement } from "lit/decorators.js"; | ||
| import { translateText } from "./Utils"; | ||
|
|
||
| @customElement("lobby-notification-button") | ||
| export class LobbyNotificationButton extends LitElement { | ||
| createRenderRoot() { | ||
| return this; | ||
| } | ||
|
|
||
| private openModal() { | ||
| const event = new CustomEvent("open-notification-modal", { | ||
| bubbles: true, | ||
| composed: true, | ||
| }); | ||
| window.dispatchEvent(event); | ||
| } | ||
|
|
||
| render() { | ||
| return html` | ||
| <button | ||
| title="${translateText("lobby_notification_modal.title")}" | ||
| class="absolute top-0 left-[50px] md:top-[10px] md:left-[60px] border-0 bg-transparent cursor-pointer text-2xl" | ||
| @click=${this.openModal} | ||
| > | ||
| 🔔 | ||
| </button> | ||
| `; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| import { GameConfig, GameInfo } from "../core/Schemas"; | ||
| import { GameMode } from "../core/game/Game"; | ||
|
|
||
| interface NotificationSettings { | ||
| ffaEnabled: boolean; | ||
| teamEnabled: boolean; | ||
| soundEnabled: boolean; | ||
| minTeamCount: number; | ||
| maxTeamCount: number; | ||
| } | ||
|
|
||
| export class LobbyNotificationManager { | ||
| private settings: NotificationSettings | null = null; | ||
| private audioContext: AudioContext | null = null; | ||
| private seenLobbies: Set<string> = new Set(); | ||
|
|
||
| constructor() { | ||
| this.loadSettings(); | ||
| this.setupEventListeners(); | ||
| } | ||
|
|
||
| private setupEventListeners() { | ||
| window.addEventListener( | ||
| "notification-settings-changed", | ||
| this.handleSettingsChanged, | ||
| ); | ||
| window.addEventListener("lobbies-updated", this.handleLobbiesUpdated); | ||
| } | ||
|
|
||
| private handleSettingsChanged = (e: Event) => { | ||
| const event = e as CustomEvent<NotificationSettings>; | ||
| this.settings = event.detail; | ||
| }; | ||
|
|
||
| private loadSettings() { | ||
| try { | ||
| const saved = localStorage.getItem("lobbyNotificationSettings"); | ||
| if (saved) { | ||
| this.settings = JSON.parse(saved); | ||
| } | ||
| } catch (error) { | ||
| console.error("Failed to load notification settings:", error); | ||
| } | ||
| } | ||
|
|
||
| private handleLobbiesUpdated = (e: Event) => { | ||
| const event = e as CustomEvent<GameInfo[]>; | ||
| const lobbies = event.detail || []; | ||
|
|
||
| // Check for new lobbies | ||
| lobbies.forEach((lobby) => { | ||
| if (!this.seenLobbies.has(lobby.gameID) && lobby.gameConfig) { | ||
| this.seenLobbies.add(lobby.gameID); | ||
|
|
||
| // Check if this lobby matches user preferences | ||
| if (this.matchesPreferences(lobby.gameConfig)) { | ||
| this.playNotificationSound(); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| // Clean up old lobbies no longer in the list | ||
| const currentIds = new Set(lobbies.map((l) => l.gameID)); | ||
| for (const id of this.seenLobbies) { | ||
| if (!currentIds.has(id)) { | ||
| this.seenLobbies.delete(id); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| private matchesPreferences(config: GameConfig): boolean { | ||
| if (!this.settings) return false; | ||
|
|
||
| // Check FFA | ||
| if (this.settings.ffaEnabled && config.gameMode === GameMode.FFA) { | ||
| return true; | ||
| } | ||
|
|
||
| // Check Team | ||
| if (this.settings.teamEnabled && config.gameMode === GameMode.Team) { | ||
| const maxPlayers = config.maxPlayers ?? 0; | ||
| const playerTeams = config.playerTeams; | ||
|
|
||
| if (maxPlayers === 0) return false; | ||
|
|
||
| // Map fixed modes to players per team | ||
| const playersPerTeamMap: Record<string, number> = { | ||
| Duos: 2, | ||
| Trios: 3, | ||
| Quads: 4, | ||
| "Humans Vs Nations": 1, | ||
| }; | ||
|
|
||
| // Calculate players per team | ||
| // If playerTeams is a string (Duos/Trios/Quads), use the map | ||
| // If it's a number, it's the number of teams, so calculate players per team | ||
| const playersPerTeam = | ||
| typeof playerTeams === "string" | ||
| ? (playersPerTeamMap[playerTeams] ?? 0) | ||
| : typeof playerTeams === "number" | ||
| ? Math.floor(maxPlayers / playerTeams) | ||
| : 0; | ||
|
|
||
| if (playersPerTeam === 0) return false; | ||
|
|
||
| if (playersPerTeam < this.settings.minTeamCount) return false; | ||
| if (playersPerTeam > this.settings.maxTeamCount) return false; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private playNotificationSound() { | ||
| if (!this.settings?.soundEnabled) { | ||
| return; | ||
| } | ||
|
|
||
| this.playBeepSound(); | ||
| } | ||
|
|
||
| private getAudioContext(): AudioContext | null { | ||
| if (this.audioContext) { | ||
| return this.audioContext; | ||
| } | ||
|
|
||
| try { | ||
| this.audioContext = new (window.AudioContext || | ||
| (window as any).webkitAudioContext)(); | ||
| return this.audioContext; | ||
| } catch (error) { | ||
| console.error("Failed to create AudioContext:", error); | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| private playBeepSound() { | ||
| try { | ||
| const audioContext = this.getAudioContext(); | ||
| if (!audioContext) return; | ||
|
|
||
| const oscillator = audioContext.createOscillator(); | ||
| const gainNode = audioContext.createGain(); | ||
|
|
||
| oscillator.connect(gainNode); | ||
| gainNode.connect(audioContext.destination); | ||
|
|
||
| oscillator.frequency.value = 800; | ||
| oscillator.type = "sine"; | ||
|
|
||
| gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); | ||
| gainNode.gain.exponentialRampToValueAtTime( | ||
| 0.01, | ||
| audioContext.currentTime + 0.3, | ||
| ); | ||
|
|
||
| oscillator.start(audioContext.currentTime); | ||
| oscillator.stop(audioContext.currentTime + 0.3); | ||
| } catch (error) { | ||
| console.error("Failed to play beep sound:", error); | ||
| } | ||
| } | ||
|
|
||
| public destroy() { | ||
| if (this.audioContext) { | ||
| this.audioContext.close(); | ||
| this.audioContext = null; | ||
| } | ||
| window.removeEventListener( | ||
| "notification-settings-changed", | ||
| this.handleSettingsChanged, | ||
| ); | ||
| window.removeEventListener("lobbies-updated", this.handleLobbiesUpdated); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.