diff --git a/src/assets/svg/speaker-slash.svg b/src/assets/svg/speaker-slash.svg new file mode 100644 index 0000000..4a09e61 --- /dev/null +++ b/src/assets/svg/speaker-slash.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/modules/game-bar/action-microphone.ts b/src/modules/game-bar/action-microphone.ts index 3010749..e4c5bba 100644 --- a/src/modules/game-bar/action-microphone.ts +++ b/src/modules/game-bar/action-microphone.ts @@ -1,7 +1,6 @@ import { BxEvent } from "@utils/bx-event"; import { BxIcon } from "@utils/bx-icon"; import { createButton, ButtonStyle, CE } from "@utils/html"; -import { t } from "@utils/translation"; import { BaseGameBarAction } from "./action-base"; import { MicrophoneShortcut, MicrophoneState } from "../shortcuts/shortcut-microphone"; @@ -24,7 +23,6 @@ export class MicrophoneAction extends BaseGameBarAction { const $btnDefault = createButton({ style: ButtonStyle.GHOST, icon: BxIcon.MICROPHONE, - title: t('show-touch-controller'), onClick: onClick, classes: ['bx-activated'], }); @@ -32,7 +30,6 @@ export class MicrophoneAction extends BaseGameBarAction { const $btnMuted = createButton({ style: ButtonStyle.GHOST, icon: BxIcon.MICROPHONE_MUTED, - title: t('hide-touch-controller'), onClick: onClick, }); diff --git a/src/modules/game-bar/action-speaker.ts b/src/modules/game-bar/action-speaker.ts new file mode 100644 index 0000000..bc3bd69 --- /dev/null +++ b/src/modules/game-bar/action-speaker.ts @@ -0,0 +1,54 @@ +import { BxEvent } from "@utils/bx-event"; +import { BxIcon } from "@utils/bx-icon"; +import { createButton, ButtonStyle, CE } from "@utils/html"; +import { BaseGameBarAction } from "./action-base"; +import { SoundShortcut, SpeakerState } from "../shortcuts/shortcut-sound"; + + +export class SpeakerAction extends BaseGameBarAction { + $content: HTMLElement; + + constructor() { + super(); + + const onClick = (e: Event) => { + BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED); + SoundShortcut.muteUnmute(); + }; + + const $btnEnable = createButton({ + style: ButtonStyle.GHOST, + icon: BxIcon.AUDIO, + onClick: onClick, + }); + + const $btnMuted = createButton({ + style: ButtonStyle.GHOST, + icon: BxIcon.SPEAKER_MUTED, + onClick: onClick, + classes: ['bx-activated'], + }); + + this.$content = CE('div', {}, + $btnEnable, + $btnMuted, + ); + + this.reset(); + + window.addEventListener(BxEvent.SPEAKER_STATE_CHANGED, e => { + const speakerState = (e as any).speakerState; + const enabled = speakerState === SpeakerState.ENABLED; + + this.$content.dataset.enabled = enabled.toString(); + }); + } + + render(): HTMLElement { + return this.$content; + } + + reset(): void { + this.$content.dataset.enabled = 'true'; + } +} diff --git a/src/modules/game-bar/action-touch-control.ts b/src/modules/game-bar/action-touch-control.ts index 69db89a..0640865 100644 --- a/src/modules/game-bar/action-touch-control.ts +++ b/src/modules/game-bar/action-touch-control.ts @@ -26,7 +26,6 @@ export class TouchControlAction extends BaseGameBarAction { icon: BxIcon.TOUCH_CONTROL_ENABLE, title: t('show-touch-controller'), onClick: onClick, - classes: ['bx-activated'], }); const $btnDisable = createButton({ @@ -34,6 +33,7 @@ export class TouchControlAction extends BaseGameBarAction { icon: BxIcon.TOUCH_CONTROL_DISABLE, title: t('hide-touch-controller'), onClick: onClick, + classes: ['bx-activated'], }); this.$content = CE('div', {}, diff --git a/src/modules/game-bar/game-bar.ts b/src/modules/game-bar/game-bar.ts index 5a966b1..3d4710a 100644 --- a/src/modules/game-bar/game-bar.ts +++ b/src/modules/game-bar/game-bar.ts @@ -9,6 +9,7 @@ import { MicrophoneAction } from "./action-microphone"; import { PrefKey } from "@/enums/pref-keys"; import { getPref, StreamTouchController } from "@/utils/settings-storages/global-settings-storage"; import { TrueAchievementsAction } from "./action-true-achievements"; +import { SpeakerAction } from "./action-speaker"; export class GameBar { @@ -43,8 +44,9 @@ export class GameBar { this.actions = [ new ScreenshotAction(), ...(STATES.userAgent.capabilities.touch && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== StreamTouchController.OFF) ? [new TouchControlAction()] : []), - new TrueAchievementsAction(), + new SpeakerAction(), new MicrophoneAction(), + new TrueAchievementsAction(), ]; // Reverse the action list if Game Bar's position is on the right side diff --git a/src/modules/shortcuts/shortcut-sound.ts b/src/modules/shortcuts/shortcut-sound.ts index 98a00b9..2b94888 100644 --- a/src/modules/shortcuts/shortcut-sound.ts +++ b/src/modules/shortcuts/shortcut-sound.ts @@ -4,6 +4,12 @@ import { Toast } from "@utils/toast"; import { ceilToNearest, floorToNearest } from "@/utils/utils"; import { PrefKey } from "@/enums/pref-keys"; import { getPref, setPref } from "@/utils/settings-storages/global-settings-storage"; +import { BxEvent } from "@/utils/bx-event"; + +export enum SpeakerState { + ENABLED, + MUTED, +} export class SoundShortcut { static adjustGainNodeVolume(amount: number): number { @@ -64,6 +70,10 @@ export class SoundShortcut { SoundShortcut.setGainNodeVolume(targetValue); Toast.show(`${t('stream')} ❯ ${t('volume')}`, status, {instant: true}); + + BxEvent.dispatch(window, BxEvent.SPEAKER_STATE_CHANGED, { + speakerState: targetValue === 0 ? SpeakerState.MUTED : SpeakerState.ENABLED, + }) return; } @@ -79,6 +89,10 @@ export class SoundShortcut { const status = $media.muted ? t('muted') : t('unmuted'); Toast.show(`${t('stream')} ❯ ${t('volume')}`, status, {instant: true}); + + BxEvent.dispatch(window, BxEvent.SPEAKER_STATE_CHANGED, { + speakerState: $media.muted ? SpeakerState.MUTED : SpeakerState.ENABLED, + }) } } } diff --git a/src/utils/bx-event.ts b/src/utils/bx-event.ts index 5b4d2d8..2a5c909 100644 --- a/src/utils/bx-event.ts +++ b/src/utils/bx-event.ts @@ -37,6 +37,7 @@ export namespace BxEvent { export const GAME_BAR_ACTION_ACTIVATED = 'bx-game-bar-action-activated'; export const MICROPHONE_STATE_CHANGED = 'bx-microphone-state-changed'; + export const SPEAKER_STATE_CHANGED = 'bx-speaker-state-changed'; export const CAPTURE_SCREENSHOT = 'bx-capture-screenshot'; diff --git a/src/utils/bx-icon.ts b/src/utils/bx-icon.ts index b115a68..495d0ac 100644 --- a/src/utils/bx-icon.ts +++ b/src/utils/bx-icon.ts @@ -14,6 +14,7 @@ import iconPower from "@assets/svg/power.svg" with { type: "text" }; import iconQuestion from "@assets/svg/question.svg" with { type: "text" }; import iconRefresh from "@assets/svg/refresh.svg" with { type: "text" }; import iconRemotePlay from "@assets/svg/remote-play.svg" with { type: "text" }; +import iconSpeakerSlash from "@assets/svg/speaker-slash.svg" with { type: "text" }; import iconStreamSettings from "@assets/svg/stream-settings.svg" with { type: "text" }; import iconStreamStats from "@assets/svg/stream-stats.svg" with { type: "text" }; import iconTouchControlDisable from "@assets/svg/touch-control-disable.svg" with { type: "text" }; @@ -64,6 +65,7 @@ export const BxIcon = { CARET_LEFT: iconCaretLeft, CARET_RIGHT: iconCaretRight, SCREENSHOT: iconCamera, + SPEAKER_MUTED: iconSpeakerSlash, TOUCH_CONTROL_ENABLE: iconTouchControlEnable, TOUCH_CONTROL_DISABLE: iconTouchControlDisable,