mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-08-11 15:47:47 +02:00
Optimize + refactor code
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Screenshot } from "@utils/screenshot";
|
||||
import { ScreenshotManager } from "@/utils/screenshot-manager";
|
||||
import { GamepadKey } from "@enums/mkb";
|
||||
import { PrompFont } from "@enums/prompt-font";
|
||||
import { CE, removeChildElements } from "@utils/html";
|
||||
@@ -97,7 +97,7 @@ export class ControllerShortcut {
|
||||
break;
|
||||
|
||||
case ShortcutAction.STREAM_SCREENSHOT_CAPTURE:
|
||||
Screenshot.takeScreenshot();
|
||||
ScreenshotManager.getInstance().takeScreenshot();
|
||||
break;
|
||||
|
||||
case ShortcutAction.STREAM_STATS_TOGGLE:
|
||||
@@ -163,8 +163,6 @@ export class ControllerShortcut {
|
||||
|
||||
// Save to storage
|
||||
window.localStorage.setItem(ControllerShortcut.STORAGE_KEY, JSON.stringify(ControllerShortcut.ACTIONS));
|
||||
|
||||
console.log(ControllerShortcut.ACTIONS);
|
||||
}
|
||||
|
||||
private static updateProfileList(e?: GamepadEvent) {
|
||||
|
@@ -30,7 +30,7 @@ export class Dialog {
|
||||
} = options;
|
||||
|
||||
// Create dialog overlay
|
||||
const $overlay = document.querySelector('.bx-dialog-overlay') as HTMLElement;
|
||||
const $overlay = document.querySelector<HTMLElement>('.bx-dialog-overlay');
|
||||
|
||||
if (!$overlay) {
|
||||
this.$overlay = CE('div', {'class': 'bx-dialog-overlay bx-gone'});
|
||||
|
@@ -2,7 +2,7 @@ import { BxIcon } from "@utils/bx-icon";
|
||||
import { createButton, ButtonStyle } from "@utils/html";
|
||||
import { BaseGameBarAction } from "./action-base";
|
||||
import { t } from "@utils/translation";
|
||||
import { Screenshot } from "@/utils/screenshot";
|
||||
import { ScreenshotManager } from "@/utils/screenshot-manager";
|
||||
|
||||
export class ScreenshotAction extends BaseGameBarAction {
|
||||
$content: HTMLElement;
|
||||
@@ -20,6 +20,6 @@ export class ScreenshotAction extends BaseGameBarAction {
|
||||
|
||||
onClick(e: Event): void {
|
||||
super.onClick(e);
|
||||
Screenshot.takeScreenshot();
|
||||
ScreenshotManager.getInstance().takeScreenshot();
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,6 @@ export class TrueAchievementsAction extends BaseGameBarAction {
|
||||
|
||||
onClick(e: Event) {
|
||||
super.onClick(e);
|
||||
TrueAchievements.open(false);
|
||||
TrueAchievements.getInstance().open(false);
|
||||
}
|
||||
}
|
||||
|
@@ -11,11 +11,13 @@ import { getPref, StreamTouchController, type GameBarPosition } from "@/utils/se
|
||||
import { TrueAchievementsAction } from "./action-true-achievements";
|
||||
import { SpeakerAction } from "./action-speaker";
|
||||
import { RendererAction } from "./action-renderer";
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
|
||||
|
||||
export class GameBar {
|
||||
private static instance: GameBar;
|
||||
public static getInstance = () => GameBar.instance ?? (GameBar.instance = new GameBar());
|
||||
private readonly LOG_TAG = 'GameBar';
|
||||
|
||||
private static readonly VISIBLE_DURATION = 2000;
|
||||
|
||||
@@ -27,6 +29,8 @@ export class GameBar {
|
||||
private actions: BaseGameBarAction[] = [];
|
||||
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
|
||||
let $container;
|
||||
|
||||
const position = getPref(PrefKey.GAME_BAR_POSITION) as GameBarPosition;
|
||||
|
@@ -6,7 +6,6 @@ import { createButton, ButtonStyle, CE } from "@utils/html";
|
||||
import { BxEvent } from "@utils/bx-event";
|
||||
import { Toast } from "@utils/toast";
|
||||
import { t } from "@utils/translation";
|
||||
import { LocalDb } from "@utils/local-db";
|
||||
import { KeyHelper } from "./key-helper";
|
||||
import type { MkbStoredPreset } from "@/types/mkb";
|
||||
import { AppInterface, STATES } from "@utils/global";
|
||||
@@ -19,8 +18,7 @@ import { SettingsNavigationDialog } from "../ui/dialog/settings-dialog";
|
||||
import { NavigationDialogManager } from "../ui/dialog/navigation-dialog";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
||||
|
||||
const LOG_TAG = 'MkbHandler';
|
||||
import { MkbPresetsDb } from "@/utils/local-db/mkb-presets-db";
|
||||
|
||||
const PointerToMouseButton = {
|
||||
1: 0,
|
||||
@@ -126,6 +124,7 @@ Source: https://github.com/yuzu-emu/yuzu-mainline/blob/master/src/input_common/d
|
||||
export class EmulatedMkbHandler extends MkbHandler {
|
||||
private static instance: EmulatedMkbHandler;
|
||||
public static getInstance = () => EmulatedMkbHandler.instance ?? (EmulatedMkbHandler.instance = new EmulatedMkbHandler());
|
||||
private static readonly LOG_TAG = 'EmulatedMkbHandler';
|
||||
|
||||
#CURRENT_PRESET_DATA = MkbPreset.convert(MkbPreset.DEFAULT_PRESET);
|
||||
|
||||
@@ -167,8 +166,9 @@ export class EmulatedMkbHandler extends MkbHandler {
|
||||
#RIGHT_STICK_X: GamepadKey[] = [];
|
||||
#RIGHT_STICK_Y: GamepadKey[] = [];
|
||||
|
||||
constructor() {
|
||||
private constructor() {
|
||||
super();
|
||||
BxLogger.info(EmulatedMkbHandler.LOG_TAG, 'constructor()');
|
||||
|
||||
this.#STICK_MAP = {
|
||||
[GamepadKey.LS_LEFT]: [this.#LEFT_STICK_X, 0, -1],
|
||||
@@ -431,7 +431,7 @@ export class EmulatedMkbHandler extends MkbHandler {
|
||||
#getCurrentPreset = (): Promise<MkbStoredPreset> => {
|
||||
return new Promise(resolve => {
|
||||
const presetId = getPref(PrefKey.MKB_DEFAULT_PRESET_ID);
|
||||
LocalDb.INSTANCE.getPreset(presetId).then((preset: MkbStoredPreset) => {
|
||||
MkbPresetsDb.getInstance().getPreset(presetId).then((preset: MkbStoredPreset) => {
|
||||
resolve(preset);
|
||||
});
|
||||
});
|
||||
@@ -680,7 +680,7 @@ export class EmulatedMkbHandler extends MkbHandler {
|
||||
AppInterface && NativeMkbHandler.getInstance().init();
|
||||
}
|
||||
} else if (getPref(PrefKey.MKB_ENABLED) && (AppInterface || !UserAgent.isMobile())) {
|
||||
BxLogger.info(LOG_TAG, 'Emulate MKB');
|
||||
BxLogger.info(EmulatedMkbHandler.LOG_TAG, 'Emulate MKB');
|
||||
EmulatedMkbHandler.getInstance().init();
|
||||
}
|
||||
});
|
||||
|
@@ -130,7 +130,6 @@ export class MkbPreset {
|
||||
mouse[MkbPresetKey.MOUSE_MAP_TO] = MkbPreset.MOUSE_SETTINGS[MkbPresetKey.MOUSE_MAP_TO].default;
|
||||
}
|
||||
|
||||
console.log(obj);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import { Dialog } from "@modules/dialog";
|
||||
import { KeyHelper } from "./key-helper";
|
||||
import { MkbPreset } from "./mkb-preset";
|
||||
import { EmulatedMkbHandler } from "./mkb-handler";
|
||||
import { LocalDb } from "@utils/local-db";
|
||||
import { BxIcon } from "@utils/bx-icon";
|
||||
import type { MkbPresetData, MkbStoredPresets } from "@/types/mkb";
|
||||
import { MkbPresetKey, GamepadKey, GamepadKeyName } from "@enums/mkb";
|
||||
@@ -12,18 +11,10 @@ import { deepClone } from "@utils/global";
|
||||
import { SettingElement } from "@/utils/setting-element";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref, setPref } from "@/utils/settings-storages/global-settings-storage";
|
||||
import { MkbPresetsDb } from "@/utils/local-db/mkb-presets-db";
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
|
||||
|
||||
type MkbRemapperElements = {
|
||||
wrapper: HTMLElement | null;
|
||||
presetsSelect: HTMLSelectElement | null;
|
||||
activateButton: HTMLButtonElement | null;
|
||||
currentBindingKey: HTMLElement | null;
|
||||
|
||||
allKeyElements: HTMLElement[];
|
||||
allMouseElements: {[key in MkbPresetKey]?: HTMLElement};
|
||||
};
|
||||
|
||||
type MkbRemapperStates = {
|
||||
currentPresetId: number;
|
||||
presets: MkbStoredPresets;
|
||||
@@ -33,7 +24,7 @@ type MkbRemapperStates = {
|
||||
};
|
||||
|
||||
export class MkbRemapper {
|
||||
readonly #BUTTON_ORDERS = [
|
||||
private readonly BUTTON_ORDERS = [
|
||||
GamepadKey.UP,
|
||||
GamepadKey.DOWN,
|
||||
GamepadKey.LEFT,
|
||||
@@ -66,16 +57,11 @@ export class MkbRemapper {
|
||||
GamepadKey.RS_RIGHT,
|
||||
];
|
||||
|
||||
static #instance: MkbRemapper;
|
||||
static get INSTANCE() {
|
||||
if (!MkbRemapper.#instance) {
|
||||
MkbRemapper.#instance = new MkbRemapper();
|
||||
}
|
||||
private static instance: MkbRemapper;
|
||||
public static getInstance = () => MkbRemapper.instance ?? (MkbRemapper.instance = new MkbRemapper());
|
||||
private readonly LOG_TAG = 'MkbRemapper';
|
||||
|
||||
return MkbRemapper.#instance;
|
||||
};
|
||||
|
||||
#STATE: MkbRemapperStates = {
|
||||
private STATE: MkbRemapperStates = {
|
||||
currentPresetId: 0,
|
||||
presets: {},
|
||||
|
||||
@@ -84,151 +70,150 @@ export class MkbRemapper {
|
||||
isEditing: false,
|
||||
};
|
||||
|
||||
#$: MkbRemapperElements = {
|
||||
wrapper: null,
|
||||
presetsSelect: null,
|
||||
activateButton: null,
|
||||
private $wrapper!: HTMLElement;
|
||||
private $presetsSelect!: HTMLSelectElement;
|
||||
private $activateButton!: HTMLButtonElement;
|
||||
|
||||
currentBindingKey: null,
|
||||
private $currentBindingKey!: HTMLElement;
|
||||
|
||||
allKeyElements: [],
|
||||
allMouseElements: {},
|
||||
};
|
||||
private allKeyElements: HTMLElement[] = [];
|
||||
private allMouseElements: {[key in MkbPresetKey]?: HTMLElement} = {};
|
||||
|
||||
bindingDialog: Dialog;
|
||||
|
||||
constructor() {
|
||||
this.#STATE.currentPresetId = getPref(PrefKey.MKB_DEFAULT_PRESET_ID);
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
this.STATE.currentPresetId = getPref(PrefKey.MKB_DEFAULT_PRESET_ID);
|
||||
|
||||
this.bindingDialog = new Dialog({
|
||||
className: 'bx-binding-dialog',
|
||||
content: CE('div', {},
|
||||
CE('p', {}, t('press-to-bind')),
|
||||
CE('i', {}, t('press-esc-to-cancel')),
|
||||
),
|
||||
CE('p', {}, t('press-to-bind')),
|
||||
CE('i', {}, t('press-esc-to-cancel')),
|
||||
),
|
||||
hideCloseButton: true,
|
||||
});
|
||||
}
|
||||
|
||||
#clearEventListeners = () => {
|
||||
window.removeEventListener('keydown', this.#onKeyDown);
|
||||
window.removeEventListener('mousedown', this.#onMouseDown);
|
||||
window.removeEventListener('wheel', this.#onWheel);
|
||||
private clearEventListeners = () => {
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
window.removeEventListener('mousedown', this.onMouseDown);
|
||||
window.removeEventListener('wheel', this.onWheel);
|
||||
};
|
||||
|
||||
#bindKey = ($elm: HTMLElement, key: any) => {
|
||||
const buttonIndex = parseInt($elm.getAttribute('data-button-index')!);
|
||||
const keySlot = parseInt($elm.getAttribute('data-key-slot')!);
|
||||
private bindKey = ($elm: HTMLElement, key: any) => {
|
||||
const buttonIndex = parseInt($elm.dataset.buttonIndex!);
|
||||
const keySlot = parseInt($elm.dataset.keySlot!);
|
||||
|
||||
// Ignore if bind the save key to the same element
|
||||
if ($elm.getAttribute('data-key-code') === key.code) {
|
||||
if ($elm.dataset.keyCode! === key.code) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Unbind duplicated keys
|
||||
for (const $otherElm of this.#$.allKeyElements) {
|
||||
if ($otherElm.getAttribute('data-key-code') === key.code) {
|
||||
this.#unbindKey($otherElm);
|
||||
for (const $otherElm of this.allKeyElements) {
|
||||
if ($otherElm.dataset.keyCode === key.code) {
|
||||
this.unbindKey($otherElm);
|
||||
}
|
||||
}
|
||||
|
||||
this.#STATE.editingPresetData!.mapping[buttonIndex][keySlot] = key.code;
|
||||
this.STATE.editingPresetData!.mapping[buttonIndex][keySlot] = key.code;
|
||||
$elm.textContent = key.name;
|
||||
$elm.setAttribute('data-key-code', key.code);
|
||||
$elm.dataset.keyCode = key.code;
|
||||
}
|
||||
|
||||
#unbindKey = ($elm: HTMLElement) => {
|
||||
const buttonIndex = parseInt($elm.getAttribute('data-button-index')!);
|
||||
const keySlot = parseInt($elm.getAttribute('data-key-slot')!);
|
||||
private unbindKey = ($elm: HTMLElement) => {
|
||||
const buttonIndex = parseInt($elm.dataset.buttonIndex!);
|
||||
const keySlot = parseInt($elm.dataset.keySlot!);
|
||||
|
||||
// Remove key from preset
|
||||
this.#STATE.editingPresetData!.mapping[buttonIndex][keySlot] = null;
|
||||
this.STATE.editingPresetData!.mapping[buttonIndex][keySlot] = null;
|
||||
$elm.textContent = '';
|
||||
$elm.removeAttribute('data-key-code');
|
||||
delete $elm.dataset.keyCode;
|
||||
}
|
||||
|
||||
#onWheel = (e: WheelEvent) => {
|
||||
private onWheel = (e: WheelEvent) => {
|
||||
e.preventDefault();
|
||||
this.#clearEventListeners();
|
||||
this.clearEventListeners();
|
||||
|
||||
this.#bindKey(this.#$.currentBindingKey!, KeyHelper.getKeyFromEvent(e));
|
||||
this.bindKey(this.$currentBindingKey!, KeyHelper.getKeyFromEvent(e));
|
||||
window.setTimeout(() => this.bindingDialog.hide(), 200);
|
||||
};
|
||||
|
||||
#onMouseDown = (e: MouseEvent) => {
|
||||
private onMouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
this.#clearEventListeners();
|
||||
this.clearEventListeners();
|
||||
|
||||
this.#bindKey(this.#$.currentBindingKey!, KeyHelper.getKeyFromEvent(e));
|
||||
this.bindKey(this.$currentBindingKey!, KeyHelper.getKeyFromEvent(e));
|
||||
window.setTimeout(() => this.bindingDialog.hide(), 200);
|
||||
};
|
||||
|
||||
#onKeyDown = (e: KeyboardEvent) => {
|
||||
private onKeyDown = (e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.#clearEventListeners();
|
||||
this.clearEventListeners();
|
||||
|
||||
if (e.code !== 'Escape') {
|
||||
this.#bindKey(this.#$.currentBindingKey!, KeyHelper.getKeyFromEvent(e));
|
||||
this.bindKey(this.$currentBindingKey!, KeyHelper.getKeyFromEvent(e));
|
||||
}
|
||||
|
||||
window.setTimeout(() => this.bindingDialog.hide(), 200);
|
||||
};
|
||||
|
||||
#onBindingKey = (e: MouseEvent) => {
|
||||
if (!this.#STATE.isEditing || e.button !== 0) {
|
||||
private onBindingKey = (e: MouseEvent) => {
|
||||
if (!this.STATE.isEditing || e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(e);
|
||||
|
||||
this.#$.currentBindingKey = e.target as HTMLElement;
|
||||
this.$currentBindingKey = e.target as HTMLElement;
|
||||
|
||||
window.addEventListener('keydown', this.#onKeyDown);
|
||||
window.addEventListener('mousedown', this.#onMouseDown);
|
||||
window.addEventListener('wheel', this.#onWheel);
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
window.addEventListener('mousedown', this.onMouseDown);
|
||||
window.addEventListener('wheel', this.onWheel);
|
||||
|
||||
this.bindingDialog.show({title: this.#$.currentBindingKey.getAttribute('data-prompt')!});
|
||||
this.bindingDialog.show({title: this.$currentBindingKey.dataset.prompt!});
|
||||
};
|
||||
|
||||
#onContextMenu = (e: Event) => {
|
||||
private onContextMenu = (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!this.#STATE.isEditing) {
|
||||
if (!this.STATE.isEditing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#unbindKey(e.target as HTMLElement);
|
||||
this.unbindKey(e.target as HTMLElement);
|
||||
};
|
||||
|
||||
#getPreset = (presetId: number) => {
|
||||
return this.#STATE.presets[presetId];
|
||||
private getPreset = (presetId: number) => {
|
||||
return this.STATE.presets[presetId];
|
||||
}
|
||||
|
||||
#getCurrentPreset = () => {
|
||||
return this.#getPreset(this.#STATE.currentPresetId);
|
||||
private getCurrentPreset = () => {
|
||||
return this.getPreset(this.STATE.currentPresetId);
|
||||
}
|
||||
|
||||
#switchPreset = (presetId: number) => {
|
||||
this.#STATE.currentPresetId = presetId;
|
||||
const presetData = this.#getCurrentPreset().data;
|
||||
private switchPreset = (presetId: number) => {
|
||||
this.STATE.currentPresetId = presetId;
|
||||
const presetData = this.getCurrentPreset().data;
|
||||
|
||||
for (const $elm of this.#$.allKeyElements) {
|
||||
const buttonIndex = parseInt($elm.getAttribute('data-button-index')!);
|
||||
const keySlot = parseInt($elm.getAttribute('data-key-slot')!);
|
||||
for (const $elm of this.allKeyElements) {
|
||||
const buttonIndex = parseInt($elm.dataset.buttonIndex!);
|
||||
const keySlot = parseInt($elm.dataset.keySlot!);
|
||||
|
||||
const buttonKeys = presetData.mapping[buttonIndex];
|
||||
if (buttonKeys && buttonKeys[keySlot]) {
|
||||
$elm.textContent = KeyHelper.codeToKeyName(buttonKeys[keySlot]!);
|
||||
$elm.setAttribute('data-key-code', buttonKeys[keySlot]!);
|
||||
$elm.dataset.keyCode = buttonKeys[keySlot]!;
|
||||
} else {
|
||||
$elm.textContent = '';
|
||||
$elm.removeAttribute('data-key-code');
|
||||
delete $elm.dataset.keyCode;
|
||||
}
|
||||
}
|
||||
|
||||
let key: MkbPresetKey;
|
||||
for (key in this.#$.allMouseElements) {
|
||||
const $elm = this.#$.allMouseElements[key]!;
|
||||
for (key in this.allMouseElements) {
|
||||
const $elm = this.allMouseElements[key]!;
|
||||
let value = presetData.mouse[key];
|
||||
if (typeof value === 'undefined') {
|
||||
value = MkbPreset.MOUSE_SETTINGS[key].default;
|
||||
@@ -238,26 +223,26 @@ export class MkbRemapper {
|
||||
}
|
||||
|
||||
// Update state of Activate button
|
||||
const activated = getPref(PrefKey.MKB_DEFAULT_PRESET_ID) === this.#STATE.currentPresetId;
|
||||
this.#$.activateButton!.disabled = activated;
|
||||
this.#$.activateButton!.querySelector('span')!.textContent = activated ? t('activated') : t('activate');
|
||||
const activated = getPref(PrefKey.MKB_DEFAULT_PRESET_ID) === this.STATE.currentPresetId;
|
||||
this.$activateButton.disabled = activated;
|
||||
this.$activateButton.querySelector('span')!.textContent = activated ? t('activated') : t('activate');
|
||||
}
|
||||
|
||||
#refresh() {
|
||||
private refresh() {
|
||||
// Clear presets select
|
||||
while (this.#$.presetsSelect!.firstChild) {
|
||||
this.#$.presetsSelect!.removeChild(this.#$.presetsSelect!.firstChild);
|
||||
while (this.$presetsSelect.firstChild) {
|
||||
this.$presetsSelect.removeChild(this.$presetsSelect.firstChild);
|
||||
}
|
||||
|
||||
LocalDb.INSTANCE.getPresets().then(presets => {
|
||||
this.#STATE.presets = presets;
|
||||
MkbPresetsDb.getInstance().getPresets().then(presets => {
|
||||
this.STATE.presets = presets;
|
||||
const $fragment = document.createDocumentFragment();
|
||||
|
||||
let defaultPresetId;
|
||||
if (this.#STATE.currentPresetId === 0) {
|
||||
this.#STATE.currentPresetId = parseInt(Object.keys(presets)[0]);
|
||||
if (this.STATE.currentPresetId === 0) {
|
||||
this.STATE.currentPresetId = parseInt(Object.keys(presets)[0]);
|
||||
|
||||
defaultPresetId = this.#STATE.currentPresetId;
|
||||
defaultPresetId = this.STATE.currentPresetId;
|
||||
setPref(PrefKey.MKB_DEFAULT_PRESET_ID, defaultPresetId);
|
||||
EmulatedMkbHandler.getInstance().refreshPresetData();
|
||||
} else {
|
||||
@@ -272,40 +257,40 @@ export class MkbRemapper {
|
||||
}
|
||||
|
||||
const $options = CE<HTMLOptionElement>('option', {value: id}, name);
|
||||
$options.selected = parseInt(id) === this.#STATE.currentPresetId;
|
||||
$options.selected = parseInt(id) === this.STATE.currentPresetId;
|
||||
|
||||
$fragment.appendChild($options);
|
||||
};
|
||||
|
||||
this.#$.presetsSelect!.appendChild($fragment);
|
||||
this.$presetsSelect.appendChild($fragment);
|
||||
|
||||
// Update state of Activate button
|
||||
const activated = defaultPresetId === this.#STATE.currentPresetId;
|
||||
this.#$.activateButton!.disabled = activated;
|
||||
this.#$.activateButton!.querySelector('span')!.textContent = activated ? t('activated') : t('activate');
|
||||
const activated = defaultPresetId === this.STATE.currentPresetId;
|
||||
this.$activateButton.disabled = activated;
|
||||
this.$activateButton.querySelector('span')!.textContent = activated ? t('activated') : t('activate');
|
||||
|
||||
!this.#STATE.isEditing && this.#switchPreset(this.#STATE.currentPresetId);
|
||||
!this.STATE.isEditing && this.switchPreset(this.STATE.currentPresetId);
|
||||
});
|
||||
}
|
||||
|
||||
#toggleEditing = (force?: boolean) => {
|
||||
this.#STATE.isEditing = typeof force !== 'undefined' ? force : !this.#STATE.isEditing;
|
||||
this.#$.wrapper!.classList.toggle('bx-editing', this.#STATE.isEditing);
|
||||
private toggleEditing = (force?: boolean) => {
|
||||
this.STATE.isEditing = typeof force !== 'undefined' ? force : !this.STATE.isEditing;
|
||||
this.$wrapper.classList.toggle('bx-editing', this.STATE.isEditing);
|
||||
|
||||
if (this.#STATE.isEditing) {
|
||||
this.#STATE.editingPresetData = deepClone(this.#getCurrentPreset().data);
|
||||
if (this.STATE.isEditing) {
|
||||
this.STATE.editingPresetData = deepClone(this.getCurrentPreset().data);
|
||||
} else {
|
||||
this.#STATE.editingPresetData = null;
|
||||
this.STATE.editingPresetData = null;
|
||||
}
|
||||
|
||||
|
||||
const childElements = this.#$.wrapper!.querySelectorAll('select, button, input');
|
||||
const childElements = this.$wrapper.querySelectorAll('select, button, input');
|
||||
for (const $elm of Array.from(childElements)) {
|
||||
if ($elm.parentElement!.parentElement!.classList.contains('bx-mkb-action-buttons')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let disable = !this.#STATE.isEditing;
|
||||
let disable = !this.STATE.isEditing;
|
||||
|
||||
if ($elm.parentElement!.classList.contains('bx-mkb-preset-tools')) {
|
||||
disable = !disable;
|
||||
@@ -316,14 +301,14 @@ export class MkbRemapper {
|
||||
}
|
||||
|
||||
render() {
|
||||
this.#$.wrapper = CE('div', {'class': 'bx-mkb-settings'});
|
||||
this.$wrapper = CE('div', {class: 'bx-mkb-settings'});
|
||||
|
||||
this.#$.presetsSelect = CE<HTMLSelectElement>('select', {tabindex: -1});
|
||||
this.#$.presetsSelect!.addEventListener('change', e => {
|
||||
this.#switchPreset(parseInt((e.target as HTMLSelectElement).value));
|
||||
this.$presetsSelect = CE<HTMLSelectElement>('select', {tabindex: -1});
|
||||
this.$presetsSelect.addEventListener('change', e => {
|
||||
this.switchPreset(parseInt((e.target as HTMLSelectElement).value));
|
||||
});
|
||||
|
||||
const promptNewName = (value?: string) => {
|
||||
const promptNewName = (value: string) => {
|
||||
let newName: string | null = '';
|
||||
while (!newName) {
|
||||
newName = prompt(t('prompt-preset-name'), value);
|
||||
@@ -336,15 +321,15 @@ export class MkbRemapper {
|
||||
return newName ? newName : false;
|
||||
};
|
||||
|
||||
const $header = CE('div', {'class': 'bx-mkb-preset-tools'},
|
||||
this.#$.presetsSelect,
|
||||
const $header = CE('div', {class: 'bx-mkb-preset-tools'},
|
||||
this.$presetsSelect,
|
||||
// Rename button
|
||||
createButton({
|
||||
title: t('rename'),
|
||||
icon: BxIcon.CURSOR_TEXT,
|
||||
tabIndex: -1,
|
||||
onClick: e => {
|
||||
const preset = this.#getCurrentPreset();
|
||||
const preset = this.getCurrentPreset();
|
||||
|
||||
let newName = promptNewName(preset.name);
|
||||
if (!newName || newName === preset.name) {
|
||||
@@ -353,28 +338,28 @@ export class MkbRemapper {
|
||||
|
||||
// Update preset with new name
|
||||
preset.name = newName;
|
||||
LocalDb.INSTANCE.updatePreset(preset).then(id => this.#refresh());
|
||||
MkbPresetsDb.getInstance().updatePreset(preset).then(id => this.refresh());
|
||||
},
|
||||
}),
|
||||
|
||||
// New button
|
||||
createButton({
|
||||
icon: BxIcon.NEW,
|
||||
title: t('new'),
|
||||
tabIndex: -1,
|
||||
onClick: e => {
|
||||
let newName = promptNewName('');
|
||||
if (!newName) {
|
||||
return;
|
||||
}
|
||||
icon: BxIcon.NEW,
|
||||
title: t('new'),
|
||||
tabIndex: -1,
|
||||
onClick: e => {
|
||||
let newName = promptNewName('');
|
||||
if (!newName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new preset selected name
|
||||
LocalDb.INSTANCE.newPreset(newName, MkbPreset.DEFAULT_PRESET).then(id => {
|
||||
this.#STATE.currentPresetId = id;
|
||||
this.#refresh();
|
||||
});
|
||||
},
|
||||
}),
|
||||
// Create new preset selected name
|
||||
MkbPresetsDb.getInstance().newPreset(newName, MkbPreset.DEFAULT_PRESET).then(id => {
|
||||
this.STATE.currentPresetId = id;
|
||||
this.refresh();
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
// Copy button
|
||||
createButton({
|
||||
@@ -382,7 +367,7 @@ export class MkbRemapper {
|
||||
title: t('copy'),
|
||||
tabIndex: -1,
|
||||
onClick: e => {
|
||||
const preset = this.#getCurrentPreset();
|
||||
const preset = this.getCurrentPreset();
|
||||
|
||||
let newName = promptNewName(`${preset.name} (2)`);
|
||||
if (!newName) {
|
||||
@@ -390,9 +375,9 @@ export class MkbRemapper {
|
||||
}
|
||||
|
||||
// Create new preset selected name
|
||||
LocalDb.INSTANCE.newPreset(newName, preset.data).then(id => {
|
||||
this.#STATE.currentPresetId = id;
|
||||
this.#refresh();
|
||||
MkbPresetsDb.getInstance().newPreset(newName, preset.data).then(id => {
|
||||
this.STATE.currentPresetId = id;
|
||||
this.refresh();
|
||||
});
|
||||
},
|
||||
}),
|
||||
@@ -408,23 +393,23 @@ export class MkbRemapper {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDb.INSTANCE.deletePreset(this.#STATE.currentPresetId).then(id => {
|
||||
this.#STATE.currentPresetId = 0;
|
||||
this.#refresh();
|
||||
MkbPresetsDb.getInstance().deletePreset(this.STATE.currentPresetId).then(id => {
|
||||
this.STATE.currentPresetId = 0;
|
||||
this.refresh();
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
this.#$.wrapper!.appendChild($header);
|
||||
this.$wrapper.appendChild($header);
|
||||
|
||||
const $rows = CE('div', {'class': 'bx-mkb-settings-rows'},
|
||||
CE('i', {'class': 'bx-mkb-note'}, t('right-click-to-unbind')),
|
||||
const $rows = CE('div', {class: 'bx-mkb-settings-rows'},
|
||||
CE('i', {class: 'bx-mkb-note'}, t('right-click-to-unbind')),
|
||||
);
|
||||
|
||||
// Render keys
|
||||
const keysPerButton = 2;
|
||||
for (const buttonIndex of this.#BUTTON_ORDERS) {
|
||||
for (const buttonIndex of this.BUTTON_ORDERS) {
|
||||
const [buttonName, buttonPrompt] = GamepadKeyName[buttonIndex];
|
||||
|
||||
let $elm;
|
||||
@@ -437,22 +422,22 @@ export class MkbRemapper {
|
||||
'data-key-slot': i,
|
||||
}, ' ');
|
||||
|
||||
$elm.addEventListener('mouseup', this.#onBindingKey);
|
||||
$elm.addEventListener('contextmenu', this.#onContextMenu);
|
||||
$elm.addEventListener('mouseup', this.onBindingKey);
|
||||
$elm.addEventListener('contextmenu', this.onContextMenu);
|
||||
|
||||
$fragment.appendChild($elm);
|
||||
this.#$.allKeyElements.push($elm);
|
||||
this.allKeyElements.push($elm);
|
||||
}
|
||||
|
||||
const $keyRow = CE('div', {'class': 'bx-mkb-key-row'},
|
||||
CE('label', {'title': buttonName}, buttonPrompt),
|
||||
const $keyRow = CE('div', {class: 'bx-mkb-key-row'},
|
||||
CE('label', {title: buttonName}, buttonPrompt),
|
||||
$fragment,
|
||||
);
|
||||
|
||||
$rows.appendChild($keyRow);
|
||||
}
|
||||
|
||||
$rows.appendChild(CE('i', {'class': 'bx-mkb-note'}, t('mkb-adjust-ingame-settings')),);
|
||||
$rows.appendChild(CE('i', {class: 'bx-mkb-note'}, t('mkb-adjust-ingame-settings')),);
|
||||
|
||||
// Render mouse settings
|
||||
const $mouseSettings = document.createDocumentFragment();
|
||||
@@ -463,7 +448,7 @@ export class MkbRemapper {
|
||||
|
||||
let $elm;
|
||||
const onChange = (e: Event, value: any) => {
|
||||
(this.#STATE.editingPresetData!.mouse as any)[key] = value;
|
||||
(this.STATE.editingPresetData!.mouse as any)[key] = value;
|
||||
};
|
||||
const $row = CE('label', {
|
||||
class: 'bx-settings-row',
|
||||
@@ -474,32 +459,32 @@ export class MkbRemapper {
|
||||
);
|
||||
|
||||
$mouseSettings.appendChild($row);
|
||||
this.#$.allMouseElements[key as MkbPresetKey] = $elm;
|
||||
this.allMouseElements[key as MkbPresetKey] = $elm;
|
||||
}
|
||||
|
||||
$rows.appendChild($mouseSettings);
|
||||
this.#$.wrapper!.appendChild($rows);
|
||||
this.$wrapper.appendChild($rows);
|
||||
|
||||
// Render action buttons
|
||||
const $actionButtons = CE('div', {'class': 'bx-mkb-action-buttons'},
|
||||
const $actionButtons = CE('div', {class: 'bx-mkb-action-buttons'},
|
||||
CE('div', {},
|
||||
// Edit button
|
||||
createButton({
|
||||
label: t('edit'),
|
||||
tabIndex: -1,
|
||||
onClick: e => this.#toggleEditing(true),
|
||||
onClick: e => this.toggleEditing(true),
|
||||
}),
|
||||
|
||||
// Activate button
|
||||
this.#$.activateButton = createButton({
|
||||
this.$activateButton = createButton({
|
||||
label: t('activate'),
|
||||
style: ButtonStyle.PRIMARY,
|
||||
tabIndex: -1,
|
||||
onClick: e => {
|
||||
setPref(PrefKey.MKB_DEFAULT_PRESET_ID, this.#STATE.currentPresetId);
|
||||
setPref(PrefKey.MKB_DEFAULT_PRESET_ID, this.STATE.currentPresetId);
|
||||
EmulatedMkbHandler.getInstance().refreshPresetData();
|
||||
|
||||
this.#refresh();
|
||||
this.refresh();
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -512,8 +497,8 @@ export class MkbRemapper {
|
||||
tabIndex: -1,
|
||||
onClick: e => {
|
||||
// Restore preset
|
||||
this.#switchPreset(this.#STATE.currentPresetId);
|
||||
this.#toggleEditing(false);
|
||||
this.switchPreset(this.STATE.currentPresetId);
|
||||
this.toggleEditing(false);
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -523,27 +508,27 @@ export class MkbRemapper {
|
||||
style: ButtonStyle.PRIMARY,
|
||||
tabIndex: -1,
|
||||
onClick: e => {
|
||||
const updatedPreset = deepClone(this.#getCurrentPreset());
|
||||
updatedPreset.data = this.#STATE.editingPresetData as MkbPresetData;
|
||||
const updatedPreset = deepClone(this.getCurrentPreset());
|
||||
updatedPreset.data = this.STATE.editingPresetData as MkbPresetData;
|
||||
|
||||
LocalDb.INSTANCE.updatePreset(updatedPreset).then(id => {
|
||||
MkbPresetsDb.getInstance().updatePreset(updatedPreset).then(id => {
|
||||
// If this is the default preset => refresh preset data
|
||||
if (id === getPref(PrefKey.MKB_DEFAULT_PRESET_ID)) {
|
||||
EmulatedMkbHandler.getInstance().refreshPresetData();
|
||||
}
|
||||
|
||||
this.#toggleEditing(false);
|
||||
this.#refresh();
|
||||
this.toggleEditing(false);
|
||||
this.refresh();
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
this.#$.wrapper!.appendChild($actionButtons);
|
||||
this.$wrapper.appendChild($actionButtons);
|
||||
|
||||
this.#toggleEditing(false);
|
||||
this.#refresh();
|
||||
return this.#$.wrapper;
|
||||
this.toggleEditing(false);
|
||||
this.refresh();
|
||||
return this.$wrapper;
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import { BxEvent } from "@/utils/bx-event";
|
||||
import { ButtonStyle, CE, createButton } from "@/utils/html";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
|
||||
type NativeMouseData = {
|
||||
X: number,
|
||||
@@ -24,6 +25,7 @@ type XcloudInputSink = {
|
||||
export class NativeMkbHandler extends MkbHandler {
|
||||
private static instance: NativeMkbHandler;
|
||||
public static getInstance = () => NativeMkbHandler.instance ?? (NativeMkbHandler.instance = new NativeMkbHandler());
|
||||
private readonly LOG_TAG = 'NativeMkbHandler';
|
||||
|
||||
#pointerClient: PointerClient | undefined;
|
||||
#enabled: boolean = false;
|
||||
@@ -39,6 +41,11 @@ export class NativeMkbHandler extends MkbHandler {
|
||||
|
||||
#$message?: HTMLElement;
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
}
|
||||
|
||||
#onKeyboardEvent(e: KeyboardEvent) {
|
||||
if (e.type === 'keyup' && e.code === 'F8') {
|
||||
e.preventDefault();
|
||||
|
@@ -2,8 +2,6 @@ import { BxLogger } from "@/utils/bx-logger";
|
||||
import { Toast } from "@/utils/toast";
|
||||
import type { MkbHandler } from "./base-mkb-handler";
|
||||
|
||||
const LOG_TAG = 'PointerClient';
|
||||
|
||||
enum PointerAction {
|
||||
MOVE = 1,
|
||||
BUTTON_PRESS = 2,
|
||||
@@ -16,10 +14,15 @@ enum PointerAction {
|
||||
export class PointerClient {
|
||||
private static instance: PointerClient;
|
||||
public static getInstance = () => PointerClient.instance ?? (PointerClient.instance = new PointerClient());
|
||||
private readonly LOG_TAG = 'PointerClient';
|
||||
|
||||
private socket: WebSocket | undefined | null;
|
||||
private mkbHandler: MkbHandler | undefined;
|
||||
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
}
|
||||
|
||||
start(port: number, mkbHandler: MkbHandler) {
|
||||
if (!port) {
|
||||
throw new Error('PointerServer port is 0');
|
||||
@@ -33,12 +36,12 @@ export class PointerClient {
|
||||
|
||||
// Connection opened
|
||||
this.socket.addEventListener('open', (event) => {
|
||||
BxLogger.info(LOG_TAG, 'connected')
|
||||
BxLogger.info(this.LOG_TAG, 'connected')
|
||||
});
|
||||
|
||||
// Error
|
||||
this.socket.addEventListener('error', (event) => {
|
||||
BxLogger.error(LOG_TAG, event);
|
||||
BxLogger.error(this.LOG_TAG, event);
|
||||
Toast.show('Cannot setup mouse: ' + event);
|
||||
});
|
||||
|
||||
|
@@ -1212,7 +1212,7 @@ export class PatcherCache {
|
||||
*/
|
||||
static #getSignature(): number {
|
||||
const scriptVersion = SCRIPT_VERSION;
|
||||
const webVersion = (document.querySelector('meta[name=gamepass-app-version]') as HTMLMetaElement)?.content;
|
||||
const webVersion = (document.querySelector<HTMLMetaElement>('meta[name=gamepass-app-version]'))?.content;
|
||||
const patches = JSON.stringify(ALL_PATCHES);
|
||||
|
||||
// Calculate signature
|
||||
|
@@ -9,8 +9,6 @@ import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref, setPref } from "@/utils/settings-storages/global-settings-storage";
|
||||
import { RemotePlayNavigationDialog } from "./ui/dialog/remote-play-dialog";
|
||||
|
||||
const LOG_TAG = 'RemotePlay';
|
||||
|
||||
export const enum RemotePlayConsoleState {
|
||||
ON = 'On',
|
||||
OFF = 'Off',
|
||||
@@ -38,6 +36,7 @@ type RemotePlayConsole = {
|
||||
export class RemotePlayManager {
|
||||
private static instance: RemotePlayManager;
|
||||
public static getInstance = () => RemotePlayManager.instance ?? (RemotePlayManager.instance = new RemotePlayManager());
|
||||
private readonly LOG_TAG = 'RemotePlayManager';
|
||||
|
||||
private isInitialized = false;
|
||||
|
||||
@@ -47,6 +46,10 @@ export class RemotePlayManager {
|
||||
private consoles!: Array<RemotePlayConsole>;
|
||||
private regions: Array<RemotePlayRegion> = [];
|
||||
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
@@ -56,9 +59,9 @@ export class RemotePlayManager {
|
||||
|
||||
this.getXhomeToken(() => {
|
||||
this.getConsolesList(() => {
|
||||
BxLogger.info(LOG_TAG, 'Consoles', this.consoles);
|
||||
BxLogger.info(this.LOG_TAG, 'Consoles', this.consoles);
|
||||
|
||||
STATES.supportedRegion && HeaderSection.showRemotePlayButton();
|
||||
STATES.supportedRegion && HeaderSection.getInstance().showRemotePlayButton();
|
||||
BxEvent.dispatch(window, BxEvent.REMOTE_PLAY_READY);
|
||||
});
|
||||
});
|
||||
|
@@ -77,13 +77,7 @@ export class SoundShortcut {
|
||||
return;
|
||||
}
|
||||
|
||||
let $media: HTMLMediaElement;
|
||||
|
||||
$media = document.querySelector('div[data-testid=media-container] audio') as HTMLAudioElement;
|
||||
if (!$media) {
|
||||
$media = document.querySelector('div[data-testid=media-container] video') as HTMLAudioElement;
|
||||
}
|
||||
|
||||
const $media = document.querySelector<HTMLAudioElement>('div[data-testid=media-container] audio') ?? document.querySelector<HTMLAudioElement>('div[data-testid=media-container] video');
|
||||
if ($media) {
|
||||
$media.muted = !$media.muted;
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import { isFullVersion } from "@macros/build" with {type: "macro"};
|
||||
|
||||
import { CE } from "@/utils/html";
|
||||
import { WebGL2Player } from "./player/webgl2-player";
|
||||
import { Screenshot } from "@/utils/screenshot";
|
||||
import { ScreenshotManager } from "@/utils/screenshot-manager";
|
||||
import { StreamPlayerType, StreamVideoProcessing } from "@enums/stream-player";
|
||||
import { STATES } from "@/utils/global";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
@@ -237,7 +237,7 @@ export class StreamPlayer {
|
||||
webGL2Player.setFilter(2);
|
||||
}
|
||||
|
||||
isFullVersion() && Screenshot.updateCanvasFilters('none');
|
||||
isFullVersion() && ScreenshotManager.getInstance().updateCanvasFilters('none');
|
||||
|
||||
webGL2Player.setSharpness(options.sharpness || 0);
|
||||
webGL2Player.setSaturation(options.saturation || 100);
|
||||
@@ -252,7 +252,7 @@ export class StreamPlayer {
|
||||
|
||||
// Apply video filters to screenshots
|
||||
if (isFullVersion() && getPref(PrefKey.SCREENSHOT_APPLY_FILTERS)) {
|
||||
Screenshot.updateCanvasFilters(filters);
|
||||
ScreenshotManager.getInstance().updateCanvasFilters(filters);
|
||||
}
|
||||
|
||||
let css = '';
|
||||
|
@@ -50,6 +50,7 @@ enum StreamBadge {
|
||||
export class StreamBadges {
|
||||
private static instance: StreamBadges;
|
||||
public static getInstance = () => StreamBadges.instance ?? (StreamBadges.instance = new StreamBadges());
|
||||
private readonly LOG_TAG = 'StreamBadges';
|
||||
|
||||
private serverInfo: StreamServerInfo = {};
|
||||
|
||||
@@ -96,6 +97,10 @@ export class StreamBadges {
|
||||
private intervalId?: number | null;
|
||||
private readonly REFRESH_INTERVAL = 3 * 1000;
|
||||
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
}
|
||||
|
||||
setRegion(region: string) {
|
||||
this.serverInfo.server = {
|
||||
region: region,
|
||||
|
@@ -18,7 +18,7 @@ export function onChangeVideoPlayerType() {
|
||||
|
||||
let isDisabled = false;
|
||||
|
||||
const $optCas = $videoProcessing.querySelector(`option[value=${StreamVideoProcessing.CAS}]`) as HTMLOptionElement;
|
||||
const $optCas = $videoProcessing.querySelector<HTMLOptionElement>(`option[value=${StreamVideoProcessing.CAS}]`);
|
||||
|
||||
if (playerType === StreamPlayerType.WEBGL2) {
|
||||
$optCas && ($optCas.disabled = false);
|
||||
|
@@ -5,11 +5,13 @@ import { STATES } from "@utils/global"
|
||||
import { PrefKey } from "@/enums/pref-keys"
|
||||
import { getPref } from "@/utils/settings-storages/global-settings-storage"
|
||||
import { StreamStat, StreamStatsCollector, type StreamStatGrade } from "@/utils/stream-stats-collector"
|
||||
import { BxLogger } from "@/utils/bx-logger"
|
||||
|
||||
|
||||
export class StreamStats {
|
||||
private static instance: StreamStats;
|
||||
public static getInstance = () => StreamStats.instance ?? (StreamStats.instance = new StreamStats());
|
||||
private readonly LOG_TAG = 'StreamStats';
|
||||
|
||||
private intervalId?: number | null;
|
||||
private readonly REFRESH_INTERVAL = 1 * 1000;
|
||||
@@ -69,7 +71,8 @@ export class StreamStats {
|
||||
|
||||
quickGlanceObserver?: MutationObserver | null;
|
||||
|
||||
constructor() {
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
@@ -39,7 +39,7 @@ export class StreamUiHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
const $streamHud = (e.target as HTMLElement).closest('#StreamHud') as HTMLElement;
|
||||
const $streamHud = (e.target as HTMLElement).closest<HTMLElement>('#StreamHud');
|
||||
if (!$streamHud) {
|
||||
return;
|
||||
}
|
||||
@@ -58,13 +58,13 @@ export class StreamUiHandler {
|
||||
$container.addEventListener('transitionend', onTransitionEnd);
|
||||
}
|
||||
|
||||
const $button = $container.querySelector('button') as HTMLElement;
|
||||
const $button = $container.querySelector<HTMLButtonElement>('button');
|
||||
if (!$button) {
|
||||
return null;
|
||||
}
|
||||
$button.setAttribute('title', label);
|
||||
|
||||
const $orgSvg = $button.querySelector('svg') as SVGElement;
|
||||
const $orgSvg = $button.querySelector<SVGElement>('svg');
|
||||
if (!$orgSvg) {
|
||||
return null;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ export class StreamUiHandler {
|
||||
}
|
||||
|
||||
private static async handleStreamMenu() {
|
||||
const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]') as HTMLElement;
|
||||
const $btnCloseHud = document.querySelector<HTMLElement>('button[class*=StreamMenu-module__backButton]');
|
||||
if (!$btnCloseHud) {
|
||||
return;
|
||||
}
|
||||
@@ -136,14 +136,14 @@ export class StreamUiHandler {
|
||||
|
||||
private static handleSystemMenu($streamHud: HTMLElement) {
|
||||
// Get the last button
|
||||
const $orgButton = $streamHud.querySelector('div[class^=HUDButton]') as HTMLElement;
|
||||
const $orgButton = $streamHud.querySelector<HTMLElement>('div[class^=HUDButton]');
|
||||
if (!$orgButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hideGripHandle = () => {
|
||||
// Grip handle
|
||||
const $gripHandle = document.querySelector('#StreamHud button[class^=GripHandle]') as HTMLElement;
|
||||
const $gripHandle = document.querySelector<HTMLElement>('#StreamHud button[class^=GripHandle]');
|
||||
if ($gripHandle && $gripHandle.ariaExpanded === 'true') {
|
||||
$gripHandle.dispatchEvent(new PointerEvent('pointerdown'));
|
||||
$gripHandle.click();
|
||||
|
@@ -2,6 +2,7 @@ import { GamepadKey } from "@/enums/mkb";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { VIRTUAL_GAMEPAD_ID } from "@/modules/mkb/mkb-handler";
|
||||
import { BxEvent } from "@/utils/bx-event";
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
import { STATES } from "@/utils/global";
|
||||
import { CE, isElementVisible } from "@/utils/html";
|
||||
import { setNearby } from "@/utils/navigation-utils";
|
||||
@@ -89,6 +90,7 @@ export abstract class NavigationDialog {
|
||||
export class NavigationDialogManager {
|
||||
private static instance: NavigationDialogManager;
|
||||
public static getInstance = () => NavigationDialogManager.instance ?? (NavigationDialogManager.instance = new NavigationDialogManager());
|
||||
private readonly LOG_TAG = 'NavigationDialogManager';
|
||||
|
||||
private static readonly GAMEPAD_POLLING_INTERVAL = 50;
|
||||
private static readonly GAMEPAD_KEYS = [
|
||||
@@ -136,7 +138,9 @@ export class NavigationDialogManager {
|
||||
private $container: HTMLElement;
|
||||
private dialog: NavigationDialog | null = null;
|
||||
|
||||
constructor() {
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
|
||||
this.$overlay = CE('div', {class: 'bx-navigation-dialog-overlay bx-gone'});
|
||||
this.$overlay.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
@@ -185,17 +189,17 @@ export class NavigationDialogManager {
|
||||
|
||||
const rect = $select.getBoundingClientRect();
|
||||
|
||||
let $label;
|
||||
let $label: HTMLElement;
|
||||
let width = Math.ceil(rect.width);
|
||||
if (!width) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (($select as HTMLSelectElement).multiple) {
|
||||
$label = $parent.querySelector('.bx-select-value') as HTMLElement;
|
||||
$label = $parent.querySelector<HTMLElement>('.bx-select-value')!;
|
||||
width += 20; // Add checkbox's width
|
||||
} else {
|
||||
$label = $parent.querySelector('div') as HTMLElement;
|
||||
$label = $parent.querySelector<HTMLElement>('div')!;
|
||||
}
|
||||
|
||||
// Set min-width
|
||||
|
@@ -7,11 +7,13 @@ import { t } from "@/utils/translation";
|
||||
import { RemotePlayConsoleState, RemotePlayManager } from "@/modules/remote-play-manager";
|
||||
import { BxSelectElement } from "@/web-components/bx-select";
|
||||
import { BxEvent } from "@/utils/bx-event";
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
|
||||
|
||||
export class RemotePlayNavigationDialog extends NavigationDialog {
|
||||
private static instance: RemotePlayNavigationDialog;
|
||||
public static getInstance = () => RemotePlayNavigationDialog.instance ?? (RemotePlayNavigationDialog.instance = new RemotePlayNavigationDialog());
|
||||
private readonly LOG_TAG = 'RemotePlayNavigationDialog';
|
||||
|
||||
private readonly STATE_LABELS: Record<RemotePlayConsoleState, string> = {
|
||||
[RemotePlayConsoleState.ON]: t('powered-on'),
|
||||
@@ -22,8 +24,9 @@ export class RemotePlayNavigationDialog extends NavigationDialog {
|
||||
|
||||
$container!: HTMLElement;
|
||||
|
||||
constructor() {
|
||||
private constructor() {
|
||||
super();
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
this.setupDialog();
|
||||
}
|
||||
|
||||
@@ -124,7 +127,7 @@ export class RemotePlayNavigationDialog extends NavigationDialog {
|
||||
}
|
||||
|
||||
focusIfNeeded(): void {
|
||||
const $btnConnect = this.$container.querySelector('.bx-remote-play-device-wrapper button') as HTMLElement;
|
||||
const $btnConnect = this.$container.querySelector<HTMLElement>('.bx-remote-play-device-wrapper button');
|
||||
$btnConnect && $btnConnect.focus();
|
||||
}
|
||||
}
|
||||
|
@@ -27,12 +27,13 @@ import { ControllerDeviceVibration, getPref, getPrefDefinition, setPref, StreamT
|
||||
import { SettingElement, type BxHtmlSettingElement } from "@/utils/setting-element";
|
||||
import type { RecommendedSettings, SettingDefinition, SuggestedSettingCategory as SuggestedSettingProfile } from "@/types/setting-definition";
|
||||
import { FullscreenText } from "../fullscreen-text";
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
|
||||
|
||||
type SettingTabContentItem = Partial<{
|
||||
pref: PrefKey;
|
||||
label: string;
|
||||
note: string;
|
||||
note: string | (() => HTMLElement);
|
||||
experimental: string;
|
||||
content: HTMLElement | (() => HTMLElement);
|
||||
options: {[key: string]: string};
|
||||
@@ -51,24 +52,29 @@ type SettingTabContent = {
|
||||
unsupportedNote?: string | Text | null;
|
||||
helpUrl?: string;
|
||||
content?: any;
|
||||
lazyContent?: boolean | (() => HTMLElement);
|
||||
items?: Array<SettingTabContentItem | PrefKey | (($parent: HTMLElement) => void) | false>;
|
||||
requiredVariants?: BuildVariant | Array<BuildVariant>;
|
||||
};
|
||||
|
||||
type SettingTab = {
|
||||
icon: SVGElement;
|
||||
group: 'global';
|
||||
items: Array<SettingTabContent | false>;
|
||||
group: SettingTabGroup,
|
||||
items: Array<SettingTabContent | false> | (() => Array<SettingTabContent | false>);
|
||||
requiredVariants?: BuildVariant | Array<BuildVariant>;
|
||||
lazyContent?: boolean;
|
||||
};
|
||||
|
||||
type SettingTabGroup = 'global' | 'stream' | 'controller' | 'mkb' | 'native-mkb' | 'shortcuts' | 'stats';
|
||||
|
||||
export class SettingsNavigationDialog extends NavigationDialog {
|
||||
private static instance: SettingsNavigationDialog;
|
||||
public static getInstance = () => SettingsNavigationDialog.instance ?? (SettingsNavigationDialog.instance = new SettingsNavigationDialog());
|
||||
private readonly LOG_TAG = 'SettingsNavigationDialog';
|
||||
|
||||
$container!: HTMLElement;
|
||||
private $tabs!: HTMLElement;
|
||||
private $settings!: HTMLElement;
|
||||
private $tabContents!: HTMLElement;
|
||||
|
||||
private $btnReload!: HTMLElement;
|
||||
private $btnGlobalReload!: HTMLButtonElement;
|
||||
@@ -326,8 +332,8 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
// xCloud version
|
||||
($parent) => {
|
||||
try {
|
||||
const appVersion = (document.querySelector('meta[name=gamepass-app-version]') as HTMLMetaElement).content;
|
||||
const appDate = new Date((document.querySelector('meta[name=gamepass-app-date]') as HTMLMetaElement).content).toISOString().substring(0, 10);
|
||||
const appVersion = document.querySelector<HTMLMetaElement>('meta[name=gamepass-app-version]')!.content;
|
||||
const appDate = new Date(document.querySelector<HTMLMetaElement>('meta[name=gamepass-app-date]')!.content).toISOString().substring(0, 10);
|
||||
$parent.appendChild(CE('div', {
|
||||
class: 'bx-settings-app-version',
|
||||
}, `xCloud website version ${appVersion} (${appDate})`));
|
||||
@@ -380,7 +386,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
disabled: !getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL),
|
||||
},
|
||||
onCreated: (setting: SettingTabContentItem, $elm: HTMLElement) => {
|
||||
const $range = $elm.querySelector('input[type=range') as HTMLInputElement;
|
||||
const $range = $elm.querySelector<HTMLInputElement>('input[type=range')!;
|
||||
window.addEventListener(BxEvent.SETTINGS_CHANGED, e => {
|
||||
const { storageKey, settingKey, settingValue } = e as any;
|
||||
if (storageKey !== StorageKey.GLOBAL || settingKey !== PrefKey.AUDIO_VOLUME) {
|
||||
@@ -511,11 +517,11 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}],
|
||||
}];
|
||||
|
||||
private readonly TAB_VIRTUAL_CONTROLLER_ITEMS: Array<SettingTabContent | false> = [{
|
||||
private readonly TAB_VIRTUAL_CONTROLLER_ITEMS: (() => Array<SettingTabContent | false>) = () => [{
|
||||
group: 'mkb',
|
||||
label: t('virtual-controller'),
|
||||
helpUrl: 'https://better-xcloud.github.io/mouse-and-keyboard/',
|
||||
content: isFullVersion() && MkbRemapper.INSTANCE.render(),
|
||||
content: MkbRemapper.getInstance().render(),
|
||||
}];
|
||||
|
||||
private readonly TAB_NATIVE_MKB_ITEMS: Array<SettingTabContent | false> = [{
|
||||
@@ -535,7 +541,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}] : [],
|
||||
}];
|
||||
|
||||
private readonly TAB_SHORTCUTS_ITEMS: Array<SettingTabContent | false> = [{
|
||||
private readonly TAB_SHORTCUTS_ITEMS: (() => Array<SettingTabContent | false>) = () => [{
|
||||
requiredVariants: 'full',
|
||||
group: 'controller-shortcuts',
|
||||
label: t('controller-shortcuts'),
|
||||
@@ -576,56 +582,59 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
],
|
||||
}];
|
||||
|
||||
private readonly SETTINGS_UI: Array<SettingTab> = [
|
||||
{
|
||||
icon: BxIcon.HOME,
|
||||
private readonly SETTINGS_UI: Record<SettingTabGroup, SettingTab> = {
|
||||
global: {
|
||||
group: 'global',
|
||||
icon: BxIcon.HOME,
|
||||
items: this.TAB_GLOBAL_ITEMS,
|
||||
},
|
||||
|
||||
{
|
||||
icon: BxIcon.DISPLAY,
|
||||
stream: {
|
||||
group: 'stream',
|
||||
icon: BxIcon.DISPLAY,
|
||||
items: this.TAB_DISPLAY_ITEMS,
|
||||
},
|
||||
|
||||
{
|
||||
icon: BxIcon.CONTROLLER,
|
||||
controller: {
|
||||
group: 'controller',
|
||||
icon: BxIcon.CONTROLLER,
|
||||
items: this.TAB_CONTROLLER_ITEMS,
|
||||
requiredVariants: 'full',
|
||||
},
|
||||
|
||||
isFullVersion() && getPref(PrefKey.MKB_ENABLED) && {
|
||||
icon: BxIcon.VIRTUAL_CONTROLLER,
|
||||
mkb: isFullVersion() && getPref(PrefKey.MKB_ENABLED) && {
|
||||
group: 'mkb',
|
||||
icon: BxIcon.VIRTUAL_CONTROLLER,
|
||||
items: this.TAB_VIRTUAL_CONTROLLER_ITEMS,
|
||||
lazyContent: true,
|
||||
requiredVariants: 'full',
|
||||
},
|
||||
|
||||
isFullVersion() && AppInterface && getPref(PrefKey.NATIVE_MKB_ENABLED) === 'on' && {
|
||||
icon: BxIcon.NATIVE_MKB,
|
||||
'native-mkb': isFullVersion() && AppInterface && getPref(PrefKey.NATIVE_MKB_ENABLED) === 'on' && {
|
||||
group: 'native-mkb',
|
||||
icon: BxIcon.NATIVE_MKB,
|
||||
items: this.TAB_NATIVE_MKB_ITEMS,
|
||||
requiredVariants: 'full',
|
||||
},
|
||||
|
||||
{
|
||||
icon: BxIcon.COMMAND,
|
||||
shortcuts: {
|
||||
group: 'shortcuts',
|
||||
icon: BxIcon.COMMAND,
|
||||
items: this.TAB_SHORTCUTS_ITEMS,
|
||||
lazyContent: true,
|
||||
requiredVariants: 'full',
|
||||
},
|
||||
|
||||
{
|
||||
icon: BxIcon.STREAM_STATS,
|
||||
stats: {
|
||||
group: 'stats',
|
||||
icon: BxIcon.STREAM_STATS,
|
||||
items: this.TAB_STATS_ITEMS,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
constructor() {
|
||||
private constructor() {
|
||||
super();
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
|
||||
this.renderFullSettings = STATES.supportedRegion && STATES.isSignedIn;
|
||||
this.setupDialog();
|
||||
@@ -653,7 +662,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}
|
||||
|
||||
// Trigger event
|
||||
const $selectUserAgent = document.querySelector(`#bx_setting_${PrefKey.USER_AGENT_PROFILE}`) as HTMLSelectElement;
|
||||
const $selectUserAgent = document.querySelector<HTMLSelectElement>(`#bx_setting_${PrefKey.USER_AGENT_PROFILE}`);
|
||||
if ($selectUserAgent) {
|
||||
$selectUserAgent.disabled = true;
|
||||
BxEvent.dispatch($selectUserAgent, 'input', {});
|
||||
@@ -757,8 +766,11 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}
|
||||
|
||||
// Get labels
|
||||
for (const settingTab of this.SETTINGS_UI) {
|
||||
if (!settingTab || !settingTab.items) {
|
||||
let settingTabGroup: keyof typeof this.SETTINGS_UI;
|
||||
for (settingTabGroup in this.SETTINGS_UI) {
|
||||
const settingTab = this.SETTINGS_UI[settingTabGroup];
|
||||
|
||||
if (!settingTab || !settingTab.items || typeof settingTab.items === 'function') {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -901,7 +913,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
let prefKey: PrefKey;
|
||||
for (prefKey in settings) {
|
||||
const suggestedValue = settings[prefKey];
|
||||
const $checkBox = $content.querySelector(`#bx_suggest_${prefKey}`) as HTMLInputElement;
|
||||
const $checkBox = $content.querySelector<HTMLInputElement>(`#bx_suggest_${prefKey}`)!;
|
||||
if (!$checkBox.checked || $checkBox.disabled) {
|
||||
continue;
|
||||
}
|
||||
@@ -961,36 +973,57 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}, t('suggest-settings-link')),
|
||||
);
|
||||
|
||||
$btnSuggest?.insertAdjacentElement('afterend', $content);
|
||||
$btnSuggest.insertAdjacentElement('afterend', $content);
|
||||
}
|
||||
|
||||
private onTabClicked(e: Event) {
|
||||
const $svg = (e.target as SVGElement).closest('svg')!;
|
||||
|
||||
// Render tab content lazily
|
||||
if (!!$svg.dataset.lazy) {
|
||||
// Remove attribute
|
||||
delete $svg.dataset.lazy;
|
||||
// Render data
|
||||
const settingTab = this.SETTINGS_UI[$svg.dataset.group as SettingTabGroup];
|
||||
|
||||
const items = (settingTab.items as Function)();
|
||||
const $tabContent = this.renderTabContent.call(this, settingTab, items);
|
||||
this.$tabContents.appendChild($tabContent);
|
||||
}
|
||||
|
||||
// Switch tab
|
||||
let $child: HTMLElement;
|
||||
const children = Array.from(this.$tabContents.children) as HTMLElement[];
|
||||
for ($child of children) {
|
||||
if ($child.dataset.tabGroup === $svg.dataset.group) {
|
||||
// Show tab content
|
||||
$child.classList.remove('bx-gone');
|
||||
|
||||
// Calculate size of controller-friendly select boxes
|
||||
if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
|
||||
this.dialogManager.calculateSelectBoxes($child as HTMLElement);
|
||||
}
|
||||
} else {
|
||||
// Hide tab content
|
||||
$child.classList.add('bx-gone');
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight current tab button
|
||||
for (const $child of Array.from(this.$tabs.children)) {
|
||||
$child.classList.remove('bx-active');
|
||||
}
|
||||
|
||||
$svg.classList.add('bx-active');
|
||||
}
|
||||
|
||||
private renderTab(settingTab: SettingTab) {
|
||||
const $svg = createSvgIcon(settingTab.icon as any);
|
||||
$svg.dataset.group = settingTab.group;
|
||||
$svg.tabIndex = 0;
|
||||
settingTab.lazyContent && ($svg.dataset.lazy = settingTab.lazyContent.toString());
|
||||
|
||||
$svg.addEventListener('click', e => {
|
||||
// Switch tab
|
||||
for (const $child of Array.from(this.$settings.children)) {
|
||||
if ($child.getAttribute('data-tab-group') === settingTab.group) {
|
||||
$child.classList.remove('bx-gone');
|
||||
|
||||
// Calculate size of controller-friendly select boxes
|
||||
if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
|
||||
this.dialogManager.calculateSelectBoxes($child as HTMLElement);
|
||||
}
|
||||
} else {
|
||||
$child.classList.add('bx-gone');
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight current tab button
|
||||
for (const $child of Array.from(this.$tabs.children)) {
|
||||
$child.classList.remove('bx-active');
|
||||
}
|
||||
|
||||
$svg.classList.add('bx-active');
|
||||
});
|
||||
$svg.addEventListener('click', this.onTabClicked.bind(this));
|
||||
|
||||
return $svg;
|
||||
}
|
||||
@@ -1137,10 +1170,19 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}
|
||||
|
||||
let label = prefDefinition?.label || setting.label;
|
||||
let note = prefDefinition?.note || setting.note;
|
||||
let unsupportedNote = prefDefinition?.unsupportedNote || setting.unsupportedNote;
|
||||
let note: string | undefined | (() => HTMLElement) | HTMLElement = prefDefinition?.note || setting.note;
|
||||
let unsupportedNote: string | undefined | (() => HTMLElement) | HTMLElement = prefDefinition?.unsupportedNote || setting.unsupportedNote;
|
||||
const experimental = prefDefinition?.experimental || setting.experimental;
|
||||
|
||||
// Render note lazily
|
||||
if (typeof note === 'function') {
|
||||
note = note();
|
||||
}
|
||||
|
||||
if (typeof unsupportedNote === 'function') {
|
||||
unsupportedNote = unsupportedNote();
|
||||
}
|
||||
|
||||
if (settingTabContent.label && setting.pref) {
|
||||
if (prefDefinition?.suggest) {
|
||||
typeof prefDefinition.suggest.lowest !== 'undefined' && (this.suggestedSettings.lowest[setting.pref] = prefDefinition.suggest.lowest);
|
||||
@@ -1195,9 +1237,101 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
!prefDefinition?.unsupported && setting.onCreated && setting.onCreated(setting, $control);
|
||||
}
|
||||
|
||||
private renderTabContent(settingTab: SettingTab, items: Array<SettingTabContent | false>): HTMLElement {
|
||||
const $tabContent = CE('div', {
|
||||
class: 'bx-gone',
|
||||
'data-tab-group': settingTab.group,
|
||||
});
|
||||
|
||||
for (const settingTabContent of items) {
|
||||
if (!settingTabContent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.isSupportedVariant(settingTabContent.requiredVariants)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't render other settings in unsupported regions
|
||||
if (!this.renderFullSettings && settingTab.group === 'global' && settingTabContent.group !== 'general' && settingTabContent.group !== 'footer') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let label = settingTabContent.label;
|
||||
|
||||
// If label is "Better xCloud" => create a link to Releases page
|
||||
if (label === t('better-xcloud')) {
|
||||
label += ' ' + SCRIPT_VERSION;
|
||||
|
||||
if (SCRIPT_VARIANT === 'lite') {
|
||||
label += ' (Lite)';
|
||||
}
|
||||
|
||||
label = createButton({
|
||||
label: label,
|
||||
url: 'https://github.com/redphx/better-xcloud/releases',
|
||||
style: ButtonStyle.NORMAL_CASE | ButtonStyle.FROSTED | ButtonStyle.FOCUSABLE,
|
||||
});
|
||||
}
|
||||
|
||||
if (label) {
|
||||
const $title = CE('h2', {
|
||||
_nearby: {
|
||||
orientation: 'horizontal',
|
||||
}
|
||||
},
|
||||
CE('span', {}, label),
|
||||
settingTabContent.helpUrl && createButton({
|
||||
icon: BxIcon.QUESTION,
|
||||
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE,
|
||||
url: settingTabContent.helpUrl,
|
||||
title: t('help'),
|
||||
}),
|
||||
);
|
||||
|
||||
$tabContent.appendChild($title);
|
||||
}
|
||||
|
||||
// Add note
|
||||
if (settingTabContent.unsupportedNote) {
|
||||
const $note = CE('b', {class: 'bx-note-unsupported'}, settingTabContent.unsupportedNote);
|
||||
|
||||
$tabContent.appendChild($note);
|
||||
}
|
||||
|
||||
// Don't render settings if this is an unsupported feature
|
||||
if (settingTabContent.unsupported) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add content DOM
|
||||
if (settingTabContent.content) {
|
||||
$tabContent.appendChild(settingTabContent.content);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Render list of settings
|
||||
settingTabContent.items = settingTabContent.items || [];
|
||||
for (const setting of settingTabContent.items) {
|
||||
if (setting === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof setting === 'function') {
|
||||
setting.apply(this, [$tabContent]);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.renderSettingRow(settingTab, $tabContent, settingTabContent, setting);
|
||||
}
|
||||
}
|
||||
|
||||
return $tabContent;
|
||||
}
|
||||
|
||||
private setupDialog() {
|
||||
let $tabs: HTMLElement;
|
||||
let $settings: HTMLElement;
|
||||
let $tabContents: HTMLElement;
|
||||
|
||||
const $container = CE('div', {
|
||||
class: 'bx-settings-dialog',
|
||||
@@ -1245,7 +1379,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
),
|
||||
),
|
||||
|
||||
$settings = CE('div', {
|
||||
$tabContents = CE('div', {
|
||||
class: 'bx-settings-tab-contents',
|
||||
_nearby: {
|
||||
orientation: 'vertical',
|
||||
@@ -1264,7 +1398,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
|
||||
this.$container = $container;
|
||||
this.$tabs = $tabs;
|
||||
this.$settings = $settings;
|
||||
this.$tabContents = $tabContents;
|
||||
|
||||
// Close dialog when not clicking on any child elements in the dialog
|
||||
$container.addEventListener('click', e => {
|
||||
@@ -1275,7 +1409,10 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}
|
||||
});
|
||||
|
||||
for (const settingTab of this.SETTINGS_UI) {
|
||||
let settingTabGroup: keyof typeof this.SETTINGS_UI
|
||||
for (settingTabGroup in this.SETTINGS_UI) {
|
||||
const settingTab = this.SETTINGS_UI[settingTabGroup];
|
||||
|
||||
if (!settingTab) {
|
||||
continue;
|
||||
}
|
||||
@@ -1293,95 +1430,13 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
const $svg = this.renderTab(settingTab);
|
||||
$tabs.appendChild($svg);
|
||||
|
||||
const $tabContent = CE('div', {
|
||||
class: 'bx-gone',
|
||||
'data-tab-group': settingTab.group,
|
||||
});
|
||||
|
||||
for (const settingTabContent of settingTab.items) {
|
||||
if (settingTabContent === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.isSupportedVariant(settingTabContent.requiredVariants)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't render other settings in unsupported regions
|
||||
if (!this.renderFullSettings && settingTab.group === 'global' && settingTabContent.group !== 'general' && settingTabContent.group !== 'footer') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let label = settingTabContent.label;
|
||||
|
||||
// If label is "Better xCloud" => create a link to Releases page
|
||||
if (label === t('better-xcloud')) {
|
||||
label += ' ' + SCRIPT_VERSION;
|
||||
|
||||
if (SCRIPT_VARIANT === 'lite') {
|
||||
label += ' (Lite)';
|
||||
}
|
||||
|
||||
label = createButton({
|
||||
label: label,
|
||||
url: 'https://github.com/redphx/better-xcloud/releases',
|
||||
style: ButtonStyle.NORMAL_CASE | ButtonStyle.FROSTED | ButtonStyle.FOCUSABLE,
|
||||
});
|
||||
}
|
||||
|
||||
if (label) {
|
||||
const $title = CE('h2', {
|
||||
_nearby: {
|
||||
orientation: 'horizontal',
|
||||
}
|
||||
},
|
||||
CE('span', {}, label),
|
||||
settingTabContent.helpUrl && createButton({
|
||||
icon: BxIcon.QUESTION,
|
||||
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE,
|
||||
url: settingTabContent.helpUrl,
|
||||
title: t('help'),
|
||||
}),
|
||||
);
|
||||
|
||||
$tabContent.appendChild($title);
|
||||
}
|
||||
|
||||
// Add note
|
||||
if (settingTabContent.unsupportedNote) {
|
||||
const $note = CE('b', {class: 'bx-note-unsupported'}, settingTabContent.unsupportedNote);
|
||||
|
||||
$tabContent.appendChild($note);
|
||||
}
|
||||
|
||||
// Don't render settings if this is an unsupported feature
|
||||
if (settingTabContent.unsupported) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add content DOM
|
||||
if (settingTabContent.content) {
|
||||
$tabContent.appendChild(settingTabContent.content);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Render list of settings
|
||||
settingTabContent.items = settingTabContent.items || [];
|
||||
for (const setting of settingTabContent.items) {
|
||||
if (setting === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof setting === 'function') {
|
||||
setting.apply(this, [$tabContent]);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.renderSettingRow(settingTab, $tabContent, settingTabContent, setting);
|
||||
}
|
||||
// Don't render lazy tab content
|
||||
if (typeof settingTab.items === 'function') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$settings.appendChild($tabContent);
|
||||
const $tabContent = this.renderTabContent.call(this, settingTab, settingTab.items);
|
||||
$tabContents.appendChild($tabContent);
|
||||
}
|
||||
|
||||
// Select first tab
|
||||
@@ -1398,13 +1453,13 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}
|
||||
|
||||
private focusActiveTab() {
|
||||
const $currentTab = this.$tabs!.querySelector('.bx-active') as HTMLElement;
|
||||
const $currentTab = this.$tabs!.querySelector<HTMLElement>('.bx-active');
|
||||
$currentTab && $currentTab.focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
private focusVisibleSetting(type: 'first' | 'last' = 'first'): boolean {
|
||||
const controls = Array.from(this.$settings.querySelectorAll('div[data-tab-group]:not(.bx-gone) > *'));
|
||||
const controls = Array.from(this.$tabContents.querySelectorAll('div[data-tab-group]:not(.bx-gone) > *'));
|
||||
if (!controls.length) {
|
||||
return false;
|
||||
}
|
||||
@@ -1450,7 +1505,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
}
|
||||
|
||||
private jumpToSettingGroup(direction: 'next' | 'previous'): boolean {
|
||||
const $tabContent = this.$settings.querySelector('div[data-tab-group]:not(.bx-gone)');
|
||||
const $tabContent = this.$tabContents.querySelector('div[data-tab-group]:not(.bx-gone)');
|
||||
if (!$tabContent) {
|
||||
return false;
|
||||
}
|
||||
@@ -1461,7 +1516,7 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
||||
$header = $tabContent.querySelector('h2');
|
||||
} else {
|
||||
// Find the parent element
|
||||
const $parent = $focusing.closest('[data-tab-group] > *') as HTMLElement;
|
||||
const $parent = $focusing.closest<HTMLElement>('[data-tab-group] > *');
|
||||
const siblingProperty = direction === 'next' ? 'nextSibling' : 'previousSibling';
|
||||
|
||||
let $tmp = $parent;
|
||||
|
@@ -1,12 +1,15 @@
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
import { CE } from "@/utils/html";
|
||||
|
||||
export class FullscreenText {
|
||||
private static instance: FullscreenText;
|
||||
public static getInstance = () => FullscreenText.instance ?? (FullscreenText.instance = new FullscreenText());
|
||||
private readonly LOG_TAG = 'FullscreenText';
|
||||
|
||||
$text: HTMLElement;
|
||||
|
||||
constructor() {
|
||||
private constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
this.$text = CE('div', {
|
||||
class: 'bx-fullscreen-text bx-gone',
|
||||
});
|
||||
|
@@ -13,101 +13,104 @@ export enum GuideMenuTab {
|
||||
}
|
||||
|
||||
export class GuideMenu {
|
||||
static #BUTTONS = {
|
||||
scriptSettings: createButton({
|
||||
label: t('better-xcloud'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY,
|
||||
onClick: e => {
|
||||
// Wait until the Guide dialog is closed
|
||||
window.addEventListener(BxEvent.XCLOUD_DIALOG_DISMISSED, e => {
|
||||
setTimeout(() => SettingsNavigationDialog.getInstance().show(), 50);
|
||||
}, {once: true});
|
||||
private static instance: GuideMenu;
|
||||
public static getInstance = () => GuideMenu.instance ?? (GuideMenu.instance = new GuideMenu());
|
||||
|
||||
// Close all xCloud's dialogs
|
||||
GuideMenu.#closeGuideMenu();
|
||||
},
|
||||
}),
|
||||
private $renderedButtons?: HTMLElement;
|
||||
|
||||
closeApp: AppInterface && createButton({
|
||||
icon: BxIcon.POWER,
|
||||
label: t('close-app'),
|
||||
title: t('close-app'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE | ButtonStyle.DANGER,
|
||||
onClick: e => {
|
||||
AppInterface.closeApp();
|
||||
},
|
||||
|
||||
attributes: {
|
||||
'data-state': 'normal',
|
||||
},
|
||||
}),
|
||||
|
||||
reloadPage: createButton({
|
||||
icon: BxIcon.REFRESH,
|
||||
label: t('reload-page'),
|
||||
title: t('reload-page'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE,
|
||||
onClick: e => {
|
||||
if (STATES.isPlaying) {
|
||||
confirm(t('confirm-reload-stream')) && window.location.reload();
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// Close all xCloud's dialogs
|
||||
GuideMenu.#closeGuideMenu();
|
||||
},
|
||||
}),
|
||||
|
||||
backToHome: createButton({
|
||||
icon: BxIcon.HOME,
|
||||
label: t('back-to-home'),
|
||||
title: t('back-to-home'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE,
|
||||
onClick: e => {
|
||||
confirm(t('back-to-home-confirm')) && (window.location.href = window.location.href.substring(0, 31));
|
||||
|
||||
// Close all xCloud's dialogs
|
||||
GuideMenu.#closeGuideMenu();
|
||||
},
|
||||
attributes: {
|
||||
'data-state': 'playing',
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
static #$renderedButtons: HTMLElement;
|
||||
|
||||
static #closeGuideMenu() {
|
||||
closeGuideMenu() {
|
||||
if (window.BX_EXPOSED.dialogRoutes) {
|
||||
window.BX_EXPOSED.dialogRoutes.closeAll();
|
||||
return;
|
||||
}
|
||||
|
||||
// Use alternative method for Lite version
|
||||
const $btnClose = document.querySelector('#gamepass-dialog-root button[class^=Header-module__closeButton]') as HTMLElement;
|
||||
const $btnClose = document.querySelector<HTMLElement>('#gamepass-dialog-root button[class^=Header-module__closeButton]');
|
||||
$btnClose && $btnClose.click();
|
||||
}
|
||||
|
||||
static #renderButtons() {
|
||||
if (GuideMenu.#$renderedButtons) {
|
||||
return GuideMenu.#$renderedButtons;
|
||||
private renderButtons() {
|
||||
if (this.$renderedButtons) {
|
||||
return this.$renderedButtons;
|
||||
}
|
||||
|
||||
const buttons = {
|
||||
scriptSettings: createButton({
|
||||
label: t('better-xcloud'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY,
|
||||
onClick: (() => {
|
||||
// Wait until the Guide dialog is closed
|
||||
window.addEventListener(BxEvent.XCLOUD_DIALOG_DISMISSED, e => {
|
||||
setTimeout(() => SettingsNavigationDialog.getInstance().show(), 50);
|
||||
}, {once: true});
|
||||
|
||||
// Close all xCloud's dialogs
|
||||
this.closeGuideMenu();
|
||||
}).bind(this),
|
||||
}),
|
||||
|
||||
closeApp: AppInterface && createButton({
|
||||
icon: BxIcon.POWER,
|
||||
label: t('close-app'),
|
||||
title: t('close-app'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE | ButtonStyle.DANGER,
|
||||
onClick: e => {
|
||||
AppInterface.closeApp();
|
||||
},
|
||||
|
||||
attributes: {
|
||||
'data-state': 'normal',
|
||||
},
|
||||
}),
|
||||
|
||||
reloadPage: createButton({
|
||||
icon: BxIcon.REFRESH,
|
||||
label: t('reload-page'),
|
||||
title: t('reload-page'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE,
|
||||
onClick: (() => {
|
||||
// Close all xCloud's dialogs
|
||||
this.closeGuideMenu();
|
||||
|
||||
if (STATES.isPlaying) {
|
||||
confirm(t('confirm-reload-stream')) && window.location.reload();
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
}).bind(this),
|
||||
}),
|
||||
|
||||
backToHome: createButton({
|
||||
icon: BxIcon.HOME,
|
||||
label: t('back-to-home'),
|
||||
title: t('back-to-home'),
|
||||
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE,
|
||||
onClick: (() => {
|
||||
// Close all xCloud's dialogs
|
||||
this.closeGuideMenu();
|
||||
|
||||
confirm(t('back-to-home-confirm')) && (window.location.href = window.location.href.substring(0, 31));
|
||||
}).bind(this),
|
||||
attributes: {
|
||||
'data-state': 'playing',
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const buttonsLayout = [
|
||||
buttons.scriptSettings,
|
||||
[
|
||||
buttons.backToHome,
|
||||
buttons.reloadPage,
|
||||
buttons.closeApp,
|
||||
],
|
||||
];
|
||||
|
||||
const $div = CE('div', {
|
||||
class: 'bx-guide-home-buttons',
|
||||
});
|
||||
|
||||
const buttons = [
|
||||
GuideMenu.#BUTTONS.scriptSettings,
|
||||
[
|
||||
GuideMenu.#BUTTONS.backToHome,
|
||||
GuideMenu.#BUTTONS.reloadPage,
|
||||
GuideMenu.#BUTTONS.closeApp,
|
||||
],
|
||||
];
|
||||
|
||||
for (const $button of buttons) {
|
||||
for (const $button of buttonsLayout) {
|
||||
if (!$button) {
|
||||
continue;
|
||||
}
|
||||
@@ -123,15 +126,15 @@ export class GuideMenu {
|
||||
}
|
||||
}
|
||||
|
||||
GuideMenu.#$renderedButtons = $div;
|
||||
this.$renderedButtons = $div;
|
||||
return $div;
|
||||
}
|
||||
|
||||
static #injectHome($root: HTMLElement, isPlaying = false) {
|
||||
injectHome($root: HTMLElement, isPlaying = false) {
|
||||
if (isFullVersion()) {
|
||||
const $achievementsProgress = $root.querySelector('button[class*=AchievementsButton-module__progressBarContainer]');
|
||||
if ($achievementsProgress) {
|
||||
TrueAchievements.injectAchievementsProgress($achievementsProgress as HTMLElement);
|
||||
TrueAchievements.getInstance().injectAchievementsProgress($achievementsProgress as HTMLElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +145,7 @@ export class GuideMenu {
|
||||
$target = $root.querySelector('a[class*=QuitGameButton]');
|
||||
|
||||
// Hide xCloud's Home button
|
||||
const $btnXcloudHome = $root.querySelector('div[class^=HomeButtonWithDivider]') as HTMLElement;
|
||||
const $btnXcloudHome = $root.querySelector<HTMLElement>('div[class^=HomeButtonWithDivider]');
|
||||
$btnXcloudHome && ($btnXcloudHome.style.display = 'none');
|
||||
} else {
|
||||
// Last divider
|
||||
@@ -156,29 +159,30 @@ export class GuideMenu {
|
||||
return false;
|
||||
}
|
||||
|
||||
const $buttons = GuideMenu.#renderButtons();
|
||||
const $buttons = this.renderButtons();
|
||||
$buttons.dataset.isPlaying = isPlaying.toString();
|
||||
$target.insertAdjacentElement('afterend', $buttons);
|
||||
}
|
||||
|
||||
static async #onShown(e: Event) {
|
||||
async onShown(e: Event) {
|
||||
const where = (e as any).where as GuideMenuTab;
|
||||
|
||||
if (where === GuideMenuTab.HOME) {
|
||||
const $root = document.querySelector('#gamepass-dialog-root div[role=dialog] div[role=tabpanel] div[class*=HomeLandingPage]') as HTMLElement;
|
||||
$root && GuideMenu.#injectHome($root, STATES.isPlaying);
|
||||
const $root = document.querySelector<HTMLElement>('#gamepass-dialog-root div[role=dialog] div[role=tabpanel] div[class*=HomeLandingPage]');
|
||||
$root && this.injectHome($root, STATES.isPlaying);
|
||||
}
|
||||
}
|
||||
|
||||
static addEventListeners() {
|
||||
window.addEventListener(BxEvent.XCLOUD_GUIDE_MENU_SHOWN, GuideMenu.#onShown);
|
||||
addEventListeners() {
|
||||
window.addEventListener(BxEvent.XCLOUD_GUIDE_MENU_SHOWN, this.onShown.bind(this));
|
||||
}
|
||||
|
||||
static observe($addedElm: HTMLElement) {
|
||||
observe($addedElm: HTMLElement) {
|
||||
const className = $addedElm.className;
|
||||
|
||||
// TrueAchievements
|
||||
if (isFullVersion() && className.includes('AchievementsButton-module__progressBarContainer')) {
|
||||
TrueAchievements.injectAchievementsProgress($addedElm);
|
||||
TrueAchievements.getInstance().injectAchievementsProgress($addedElm);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -192,7 +196,7 @@ export class GuideMenu {
|
||||
if (isFullVersion()) {
|
||||
const $achievDetailPage = $addedElm.querySelector('div[class*=AchievementDetailPage]');
|
||||
if ($achievDetailPage) {
|
||||
TrueAchievements.injectAchievementDetailPage($achievDetailPage as HTMLElement);
|
||||
TrueAchievements.getInstance().injectAchievementDetailPage($achievDetailPage as HTMLElement);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -7,36 +7,45 @@ import { t } from "@utils/translation";
|
||||
import { SettingsNavigationDialog } from "./dialog/settings-dialog";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
||||
import { BxLogger } from "@/utils/bx-logger";
|
||||
|
||||
export class HeaderSection {
|
||||
static #$remotePlayBtn = createButton({
|
||||
classes: ['bx-header-remote-play-button', 'bx-gone'],
|
||||
icon: BxIcon.REMOTE_PLAY,
|
||||
title: t('remote-play'),
|
||||
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE | ButtonStyle.CIRCULAR,
|
||||
onClick: e => {
|
||||
RemotePlayManager.getInstance().togglePopup();
|
||||
},
|
||||
});
|
||||
private static instance: HeaderSection;
|
||||
public static getInstance = () => HeaderSection.instance ?? (HeaderSection.instance = new HeaderSection());
|
||||
private readonly LOG_TAG = 'HeaderSection';
|
||||
|
||||
static #$settingsBtn = createButton({
|
||||
classes: ['bx-header-settings-button'],
|
||||
label: '???',
|
||||
style: ButtonStyle.FROSTED | ButtonStyle.DROP_SHADOW | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_HEIGHT,
|
||||
onClick: e => {
|
||||
SettingsNavigationDialog.getInstance().show();
|
||||
},
|
||||
});
|
||||
private $btnRemotePlay: HTMLElement;
|
||||
private $btnSettings: HTMLElement;
|
||||
private $buttonsWrapper: HTMLElement;
|
||||
|
||||
static #$buttonsWrapper = CE('div', {},
|
||||
getPref(PrefKey.REMOTE_PLAY_ENABLED) ? HeaderSection.#$remotePlayBtn : null,
|
||||
HeaderSection.#$settingsBtn,
|
||||
);
|
||||
private observer?: MutationObserver;
|
||||
private timeoutId?: number | null;
|
||||
|
||||
static #observer: MutationObserver;
|
||||
static #timeout: number | null;
|
||||
constructor() {
|
||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||
|
||||
static #injectSettingsButton($parent?: HTMLElement) {
|
||||
this.$btnRemotePlay = createButton({
|
||||
classes: ['bx-header-remote-play-button', 'bx-gone'],
|
||||
icon: BxIcon.REMOTE_PLAY,
|
||||
title: t('remote-play'),
|
||||
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE | ButtonStyle.CIRCULAR,
|
||||
onClick: e => RemotePlayManager.getInstance().togglePopup(),
|
||||
});
|
||||
|
||||
this.$btnSettings = createButton({
|
||||
classes: ['bx-header-settings-button'],
|
||||
label: '???',
|
||||
style: ButtonStyle.FROSTED | ButtonStyle.DROP_SHADOW | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_HEIGHT,
|
||||
onClick: e => SettingsNavigationDialog.getInstance().show(),
|
||||
});
|
||||
|
||||
this.$buttonsWrapper = CE('div', {},
|
||||
getPref(PrefKey.REMOTE_PLAY_ENABLED) ? this.$btnRemotePlay : null,
|
||||
this.$btnSettings,
|
||||
);
|
||||
}
|
||||
|
||||
private injectSettingsButton($parent?: HTMLElement) {
|
||||
if (!$parent) {
|
||||
return;
|
||||
}
|
||||
@@ -44,8 +53,8 @@ export class HeaderSection {
|
||||
const PREF_LATEST_VERSION = getPref(PrefKey.LATEST_VERSION);
|
||||
|
||||
// Setup Settings button
|
||||
const $btnSettings = HeaderSection.#$settingsBtn;
|
||||
if (isElementVisible(HeaderSection.#$buttonsWrapper)) {
|
||||
const $btnSettings = this.$btnSettings;
|
||||
if (isElementVisible(this.$buttonsWrapper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,38 +66,42 @@ export class HeaderSection {
|
||||
}
|
||||
|
||||
// Add the Settings button to the web page
|
||||
$parent.appendChild(HeaderSection.#$buttonsWrapper);
|
||||
$parent.appendChild(this.$buttonsWrapper);
|
||||
}
|
||||
|
||||
static checkHeader() {
|
||||
private checkHeader() {
|
||||
let $target = document.querySelector('#PageContent div[class*=EdgewaterHeader-module__rightSectionSpacing]');
|
||||
if (!$target) {
|
||||
$target = document.querySelector('div[class^=UnsupportedMarketPage-module__buttons]');
|
||||
}
|
||||
|
||||
$target && HeaderSection.#injectSettingsButton($target as HTMLElement);
|
||||
$target && this.injectSettingsButton($target as HTMLElement);
|
||||
}
|
||||
|
||||
static showRemotePlayButton() {
|
||||
HeaderSection.#$remotePlayBtn.classList.remove('bx-gone');
|
||||
}
|
||||
|
||||
static watchHeader() {
|
||||
private watchHeader() {
|
||||
const $root = document.querySelector('#PageContent header') || document.querySelector('#root');
|
||||
if (!$root) {
|
||||
return;
|
||||
}
|
||||
|
||||
HeaderSection.#timeout && clearTimeout(HeaderSection.#timeout);
|
||||
HeaderSection.#timeout = null;
|
||||
this.timeoutId && clearTimeout(this.timeoutId);
|
||||
this.timeoutId = null;
|
||||
|
||||
HeaderSection.#observer && HeaderSection.#observer.disconnect();
|
||||
HeaderSection.#observer = new MutationObserver(mutationList => {
|
||||
HeaderSection.#timeout && clearTimeout(HeaderSection.#timeout);
|
||||
HeaderSection.#timeout = window.setTimeout(HeaderSection.checkHeader, 2000);
|
||||
this.observer && this.observer.disconnect();
|
||||
this.observer = new MutationObserver(mutationList => {
|
||||
this.timeoutId && clearTimeout(this.timeoutId);
|
||||
this.timeoutId = window.setTimeout(this.checkHeader.bind(this), 2000);
|
||||
});
|
||||
HeaderSection.#observer.observe($root, {subtree: true, childList: true});
|
||||
this.observer.observe($root, {subtree: true, childList: true});
|
||||
|
||||
HeaderSection.checkHeader();
|
||||
this.checkHeader();
|
||||
}
|
||||
|
||||
showRemotePlayButton() {
|
||||
this.$btnRemotePlay.classList.remove('bx-gone');
|
||||
}
|
||||
|
||||
static watchHeader() {
|
||||
HeaderSection.getInstance().watchHeader();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user