* Replace BxEvent.TITLE_INFO_READY with Event Bus

* Migrate more events

* Migrate stream events to event bus

* Migrate preset events

* Migrate more

* Fix dispatching "input" event twice in Number Stepper
This commit is contained in:
redphx
2024-12-08 17:55:44 +07:00
committed by GitHub
parent 160044c958
commit 79ebb1a817
22 changed files with 282 additions and 191 deletions

View File

@@ -7,23 +7,6 @@ export namespace BxEvent {
export const JUMP_BACK_IN_READY = 'bx-jump-back-in-ready';
export const POPSTATE = 'bx-popstate';
export const TITLE_INFO_READY = 'bx-title-info-ready';
export const SETTINGS_CHANGED = 'bx-settings-changed';
export const STREAM_LOADING = 'bx-stream-loading';
export const STREAM_STARTING = 'bx-stream-starting';
export const STREAM_STARTED = 'bx-stream-started';
export const STREAM_PLAYING = 'bx-stream-playing';
export const STREAM_STOPPED = 'bx-stream-stopped';
export const STREAM_ERROR_PAGE = 'bx-stream-error-page';
export const STREAM_WEBRTC_CONNECTED = 'bx-stream-webrtc-connected';
export const STREAM_WEBRTC_DISCONNECTED = 'bx-stream-webrtc-disconnected';
export const MKB_UPDATED = 'bx-mkb-updated';
export const KEYBOARD_SHORTCUTS_UPDATED = 'bx-keyboard-shortcuts-updated';
// export const STREAM_EVENT_TARGET_READY = 'bx-stream-event-target-ready';
export const STREAM_SESSION_READY = 'bx-stream-session-ready';
@@ -33,11 +16,7 @@ export namespace BxEvent {
export const REMOTE_PLAY_READY = 'bx-remote-play-ready';
export const REMOTE_PLAY_FAILED = 'bx-remote-play-failed';
export const XCLOUD_SERVERS_READY = 'bx-servers-ready';
export const XCLOUD_SERVERS_UNAVAILABLE = 'bx-servers-unavailable';
export const DATA_CHANNEL_CREATED = 'bx-data-channel-created';
export const DEVICE_VIBRATION_CHANGED = 'bx-device-vibration-changed';
export const GAME_BAR_ACTION_ACTIVATED = 'bx-game-bar-action-activated';
export const MICROPHONE_STATE_CHANGED = 'bx-microphone-state-changed';
@@ -51,8 +30,6 @@ export namespace BxEvent {
export const NAVIGATION_FOCUS_CHANGED = 'bx-nav-focus-changed';
export const GH_PAGES_FORCE_NATIVE_MKB_UPDATED = 'bx-gh-pages-force-native-mkb-updated';
// xCloud Dialog events
export const XCLOUD_DIALOG_SHOWN = 'bx-xcloud-dialog-shown';
export const XCLOUD_DIALOG_DISMISSED = 'bx-xcloud-dialog-dismissed';
@@ -86,7 +63,7 @@ export namespace BxEvent {
target.dispatchEvent(event);
AppInterface && AppInterface.onEvent(eventName);
BX_FLAGS.Debug && BxLogger.warning('BxEvent', 'dispatch', eventName, data)
BX_FLAGS.Debug && BxLogger.warning('BxEvent', 'dispatch', eventName, data);
}
}

View File

@@ -1,7 +1,6 @@
import { isFullVersion } from "@macros/build" with { type: "macro" };
import { ControllerShortcut } from "@/modules/controller-shortcut";
import { BxEvent } from "@utils/bx-event";
import { deepClone, STATES } from "@utils/global";
import { BxLogger } from "./bx-logger";
import { BX_FLAGS } from "./bx-flags";
@@ -12,6 +11,7 @@ import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
import { TouchController } from "@/modules/touch-controller";
import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values";
import { Patcher, type PatchPage } from "@/modules/patcher/patcher";
import { EventBus } from "./event-bus";
export enum SupportedInputType {
CONTROLLER = 'Controller',
@@ -139,7 +139,7 @@ export const BxExposed = {
// Save this info in STATES
STATES.currentStream.titleInfo = titleInfo;
BxEvent.dispatch(window, BxEvent.TITLE_INFO_READY);
EventBus.Script.emit('titleInfoReady', {});
return titleInfo;
},

81
src/utils/event-bus.ts Normal file
View File

@@ -0,0 +1,81 @@
import type { PrefKey, StorageKey } from "@/enums/pref-keys";
import { BX_FLAGS } from "./bx-flags";
import { BxLogger } from "./bx-logger";
type EventCallback<T = any> = (payload: T) => void;
type ScriptEvents = {
xcloudServerReady: {};
xcloudServerUnavailable: {};
titleInfoReady: {};
settingChanged: {
storageKey: StorageKey;
settingKey: PrefKey;
settingValue: any;
};
mkbSettingUpdated: {};
keyboardShortcutsUpdated: {};
deviceVibrationUpdated: {};
// GH pages
listForcedNativeMkbUpdated: {};
};
type StreamEvents = {
stateLoading: {};
stateStarting: {};
statePlaying: { $video?: HTMLVideoElement };
stateStopped: {};
stateError: {};
};
export class EventBus<TEvents extends Record<string, any>> {
private listeners: Map<keyof TEvents, Set<EventCallback<any>>> = new Map();
static readonly Script = new EventBus<ScriptEvents>();
static readonly Stream = new EventBus<StreamEvents>();
on<K extends keyof TEvents>(event: K, callback: EventCallback<TEvents[K]>): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event)!.add(callback);
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'on', event, callback);
}
off<K extends keyof TEvents>(event: K, callback: EventCallback<TEvents[K]> | null): void {
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'off', event, callback);
if (!callback) {
// Remove all listener callbacks
this.listeners.delete(event);
return;
}
const callbacks = this.listeners.get(event);
if (!callbacks) {
return;
}
callbacks.delete(callback);
if (callbacks.size === 0) {
this.listeners.delete(event);
}
}
offAll(): void {
this.listeners.clear();
}
emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void {
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'emit', event, payload);
const callbacks = this.listeners.get(event) || [];
for (const callback of callbacks) {
callback(payload);
}
}
}

View File

@@ -1,7 +1,7 @@
import { StorageKey } from "@/enums/pref-keys";
import { NATIVE_FETCH } from "./bx-flags";
import { BxLogger } from "./bx-logger";
import { BxEvent } from "./bx-event";
import { EventBus } from "./event-bus";
export type ForceNativeMkbResponse = {
@@ -53,7 +53,7 @@ export class GhPagesUtils {
if (json.$schemaVersion === supportedSchema) {
// Save to storage
window.localStorage.setItem(key, JSON.stringify(json));
BxEvent.dispatch(window, BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED);
EventBus.Script.emit('listForcedNativeMkbUpdated', {});
}
});

View File

@@ -3,14 +3,15 @@ import { LoadingScreen } from "@modules/loading-screen";
import { RemotePlayManager } from "@/modules/remote-play-manager";
import { HeaderSection } from "@/modules/ui/header";
import { NavigationDialogManager } from "@/modules/ui/dialog/navigation-dialog";
import { EventBus } from "./event-bus";
export function patchHistoryMethod(type: 'pushState' | 'replaceState') {
const orig = window.history[type];
return function(...args: any[]) {
BxEvent.dispatch(window, BxEvent.POPSTATE, {
arguments: args,
});
arguments: args,
});
// @ts-ignore
return orig.apply(this, arguments);
@@ -38,5 +39,5 @@ export function onHistoryChanged(e: PopStateEvent) {
LoadingScreen.reset();
window.setTimeout(HeaderSection.watchHeader, 2000);
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED);
EventBus.Stream.emit('stateStopped', {});
}

View File

@@ -7,6 +7,7 @@ import { PrefKey } from "@/enums/pref-keys";
import { getPref, getPrefDefinition } from "./settings-storages/global-settings-storage";
import { CodecProfile } from "@/enums/pref-values";
import type { SettingDefinition } from "@/types/setting-definition";
import { EventBus } from "./event-bus";
export function patchVideoApi() {
const PREF_SKIP_SPLASH_VIDEO = getPref(PrefKey.UI_SKIP_SPLASH_VIDEO);
@@ -27,9 +28,9 @@ export function patchVideoApi() {
} satisfies StreamPlayerOptions;
STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref(PrefKey.VIDEO_PLAYER_TYPE), playerOptions);
BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, {
$video: this,
});
EventBus.Stream.emit('statePlaying', {
$video: this,
})
}
const nativePlay = HTMLMediaElement.prototype.play;

View File

@@ -1,16 +1,16 @@
import type { PrefKey } from "@/enums/pref-keys";
import type { PrefKey, StorageKey } from "@/enums/pref-keys";
import type { NumberStepperParams, SettingAction, SettingDefinitions } from "@/types/setting-definition";
import { BxEvent } from "../bx-event";
import { t } from "../translation";
import { SCRIPT_VARIANT } from "../global";
import { EventBus } from "../event-bus";
export class BaseSettingsStore {
private storage: Storage;
private storageKey: string;
private storageKey: StorageKey;
private _settings: object | null;
private definitions: SettingDefinitions;
constructor(storageKey: string, definitions: SettingDefinitions) {
constructor(storageKey: StorageKey, definitions: SettingDefinitions) {
this.storage = window.localStorage;
this.storageKey = storageKey;
@@ -93,7 +93,7 @@ export class BaseSettingsStore {
this.settings[key] = this.validateValue('get', key, value);
this.saveSettings();
emitEvent && BxEvent.dispatch(window, BxEvent.SETTINGS_CHANGED, {
emitEvent && EventBus.Script.emit('settingChanged', {
storageKey: this.storageKey,
settingKey: key,
settingValue: value,

View File

@@ -12,7 +12,7 @@ import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerSty
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
import { GhPagesUtils } from "../gh-pages";
import { BxEvent } from "../bx-event";
import { EventBus } from "../event-bus";
function getSupportedCodecProfiles() {
@@ -432,7 +432,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
if (!setting.unsupported) {
(setting as any).multipleOptions = GhPagesUtils.getNativeMkbCustomList(true);
window.addEventListener(BxEvent.GH_PAGES_FORCE_NATIVE_MKB_UPDATED, e => {
EventBus.Script.on('listForcedNativeMkbUpdated', () => {
(setting as any).multipleOptions = GhPagesUtils.getNativeMkbCustomList();
});
}

View File

@@ -10,10 +10,10 @@ import { hasGamepad } from "./gamepad";
import { MkbMappingPresetsTable } from "./local-db/mkb-mapping-presets-table";
import type { GamepadKey } from "@/enums/gamepad";
import { MkbPresetKey, MouseConstant } from "@/enums/mkb";
import { BxEvent } from "./bx-event";
import { KeyboardShortcutDefaultId, KeyboardShortcutsTable } from "./local-db/keyboard-shortcuts-table";
import { ShortcutAction } from "@/enums/shortcut-actions";
import { KeyHelper } from "@/modules/mkb/key-helper";
import { EventBus } from "./event-bus";
export type StreamSettingsData = {
@@ -110,7 +110,7 @@ export class StreamSettings {
}
StreamSettings.settings.deviceVibrationIntensity = intensity;
BxEvent.dispatch(window, BxEvent.DEVICE_VIBRATION_CHANGED);
EventBus.Script.emit('deviceVibrationUpdated', {});
}
static async refreshMkbSettings() {
@@ -148,7 +148,7 @@ export class StreamSettings {
settings.mkbPreset = converted;
setPref(PrefKey.MKB_P1_MAPPING_PRESET_ID, orgPreset.id);
BxEvent.dispatch(window, BxEvent.MKB_UPDATED);
EventBus.Script.emit('mkbSettingUpdated', {});
}
static async refreshKeyboardShortcuts() {
@@ -159,7 +159,7 @@ export class StreamSettings {
settings.keyboardShortcuts = null;
setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId);
BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED);
EventBus.Script.emit('keyboardShortcutsUpdated', {});
return;
}
@@ -179,7 +179,7 @@ export class StreamSettings {
settings.keyboardShortcuts = converted;
setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, orgPreset.id);
BxEvent.dispatch(window, BxEvent.KEYBOARD_SHORTCUTS_UPDATED);
EventBus.Script.emit('keyboardShortcutsUpdated', {});
}
static async refreshAllSettings() {

View File

@@ -1,10 +1,10 @@
import { PrefKey } from "@/enums/pref-keys";
import { BxEvent } from "./bx-event";
import { STATES } from "./global";
import { humanFileSize, secondsToHm } from "./html";
import { getPref } from "./settings-storages/global-settings-storage";
import { BxLogger } from "./bx-logger";
import { StreamStat } from "@/enums/pref-values";
import { EventBus } from "./event-bus";
export type StreamStatGrade = '' | 'bad' | 'ok' | 'good';
@@ -310,9 +310,8 @@ export class StreamStatsCollector {
}
static setupEvents() {
window.addEventListener(BxEvent.STREAM_PLAYING, e => {
const statsCollector = StreamStatsCollector.getInstance();
statsCollector.reset();
EventBus.Stream.on('statePlaying', () => {
StreamStatsCollector.getInstance().reset();
});
}
}

View File

@@ -4,7 +4,6 @@ import { LoadingScreen } from "@modules/loading-screen";
import { RemotePlayManager } from "@/modules/remote-play-manager";
import { StreamBadges } from "@modules/stream/stream-badges";
import { TouchController } from "@modules/touch-controller";
import { BxEvent } from "./bx-event";
import { NATIVE_FETCH, BX_FLAGS } from "./bx-flags";
import { STATES } from "./global";
import { generateMsDeviceInfo, getOsNameFromResolution, patchIceCandidates } from "./network";
@@ -13,6 +12,7 @@ import { BypassServerIps } from "@/enums/bypass-servers";
import { PrefKey } from "@/enums/pref-keys";
import { getPref } from "./settings-storages/global-settings-storage";
import { NativeMkbMode, StreamResolution, TouchControllerMode } from "@/enums/pref-values";
import { EventBus } from "./event-bus";
export class XcloudInterceptor {
private static readonly SERVER_EXTRA_INFO: Record<string, [string, ServerContinent]> = {
@@ -52,7 +52,7 @@ export class XcloudInterceptor {
const response = await NATIVE_FETCH(request, init);
if (response.status !== 200) {
// Unsupported region
BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE);
EventBus.Script.emit('xcloudServerUnavailable', {});
return response;
}
@@ -89,7 +89,7 @@ export class XcloudInterceptor {
STATES.serverRegions[region.name] = Object.assign({}, region);
}
BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_READY);
EventBus.Script.emit('xcloudServerReady', {});
const preferredRegion = getPreferredServerRegion();
if (preferredRegion && preferredRegion in STATES.serverRegions) {
@@ -107,7 +107,7 @@ export class XcloudInterceptor {
}
private static async handlePlay(request: RequestInfo | URL, init?: RequestInit) {
BxEvent.dispatch(window, BxEvent.STREAM_LOADING);
EventBus.Stream.emit('stateLoading', {});
const PREF_STREAM_TARGET_RESOLUTION = getPref<StreamResolution>(PrefKey.STREAM_RESOLUTION);
const PREF_STREAM_PREFERRED_LOCALE = getPref<StreamPreferredLocale>(PrefKey.STREAM_PREFERRED_LOCALE);
@@ -189,7 +189,7 @@ export class XcloudInterceptor {
return response;
}
BxEvent.dispatch(window, BxEvent.STREAM_STARTING);
EventBus.Stream.emit('stateStarting', {});
const obj = JSON.parse(text);
let overrides = JSON.parse(obj.clientStreamingConfigOverrides || '{}') || {};

View File

@@ -9,6 +9,7 @@ import { getPref } from "./settings-storages/global-settings-storage";
import type { RemotePlayConsoleAddresses } from "@/types/network";
import { RemotePlayManager } from "@/modules/remote-play-manager";
import { StreamResolution, TouchControllerMode } from "@/enums/pref-values";
import { EventBus } from "./event-bus";
export class XhomeInterceptor {
private static consoleAddrs: RemotePlayConsoleAddresses = {};
@@ -35,7 +36,7 @@ export class XhomeInterceptor {
}
private static async handleConfiguration(request: Request | URL) {
BxEvent.dispatch(window, BxEvent.STREAM_STARTING);
EventBus.Stream.emit('stateStarting', {});
const response = await NATIVE_FETCH(request);
const obj = await response.clone().json();
@@ -124,7 +125,7 @@ export class XhomeInterceptor {
}
private static async handlePlay(request: RequestInfo | URL) {
BxEvent.dispatch(window, BxEvent.STREAM_LOADING);
EventBus.Stream.emit('stateLoading', {});
const clone = (request as Request).clone();
const body = await clone.json();