diff --git a/build.ts b/build.ts index 798ba51..c3e5070 100755 --- a/build.ts +++ b/build.ts @@ -21,6 +21,7 @@ enum BuildTarget { type BuildVariant = 'full' | 'lite'; const MINIFY_SYNTAX = true; +const INDENT_SPACES = false; function minifySvgImports(str: string): string { // Minify SVG imports @@ -128,8 +129,12 @@ function postProcess(str: string): string { str = minifyIfElse(str); str = str.replaceAll(/\n(\s+)/g, (match, p1) => { - const len = p1.length / 2; - return '\n' + ' '.repeat(len); + if (INDENT_SPACES) { + const len = p1.length / 2; + return '\n' + ' '.repeat(len); + } else { + return '\n'; + } }); } diff --git a/bun.lock b/bun.lock old mode 100755 new mode 100644 index 40bef75..4bbb893 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,5 @@ { - "lockfileVersion": 0, + "lockfileVersion": 1, "workspaces": { "": { "devDependencies": { @@ -24,15 +24,15 @@ "@eslint/config-array": ["@eslint/config-array@0.19.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ=="], - "@eslint/core": ["@eslint/core@0.9.0", "", {}, "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg=="], + "@eslint/core": ["@eslint/core@0.10.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw=="], "@eslint/eslintrc": ["@eslint/eslintrc@3.2.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w=="], - "@eslint/js": ["@eslint/js@9.17.0", "", {}, "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w=="], + "@eslint/js": ["@eslint/js@9.19.0", "", {}, "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ=="], "@eslint/object-schema": ["@eslint/object-schema@2.1.4", "", {}, "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ=="], - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.3", "", { "dependencies": { "levn": "^0.4.1" } }, "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA=="], + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.5", "", { "dependencies": { "@eslint/core": "^0.10.0", "levn": "^0.4.1" } }, "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -48,13 +48,13 @@ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - "@types/bun": ["@types/bun@1.1.14", "", { "dependencies": { "bun-types": "1.1.37" } }, "sha512-opVYiFGtO2af0dnWBdZWlioLBoxSdDO5qokaazLhq8XQtGZbY4pY3/JxY8Zdf/hEwGubbp7ErZXoN1+h2yesxA=="], + "@types/bun": ["@types/bun@1.2.0", "", { "dependencies": { "bun-types": "1.2.0" } }, "sha512-5N1JqdahfpBlAv4wy6svEYcd/YfO2GNrbL95JOmFx8nkE6dbK4R0oSE5SpBA4vBRqgrOUAXF8Dpiz+gi7r80SA=="], "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/node": ["@types/node@22.10.2", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ=="], + "@types/node": ["@types/node@22.10.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww=="], "@types/stylus": ["@types/stylus@0.48.43", "", { "dependencies": { "@types/node": "*" } }, "sha512-72dv/zdhuyXWVHUXG2VTPEQdOG+oen95/DNFx2aMFFaY6LoITI6PwEqf5x31JF49kp2w9hvUzkNfTGBIeg61LQ=="], @@ -80,7 +80,7 @@ "browserslist": ["browserslist@4.24.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA=="], - "bun-types": ["bun-types@1.1.37", "", { "dependencies": { "@types/node": "~20.12.8", "@types/ws": "~8.5.10" } }, "sha512-C65lv6eBr3LPJWFZ2gswyrGZ82ljnH8flVE03xeXxKhi2ZGtFiO4isRKTKnitbSqtRAcaqYSR6djt1whI66AbA=="], + "bun-types": ["bun-types@1.2.0", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-KEaJxyZfbV/c4eyG0vyehDpYmBGreNiQbZIqvVHJwZ4BmeuWlNZ7EAzMN2Zcd7ailmS/tGVW0BgYbGf+lGEpWw=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], @@ -110,7 +110,7 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "eslint": ["eslint@9.17.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.9.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.17.0", "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA=="], + "eslint": ["eslint@9.19.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.19.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA=="], "eslint-plugin-compat": ["eslint-plugin-compat@6.0.2", "", { "dependencies": { "@mdn/browser-compat-data": "^5.5.35", "ast-metadata-inferer": "^0.8.1", "browserslist": "^4.24.2", "caniuse-lite": "^1.0.30001687", "find-up": "^5.0.0", "globals": "^15.7.0", "lodash.memoize": "^4.1.2", "semver": "^7.6.2" }, "peerDependencies": { "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-1ME+YfJjmOz1blH0nPZpHgjMGK4kjgEeoYqGCqoBPQ/mGu/dJzdoP0f1C8H2jcWZjzhZjAMccbM/VdXhPORIfA=="], @@ -280,8 +280,6 @@ "ast-metadata-inferer/@mdn/browser-compat-data": ["@mdn/browser-compat-data@5.6.26", "", {}, "sha512-7NdgdOR7lkzrN70zGSULmrcvKyi/aJjpTJRCbuy8IZuHiLkPTvsr10jW0MJgWzK2l2wTmhdQvegTw6yNU5AVNQ=="], - "bun-types/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="], - "foreground-child/cross-spawn": ["cross-spawn@7.0.3", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w=="], "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -302,8 +300,6 @@ "@types/ws/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "bun-types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], diff --git a/package.json b/package.json index 14c4fcc..dd0800f 100755 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ "build": "build.ts" }, "devDependencies": { - "@types/bun": "^1.1.14", - "@types/node": "^22.10.2", + "@types/bun": "^1.2.0", + "@types/node": "^22.10.10", "@types/stylus": "^0.48.43", - "eslint": "^9.17.0", + "eslint": "^9.19.0", "eslint-plugin-compat": "^6.0.2", "stylus": "^0.64.0" }, diff --git a/src/assets/css/button.styl b/src/assets/css/button.styl index 0ea50a7..05b9d29 100755 --- a/src/assets/css/button.styl +++ b/src/assets/css/button.styl @@ -37,6 +37,7 @@ &:disabled { cursor: default; background-color: unquote('rgb(var(--button-disabled-rgb))'); + opacity: 0.5; } &.bx-ghost { diff --git a/src/assets/css/root.styl b/src/assets/css/root.styl index 87d29b2..fe741e7 100755 --- a/src/assets/css/root.styl +++ b/src/assets/css/root.styl @@ -25,7 +25,7 @@ button_color(name, normal, hover, active, disabled) button_color('default', #2d3036, #515863, #222428, #8e8e8e); button_color('primary', #008746, #04b358, #044e2a, #448262); button_color('warning', #c16e04, #fa9005, #965603, #a2816c); - button_color('danger', #c10404, #e61d1d, #a26c6c, #df5656); + button_color('danger', #c10404, #e61d1d, #a26c6c, #bd8282); --bx-fullscreen-text-z-index: 9999; --bx-toast-z-index: 6000; @@ -47,7 +47,7 @@ button_color(name, normal, hover, active, disabled) @font-face { font-family: 'promptfont'; src: url('https://redphx.github.io/better-xcloud/fonts/promptfont.otf'); - unicode-range: U+2196-E011; + unicode-range: U+2196-E011, U+27F6, U+FF31; } /* Fix Stream menu buttons not hiding */ diff --git a/src/assets/css/settings-dialog.styl b/src/assets/css/settings-dialog.styl index ff59e1d..91a3cb2 100755 --- a/src/assets/css/settings-dialog.styl +++ b/src/assets/css/settings-dialog.styl @@ -98,10 +98,8 @@ tabsWidth = 48px; flex-direction: column; - padding: 10px; margin-left: tabsWidth; width: 450px; - max-width: calc(100vw - tabsWidth); background: #1a1b1e; color: #fff; font-weight: 400; @@ -112,13 +110,6 @@ overflow: overlay; z-index: 1; - > div[data-tab-group=mkb] { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; - } - .bx-top-buttons { display: flex; flex-direction: column; @@ -284,7 +275,8 @@ color: #828282; } -.bx-settings-tab-contents { +.bx-settings-tab-content { + padding: 10px; border-radius-size = 6px; > div { @@ -307,6 +299,14 @@ border-radius: border-radius-size; } } + + &:not([data-game-id="-1"]) { + .bx-settings-row[data-override=true], .bx-settings-row:has(*[data-override=true]) { + border-left: 4px solid orange !important; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; + } + } } .bx-suggest-toggler { @@ -533,3 +533,53 @@ flex: 1; } } + +.bx-stream-settings-selection { + margin-bottom: 8px; + position: sticky; + z-index: 1000; + top: 0; + + > div { + display: flex; + gap: 8px; + background: #222222; + padding: 10px; + border-bottom: 4px solid #353638; + box-shadow: 0 0 6px #000; + position: relative; + z-index: 1; + + .bx-select { + flex: 1; + + label { + font-weight: bold; + font-size: 1.1rem; + line-height: initial; + + span { + line-height: initial; + } + } + + .bx-select-indicators { + display: none; + } + } + } + + p { + font-family: var(--bx-promptfont-font), var(--bx-normal-font); + margin: 0; + font-size: 13px; + background: #505050f2; + height: 25px; + line-height: 23px; + position: absolute; + bottom: -25px; + left: 0; + right: 0; + text-shadow: 0 1px #000; + } +} diff --git a/src/assets/svg/global-restore.svg b/src/assets/svg/global-restore.svg new file mode 100644 index 0000000..a12f7ba --- /dev/null +++ b/src/assets/svg/global-restore.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/enums/mkb.ts b/src/enums/mkb.ts index e48ec4f..dda3999 100755 --- a/src/enums/mkb.ts +++ b/src/enums/mkb.ts @@ -37,131 +37,6 @@ export const enum MkbPresetKey { } -export type KeyCode = - | 'Backspace' - | 'Tab' - | 'Enter' - | 'ShiftLeft' - | 'ShiftRight' - | 'ControlLeft' - | 'ControlRight' - | 'AltLeft' - | 'AltRight' - | 'Pause' - | 'CapsLock' - | 'Escape' - | 'Space' - | 'PageUp' - | 'PageDown' - | 'End' - | 'Home' - | 'ArrowLeft' - | 'ArrowUp' - | 'ArrowRight' - | 'ArrowDown' - | 'PrintScreen' - | 'Insert' - | 'Delete' - | 'Digit0' - | 'Digit1' - | 'Digit2' - | 'Digit3' - | 'Digit4' - | 'Digit5' - | 'Digit6' - | 'Digit7' - | 'Digit8' - | 'Digit9' - | 'KeyA' - | 'KeyB' - | 'KeyC' - | 'KeyD' - | 'KeyE' - | 'KeyF' - | 'KeyG' - | 'KeyH' - | 'KeyI' - | 'KeyJ' - | 'KeyK' - | 'KeyL' - | 'KeyM' - | 'KeyN' - | 'KeyO' - | 'KeyP' - | 'KeyQ' - | 'KeyR' - | 'KeyS' - | 'KeyT' - | 'KeyU' - | 'KeyV' - | 'KeyW' - | 'KeyX' - | 'KeyY' - | 'KeyZ' - | 'MetaLeft' - | 'MetaRight' - | 'ContextMenu' - | 'F1' - | 'F2' - | 'F3' - | 'F4' - | 'F5' - | 'F6' - | 'F7' - | 'F8' - | 'F9' - | 'F10' - | 'F11' - | 'F12' - | 'NumLock' - | 'ScrollLock' - | 'AudioVolumeMute' - | 'AudioVolumeDown' - | 'AudioVolumeUp' - | 'MediaTrackNext' - | 'MediaTrackPrevious' - | 'MediaStop' - | 'MediaPlayPause' - | 'LaunchMail' - | 'LaunchMediaPlayer' - | 'LaunchApplication1' - | 'LaunchApplication2' - | 'Semicolon' - | 'Equal' - | 'Comma' - | 'Minus' - | 'Period' - | 'Slash' - | 'Backquote' - | 'BracketLeft' - | 'Backslash' - | 'BracketRight' - | 'Quote' - | 'Numpad0' - | 'Numpad1' - | 'Numpad2' - | 'Numpad3' - | 'Numpad4' - | 'Numpad5' - | 'Numpad6' - | 'Numpad7' - | 'Numpad8' - | 'Numpad9' - | 'NumpadMultiply' - | 'NumpadAdd' - | 'NumpadSubtract' - | 'NumpadDecimal' - | 'NumpadDivide'; - -export type KeyCodeExcludeModifiers = Exclude - export const enum KeyModifier { CTRL = 1, ALT = 2, diff --git a/src/enums/pref-keys.ts b/src/enums/pref-keys.ts index dc93a99..9f5018b 100755 --- a/src/enums/pref-keys.ts +++ b/src/enums/pref-keys.ts @@ -1,7 +1,9 @@ +import type { BaseSettingsStorage } from "@/utils/settings-storages/base-settings-storage"; import type { BlockFeature, CodecProfile, DeviceVibrationMode, GameBarPosition, LoadingScreenRocket, NativeMkbMode, StreamPlayerType, StreamResolution, StreamStat, StreamStatPosition, StreamVideoProcessing, TouchControllerMode, TouchControllerStyleCustom, TouchControllerStyleStandard, UiLayout, UiSection, VideoPosition, VideoPowerPreference, VideoRatio } from "./pref-values" export const enum StorageKey { GLOBAL = 'BetterXcloud', + STREAM = 'BetterXcloud.Stream', LOCALE = 'BetterXcloud.Locale', LOCALE_TRANSLATIONS = 'BetterXcloud.Locale.Translations', @@ -16,7 +18,7 @@ export const enum StorageKey { } -export const enum PrefKey { +export const enum GlobalPref { VERSION_LAST_CHECK = 'version.lastCheck', VERSION_LATEST = 'version.latest', VERSION_CURRENT = 'version.current', @@ -43,26 +45,11 @@ export const enum PrefKey { GAME_BAR_POSITION = 'gameBar.position', - LOCAL_CO_OP_ENABLED = 'localCoOp.enabled', - - DEVICE_VIBRATION_MODE = 'deviceVibration.mode', - DEVICE_VIBRATION_INTENSITY = 'deviceVibration.intensity', - - CONTROLLER_POLLING_RATE = 'controller.pollingRate', - NATIVE_MKB_MODE = 'nativeMkb.mode', NATIVE_MKB_FORCED_GAMES = 'nativeMkb.forcedGames', - NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityX', - NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityY', MKB_ENABLED = 'mkb.enabled', MKB_HIDE_IDLE_CURSOR = 'mkb.cursor.hideIdle', - MKB_P1_MAPPING_PRESET_ID = 'mkb.p1.preset.mappingId', - MKB_P1_SLOT = 'mkb.p1.slot', - MKB_P2_MAPPING_PRESET_ID = 'mkb.p2.preset.mappingId', - MKB_P2_SLOT = 'mkb.p2.slot', - - KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID = 'keyboardShortcuts.preset.inGameId', SCREENSHOT_APPLY_FILTERS = 'screenshot.applyFilters', @@ -88,6 +75,84 @@ export const enum PrefKey { UI_REDUCE_ANIMATIONS = 'ui.reduceAnimations', UI_IMAGE_QUALITY = 'ui.imageQuality', + AUDIO_MIC_ON_PLAYING = 'audio.mic.onPlaying', + AUDIO_VOLUME_CONTROL_ENABLED = 'audio.volume.booster.enabled', + + REMOTE_PLAY_ENABLED = 'xhome.enabled', + REMOTE_PLAY_STREAM_RESOLUTION = 'xhome.video.resolution', + + GAME_FORTNITE_FORCE_CONSOLE = 'game.fortnite.forceConsole', +} + +export type GlobalPrefTypeMap = { + [GlobalPref.AUDIO_MIC_ON_PLAYING]: boolean; + [GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED]: boolean; + [GlobalPref.BLOCK_FEATURES]: BlockFeature[]; + [GlobalPref.BLOCK_TRACKING]: boolean; + [GlobalPref.GAME_BAR_POSITION]: GameBarPosition; + [GlobalPref.GAME_FORTNITE_FORCE_CONSOLE]: boolean; + [GlobalPref.LOADING_SCREEN_GAME_ART]: boolean; + [GlobalPref.LOADING_SCREEN_ROCKET]: LoadingScreenRocket; + [GlobalPref.LOADING_SCREEN_SHOW_WAIT_TIME]: boolean; + [GlobalPref.MKB_ENABLED]: boolean; + [GlobalPref.MKB_HIDE_IDLE_CURSOR]: boolean; + [GlobalPref.NATIVE_MKB_FORCED_GAMES]: string[]; + [GlobalPref.NATIVE_MKB_MODE]: NativeMkbMode; + [GlobalPref.REMOTE_PLAY_ENABLED]: boolean; + [GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION]: StreamResolution; + [GlobalPref.SCREENSHOT_APPLY_FILTERS]: boolean; + [GlobalPref.SERVER_BYPASS_RESTRICTION]: string; + [GlobalPref.SERVER_PREFER_IPV6]: boolean; + [GlobalPref.SERVER_REGION]: string; + [GlobalPref.STREAM_CODEC_PROFILE]: CodecProfile; + [GlobalPref.STREAM_COMBINE_SOURCES]: boolean; + [GlobalPref.STREAM_MAX_VIDEO_BITRATE]: number; + [GlobalPref.STREAM_PREFERRED_LOCALE]: StreamPreferredLocale; + [GlobalPref.STREAM_RESOLUTION]: StreamResolution; + [GlobalPref.TOUCH_CONTROLLER_AUTO_OFF]: boolean; + [GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY]: number; + [GlobalPref.TOUCH_CONTROLLER_MODE]: TouchControllerMode; + [GlobalPref.TOUCH_CONTROLLER_STYLE_CUSTOM]: TouchControllerStyleCustom; + [GlobalPref.TOUCH_CONTROLLER_STYLE_STANDARD]: TouchControllerStyleStandard; + [GlobalPref.UI_CONTROLLER_FRIENDLY]: boolean; + [GlobalPref.UI_CONTROLLER_SHOW_STATUS]: boolean; + [GlobalPref.UI_DISABLE_FEEDBACK_DIALOG]: boolean; + [GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME]: boolean; + [GlobalPref.UI_HIDE_SECTIONS]: UiSection[]; + [GlobalPref.UI_HIDE_SYSTEM_MENU_ICON]: boolean; + [GlobalPref.UI_IMAGE_QUALITY]: number; + [GlobalPref.UI_LAYOUT]: UiLayout; + [GlobalPref.UI_REDUCE_ANIMATIONS]: boolean; + [GlobalPref.UI_SCROLLBAR_HIDE]: boolean; + [GlobalPref.UI_SIMPLIFY_STREAM_MENU]: boolean; + [GlobalPref.UI_SKIP_SPLASH_VIDEO]: boolean; + [GlobalPref.VERSION_CURRENT]: string; + [GlobalPref.VERSION_LAST_CHECK]: number; + [GlobalPref.VERSION_LATEST]: string; + + [GlobalPref.SCRIPT_LOCALE]: string; + [GlobalPref.USER_AGENT_PROFILE]: string; +} + +export const enum StreamPref { + LOCAL_CO_OP_ENABLED = 'localCoOp.enabled', + + DEVICE_VIBRATION_MODE = 'deviceVibration.mode', + DEVICE_VIBRATION_INTENSITY = 'deviceVibration.intensity', + + CONTROLLER_POLLING_RATE = 'controller.pollingRate', + CONTROLLER_SETTINGS = 'controller.settings', + + NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityX', + NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityY', + + MKB_P1_MAPPING_PRESET_ID = 'mkb.p1.preset.mappingId', + MKB_P1_SLOT = 'mkb.p1.slot', + MKB_P2_MAPPING_PRESET_ID = 'mkb.p2.preset.mappingId', + MKB_P2_SLOT = 'mkb.p2.slot', + + KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID = 'keyboardShortcuts.preset.inGameId', + VIDEO_PLAYER_TYPE = 'video.player.type', VIDEO_POWER_PREFERENCE = 'video.player.powerPreference', VIDEO_PROCESSING = 'video.processing', @@ -99,8 +164,6 @@ export const enum PrefKey { VIDEO_SATURATION = 'video.saturation', VIDEO_POSITION = 'video.position', - AUDIO_MIC_ON_PLAYING = 'audio.mic.onPlaying', - AUDIO_VOLUME_CONTROL_ENABLED = 'audio.volume.booster.enabled', AUDIO_VOLUME = 'audio.volume', STATS_ITEMS = 'stats.items', @@ -111,85 +174,137 @@ export const enum PrefKey { STATS_OPACITY_ALL = 'stats.opacity.all', STATS_OPACITY_BACKGROUND = 'stats.opacity.background', STATS_CONDITIONAL_FORMATTING = 'stats.colors', - - REMOTE_PLAY_ENABLED = 'xhome.enabled', - REMOTE_PLAY_STREAM_RESOLUTION = 'xhome.video.resolution', - - GAME_FORTNITE_FORCE_CONSOLE = 'game.fortnite.forceConsole', } - -export type PrefTypeMap = { - [PrefKey.AUDIO_MIC_ON_PLAYING]: boolean, - [PrefKey.AUDIO_VOLUME_CONTROL_ENABLED]: boolean, - [PrefKey.AUDIO_VOLUME]: number, - [PrefKey.BLOCK_FEATURES]: BlockFeature[], - [PrefKey.BLOCK_TRACKING]: boolean, - [PrefKey.CONTROLLER_POLLING_RATE]: number, - [PrefKey.DEVICE_VIBRATION_INTENSITY]: number, - [PrefKey.DEVICE_VIBRATION_MODE]: DeviceVibrationMode, - [PrefKey.GAME_BAR_POSITION]: GameBarPosition, - [PrefKey.GAME_FORTNITE_FORCE_CONSOLE]: boolean, - [PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID]: number, - [PrefKey.LOADING_SCREEN_GAME_ART]: boolean, - [PrefKey.LOADING_SCREEN_ROCKET]: LoadingScreenRocket, - [PrefKey.LOADING_SCREEN_SHOW_WAIT_TIME]: boolean, - [PrefKey.LOCAL_CO_OP_ENABLED]: boolean, - [PrefKey.MKB_ENABLED]: boolean, - [PrefKey.MKB_HIDE_IDLE_CURSOR]: boolean, - [PrefKey.MKB_P1_MAPPING_PRESET_ID]: number, - [PrefKey.MKB_P1_SLOT]: number, - [PrefKey.NATIVE_MKB_FORCED_GAMES]: string[], - [PrefKey.NATIVE_MKB_MODE]: NativeMkbMode, - [PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: number, - [PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY]: number, - [PrefKey.REMOTE_PLAY_ENABLED]: boolean, - [PrefKey.REMOTE_PLAY_STREAM_RESOLUTION]: StreamResolution, - [PrefKey.SCREENSHOT_APPLY_FILTERS]: boolean, - [PrefKey.SERVER_BYPASS_RESTRICTION]: string, - [PrefKey.SERVER_PREFER_IPV6]: boolean, - [PrefKey.SERVER_REGION]: string, - [PrefKey.STATS_CONDITIONAL_FORMATTING]: boolean, - [PrefKey.STATS_ITEMS]: StreamStat[], - [PrefKey.STATS_OPACITY_ALL]: number, - [PrefKey.STATS_OPACITY_BACKGROUND]: number, - [PrefKey.STATS_POSITION]: StreamStatPosition, - [PrefKey.STATS_QUICK_GLANCE_ENABLED]: boolean, - [PrefKey.STATS_SHOW_WHEN_PLAYING]: boolean, - [PrefKey.STATS_TEXT_SIZE]: string, - [PrefKey.STREAM_CODEC_PROFILE]: CodecProfile, - [PrefKey.STREAM_COMBINE_SOURCES]: boolean, - [PrefKey.STREAM_MAX_VIDEO_BITRATE]: number, - [PrefKey.STREAM_PREFERRED_LOCALE]: StreamPreferredLocale, - [PrefKey.STREAM_RESOLUTION]: StreamResolution, - [PrefKey.TOUCH_CONTROLLER_AUTO_OFF]: boolean, - [PrefKey.TOUCH_CONTROLLER_DEFAULT_OPACITY]: number, - [PrefKey.TOUCH_CONTROLLER_MODE]: TouchControllerMode, - [PrefKey.TOUCH_CONTROLLER_STYLE_CUSTOM]: TouchControllerStyleCustom, - [PrefKey.TOUCH_CONTROLLER_STYLE_STANDARD]: TouchControllerStyleStandard, - [PrefKey.UI_CONTROLLER_FRIENDLY]: boolean, - [PrefKey.UI_CONTROLLER_SHOW_STATUS]: boolean, - [PrefKey.UI_DISABLE_FEEDBACK_DIALOG]: boolean, - [PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME]: boolean, - [PrefKey.UI_HIDE_SECTIONS]: UiSection[], - [PrefKey.UI_HIDE_SYSTEM_MENU_ICON]: boolean, - [PrefKey.UI_IMAGE_QUALITY]: number, - [PrefKey.UI_LAYOUT]: UiLayout, - [PrefKey.UI_REDUCE_ANIMATIONS]: boolean, - [PrefKey.UI_SCROLLBAR_HIDE]: boolean, - [PrefKey.UI_SIMPLIFY_STREAM_MENU]: boolean, - [PrefKey.UI_SKIP_SPLASH_VIDEO]: boolean, - [PrefKey.VERSION_CURRENT]: string, - [PrefKey.VERSION_LAST_CHECK]: number, - [PrefKey.VERSION_LATEST]: string, - [PrefKey.VIDEO_BRIGHTNESS]: number, - [PrefKey.VIDEO_CONTRAST]: number, - [PrefKey.VIDEO_MAX_FPS]: number, - [PrefKey.VIDEO_PLAYER_TYPE]: StreamPlayerType, - [PrefKey.VIDEO_POSITION]: VideoPosition, - [PrefKey.VIDEO_POWER_PREFERENCE]: VideoPowerPreference, - [PrefKey.VIDEO_PROCESSING]: StreamVideoProcessing, - [PrefKey.VIDEO_RATIO]: VideoRatio, - [PrefKey.VIDEO_SATURATION]: number, - [PrefKey.VIDEO_SHARPNESS]: number, +export type StreamPrefTypeMap = { + [StreamPref.AUDIO_VOLUME]: number; + [StreamPref.CONTROLLER_POLLING_RATE]: number; + [StreamPref.CONTROLLER_SETTINGS]: ControllerSettings; + [StreamPref.DEVICE_VIBRATION_INTENSITY]: number; + [StreamPref.DEVICE_VIBRATION_MODE]: DeviceVibrationMode; + [StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID]: number; + [StreamPref.LOCAL_CO_OP_ENABLED]: boolean; + [StreamPref.MKB_P1_MAPPING_PRESET_ID]: number; + [StreamPref.MKB_P1_SLOT]: number; + [StreamPref.MKB_P2_MAPPING_PRESET_ID]: number; + [StreamPref.MKB_P2_SLOT]: number; + [StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: number; + [StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY]: number; + [StreamPref.STATS_CONDITIONAL_FORMATTING]: boolean; + [StreamPref.STATS_ITEMS]: StreamStat[]; + [StreamPref.STATS_OPACITY_ALL]: number; + [StreamPref.STATS_OPACITY_BACKGROUND]: number; + [StreamPref.STATS_POSITION]: StreamStatPosition; + [StreamPref.STATS_QUICK_GLANCE_ENABLED]: boolean; + [StreamPref.STATS_SHOW_WHEN_PLAYING]: boolean; + [StreamPref.STATS_TEXT_SIZE]: string; + [StreamPref.VIDEO_BRIGHTNESS]: number; + [StreamPref.VIDEO_CONTRAST]: number; + [StreamPref.VIDEO_MAX_FPS]: number; + [StreamPref.VIDEO_PLAYER_TYPE]: StreamPlayerType; + [StreamPref.VIDEO_POSITION]: VideoPosition; + [StreamPref.VIDEO_POWER_PREFERENCE]: VideoPowerPreference; + [StreamPref.VIDEO_PROCESSING]: StreamVideoProcessing; + [StreamPref.VIDEO_RATIO]: VideoRatio; + [StreamPref.VIDEO_SATURATION]: number; + [StreamPref.VIDEO_SHARPNESS]: number; } + +export type AllPrefs = GlobalPref | StreamPref; + +export const ALL_PREFS: { + global: GlobalPref[], + stream: StreamPref[], +} = { + global: [ + GlobalPref.AUDIO_MIC_ON_PLAYING, + GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED, + GlobalPref.BLOCK_FEATURES, + GlobalPref.BLOCK_TRACKING, + GlobalPref.GAME_BAR_POSITION, + GlobalPref.GAME_FORTNITE_FORCE_CONSOLE, + GlobalPref.LOADING_SCREEN_GAME_ART, + GlobalPref.LOADING_SCREEN_ROCKET, + GlobalPref.LOADING_SCREEN_SHOW_WAIT_TIME, + GlobalPref.MKB_ENABLED, + GlobalPref.MKB_HIDE_IDLE_CURSOR, + GlobalPref.NATIVE_MKB_FORCED_GAMES, + GlobalPref.NATIVE_MKB_MODE, + GlobalPref.REMOTE_PLAY_ENABLED, + GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION, + GlobalPref.SCREENSHOT_APPLY_FILTERS, + GlobalPref.SERVER_BYPASS_RESTRICTION, + GlobalPref.SERVER_PREFER_IPV6, + GlobalPref.SERVER_REGION, + GlobalPref.STREAM_CODEC_PROFILE, + GlobalPref.STREAM_COMBINE_SOURCES, + GlobalPref.STREAM_MAX_VIDEO_BITRATE, + GlobalPref.STREAM_PREFERRED_LOCALE, + GlobalPref.STREAM_RESOLUTION, + GlobalPref.TOUCH_CONTROLLER_AUTO_OFF, + GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY, + GlobalPref.TOUCH_CONTROLLER_MODE, + GlobalPref.TOUCH_CONTROLLER_STYLE_CUSTOM, + GlobalPref.TOUCH_CONTROLLER_STYLE_STANDARD, + GlobalPref.UI_CONTROLLER_FRIENDLY, + GlobalPref.UI_CONTROLLER_SHOW_STATUS, + GlobalPref.UI_DISABLE_FEEDBACK_DIALOG, + GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME, + GlobalPref.UI_HIDE_SECTIONS, + GlobalPref.UI_HIDE_SYSTEM_MENU_ICON, + GlobalPref.UI_IMAGE_QUALITY, + GlobalPref.UI_LAYOUT, + GlobalPref.UI_REDUCE_ANIMATIONS, + GlobalPref.UI_SCROLLBAR_HIDE, + GlobalPref.UI_SIMPLIFY_STREAM_MENU, + GlobalPref.UI_SKIP_SPLASH_VIDEO, + GlobalPref.VERSION_CURRENT, + GlobalPref.VERSION_LAST_CHECK, + GlobalPref.VERSION_LATEST, + + GlobalPref.SCRIPT_LOCALE, + GlobalPref.USER_AGENT_PROFILE, + ], + stream: [ + StreamPref.AUDIO_VOLUME, + StreamPref.CONTROLLER_POLLING_RATE, + StreamPref.CONTROLLER_SETTINGS, + StreamPref.DEVICE_VIBRATION_INTENSITY, + StreamPref.DEVICE_VIBRATION_MODE, + StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, + StreamPref.LOCAL_CO_OP_ENABLED, + StreamPref.MKB_P1_MAPPING_PRESET_ID, + StreamPref.MKB_P1_SLOT, + StreamPref.MKB_P2_MAPPING_PRESET_ID, + StreamPref.MKB_P2_SLOT, + StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY, + StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY, + StreamPref.STATS_CONDITIONAL_FORMATTING, + StreamPref.STATS_ITEMS, + StreamPref.STATS_OPACITY_ALL, + StreamPref.STATS_OPACITY_BACKGROUND, + StreamPref.STATS_POSITION, + StreamPref.STATS_QUICK_GLANCE_ENABLED, + StreamPref.STATS_SHOW_WHEN_PLAYING, + StreamPref.STATS_TEXT_SIZE, + StreamPref.VIDEO_BRIGHTNESS, + StreamPref.VIDEO_CONTRAST, + StreamPref.VIDEO_MAX_FPS, + StreamPref.VIDEO_PLAYER_TYPE, + StreamPref.VIDEO_POSITION, + StreamPref.VIDEO_POWER_PREFERENCE, + StreamPref.VIDEO_PROCESSING, + StreamPref.VIDEO_RATIO, + StreamPref.VIDEO_SATURATION, + StreamPref.VIDEO_SHARPNESS, + ], +} as const; + +export type AnySettingsStorage = BaseSettingsStorage | BaseSettingsStorage; +export type AnyPref = GlobalPref | StreamPref; + +export type PrefTypeMap = Key extends GlobalPref + ? GlobalPrefTypeMap + : Key extends StreamPref + ? StreamPrefTypeMap + : never; diff --git a/src/index.ts b/src/index.ts index 38a00fc..f3288c0 100755 --- a/src/index.ts +++ b/src/index.ts @@ -32,8 +32,7 @@ import { HeaderSection } from "./modules/ui/header"; import { GameTile } from "./modules/ui/game-tile"; import { ProductDetailsPage } from "./modules/ui/product-details"; import { NavigationDialogManager } from "./modules/ui/dialog/navigation-dialog"; -import { PrefKey } from "./enums/pref-keys"; -import { getPref } from "./utils/settings-storages/global-settings-storage"; +import { GlobalPref, StreamPref } from "./enums/pref-keys"; import { SettingsDialog } from "./modules/ui/dialog/settings-dialog"; import { StreamUiHandler } from "./modules/stream/stream-ui"; import { UserAgent } from "./utils/user-agent"; @@ -45,6 +44,11 @@ import { KeyboardShortcutHandler } from "./modules/mkb/keyboard-shortcut-handler import { GhPagesUtils } from "./utils/gh-pages"; import { DeviceVibrationManager } from "./modules/device-vibration-manager"; import { BxEventBus } from "./utils/bx-event-bus"; +import { getGlobalPref, getStreamPref } from "./utils/pref-utils"; +import { SettingsManager } from "./modules/settings-manager"; +import { Toast } from "./utils/toast"; + +SettingsManager.getInstance(); // Handle login page if (window.location.pathname.includes('/auth/msa')) { @@ -173,7 +177,7 @@ document.addEventListener('readystatechange', e => { } // Hide "Play with Friends" skeleton section - if (getPref(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) || getPref(PrefKey.BLOCK_FEATURES).includes(BlockFeature.FRIENDS)) { + if (getGlobalPref(GlobalPref.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) || getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.FRIENDS)) { const $parent = document.querySelector('div[class*=PlayWithFriendsSkeleton]')?.closest('div[class*=HomePage-module]'); $parent && ($parent.style.display = 'none'); } @@ -219,7 +223,7 @@ BxEventBus.Stream.on('state.loading', () => { }); // Setup loading screen -getPref(PrefKey.LOADING_SCREEN_GAME_ART) && BxEventBus.Script.on('titleInfo.ready', LoadingScreen.setup); +getGlobalPref(GlobalPref.LOADING_SCREEN_GAME_ART) && BxEventBus.Script.on('titleInfo.ready', LoadingScreen.setup); BxEventBus.Stream.on('state.starting', () => { // Hide loading screen @@ -260,7 +264,10 @@ BxEventBus.Stream.on('state.playing', payload => { ScreenshotManager.getInstance().updateCanvasSize($video.videoWidth, $video.videoHeight); // Setup local co-op - getPref(PrefKey.LOCAL_CO_OP_ENABLED) && BxExposed.toggleLocalCoOp(getPref(PrefKey.LOCAL_CO_OP_ENABLED)); + if (getStreamPref(StreamPref.LOCAL_CO_OP_ENABLED)) { + BxExposed.toggleLocalCoOp(true); + Toast.show(t('local-co-op'), t('enabled')); + } } @@ -295,20 +302,32 @@ BxEventBus.Stream.on('dataChannelCreated', payload => { } // Get xboxTitleId from message + const currentStream = STATES.currentStream; const json = JSON.parse(JSON.parse(msg.data).content); - const xboxTitleId = parseInt(json.titleid, 16); - STATES.currentStream.xboxTitleId = xboxTitleId; + const currentId = currentStream.xboxTitleId ?? null; + let newId: number = parseInt(json.titleid, 16); // Get titleSlug for Remote Play if (STATES.remotePlay.isPlaying) { - STATES.currentStream.titleSlug = 'remote-play'; + currentStream.titleSlug = 'remote-play'; if (json.focused) { - const productTitle = await XboxApi.getProductTitle(xboxTitleId); + const productTitle = await XboxApi.getProductTitle(newId); if (productTitle) { - STATES.currentStream.titleSlug = productTitleToSlug(productTitle); + currentStream.titleSlug = productTitleToSlug(productTitle); + } else { + newId = -1; } + } else { + newId = 0; } } + + if (currentId !== newId) { + currentStream.xboxTitleId = newId; + BxEventBus.Stream.emit('xboxTitleId.changed', { + id: newId, + }); + } }); }); @@ -345,6 +364,8 @@ function unload() { TouchController.reset(); GameBar.getInstance()?.disable(); + + BxEventBus.Stream.emit('xboxTitleId.changed', { id: -1 }); } } @@ -362,8 +383,8 @@ function main() { GhPagesUtils.fetchLatestCommit(); if (isFullVersion()) { - if (getPref(PrefKey.NATIVE_MKB_MODE) !== NativeMkbMode.OFF) { - const customList = getPref(PrefKey.NATIVE_MKB_FORCED_GAMES); + if (getGlobalPref(GlobalPref.NATIVE_MKB_MODE) !== NativeMkbMode.OFF) { + const customList = getGlobalPref(GlobalPref.NATIVE_MKB_FORCED_GAMES); BX_FLAGS.ForceNativeMkbTitles.push(...customList); } } @@ -378,9 +399,9 @@ function main() { patchCanvasContext(); isFullVersion() && AppInterface && patchPointerLockApi(); - getPref(PrefKey.AUDIO_VOLUME_CONTROL_ENABLED) && patchAudioContext(); + getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED) && patchAudioContext(); - if (getPref(PrefKey.BLOCK_TRACKING)) { + if (getGlobalPref(GlobalPref.BLOCK_TRACKING)) { patchMeControl(); disableAdobeAudienceManager(); } @@ -407,28 +428,28 @@ function main() { disablePwa(); // Preload Remote Play - if (getPref(PrefKey.REMOTE_PLAY_ENABLED)) { + if (getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) { RemotePlayManager.detect(); } - if (getPref(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL) { + if (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL) { TouchController.setup(); } // Start PointerProviderServer - if (AppInterface && (getPref(PrefKey.MKB_ENABLED) || getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON)) { + if (AppInterface && (getGlobalPref(GlobalPref.MKB_ENABLED) || getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.ON)) { STATES.pointerServerPort = AppInterface.startPointerServer() || 9269; BxLogger.info('startPointerServer', 'Port', STATES.pointerServerPort.toString()); } // Show wait time in game card - getPref(PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME) && GameTile.setup(); + getGlobalPref(GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME) && GameTile.setup(); EmulatedMkbHandler.setupEvents(); } // Show a toast when connecting/disconecting controller - if (getPref(PrefKey.UI_CONTROLLER_SHOW_STATUS)) { + if (getGlobalPref(GlobalPref.UI_CONTROLLER_SHOW_STATUS)) { window.addEventListener('gamepadconnected', e => showGamepadToast(e.gamepad)); window.addEventListener('gamepaddisconnected', e => showGamepadToast(e.gamepad)); } diff --git a/src/modules/device-vibration-manager.ts b/src/modules/device-vibration-manager.ts index f3aa5d7..d703add 100755 --- a/src/modules/device-vibration-manager.ts +++ b/src/modules/device-vibration-manager.ts @@ -47,7 +47,7 @@ export class DeviceVibrationManager { } }); - BxEventBus.Script.on('deviceVibration.updated', () => this.setupDataChannel()); + BxEventBus.Stream.on('deviceVibration.updated', () => this.setupDataChannel()); } private setupDataChannel() { diff --git a/src/modules/game-bar/game-bar.ts b/src/modules/game-bar/game-bar.ts index e8c339f..aeab255 100755 --- a/src/modules/game-bar/game-bar.ts +++ b/src/modules/game-bar/game-bar.ts @@ -6,21 +6,21 @@ import { BxIcon } from "@utils/bx-icon"; import type { BaseGameBarAction } from "./base-action"; import { STATES } from "@utils/global"; import { MicrophoneAction } from "./microphone-action"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref } from "@/enums/pref-keys"; import { TrueAchievementsAction } from "./true-achievements-action"; import { SpeakerAction } from "./speaker-action"; import { RendererAction } from "./renderer-action"; import { BxLogger } from "@/utils/bx-logger"; import { GameBarPosition, TouchControllerMode } from "@/enums/pref-values"; import { BxEventBus } from "@/utils/bx-event-bus"; +import { getGlobalPref } from "@/utils/pref-utils"; export class GameBar { private static instance: GameBar | null | undefined; public static getInstance(): typeof GameBar['instance'] { if (typeof GameBar.instance === 'undefined') { - if (getPref(PrefKey.GAME_BAR_POSITION) !== GameBarPosition.OFF) { + if (getGlobalPref(GlobalPref.GAME_BAR_POSITION) !== GameBarPosition.OFF) { GameBar.instance = new GameBar(); } else { GameBar.instance = null; @@ -46,7 +46,7 @@ export class GameBar { let $container; - const position = getPref(PrefKey.GAME_BAR_POSITION); + const position = getGlobalPref(GlobalPref.GAME_BAR_POSITION); const $gameBar = CE('div', { id: 'bx-game-bar', class: 'bx-gone', 'data-position': position }, $container = CE('div', { class: 'bx-game-bar-container bx-offscreen' }), @@ -55,7 +55,7 @@ export class GameBar { this.actions = [ new ScreenshotAction(), - ...(STATES.userAgent.capabilities.touch && (getPref(PrefKey.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.OFF) ? [new TouchControlAction()] : []), + ...(STATES.userAgent.capabilities.touch && (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.OFF) ? [new TouchControlAction()] : []), new SpeakerAction(), new RendererAction(), new MicrophoneAction(), diff --git a/src/modules/loading-screen.ts b/src/modules/loading-screen.ts index c7b7853..4fbfc5d 100755 --- a/src/modules/loading-screen.ts +++ b/src/modules/loading-screen.ts @@ -2,8 +2,8 @@ import { CE } from "@utils/html"; import { getPreferredServerRegion } from "@utils/region"; import { t } from "@utils/translation"; import { STATES } from "@utils/global"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref } from "@/enums/pref-keys"; +import { getGlobalPref } from "@/utils/pref-utils"; import { compressCss } from "@macros/build" with { type: "macro" }; import { LoadingScreenRocket } from "@/enums/pref-values"; @@ -37,7 +37,7 @@ export class LoadingScreen { LoadingScreen.setBackground(titleInfo.product.heroImageUrl || titleInfo.product.titledHeroImageUrl || titleInfo.product.tileImageUrl); - if (getPref(PrefKey.LOADING_SCREEN_ROCKET) === LoadingScreenRocket.HIDE) { + if (getGlobalPref(GlobalPref.LOADING_SCREEN_ROCKET) === LoadingScreenRocket.HIDE) { LoadingScreen.hideRocket(); } } @@ -63,7 +63,7 @@ export class LoadingScreen { // Limit max width to reduce image size imageUrl = imageUrl + '?w=1920'; - const imageQuality = getPref(PrefKey.UI_IMAGE_QUALITY); + const imageQuality = getGlobalPref(GlobalPref.UI_IMAGE_QUALITY); if (imageQuality !== 90) { imageUrl += '&q=' + imageQuality; } @@ -94,7 +94,7 @@ export class LoadingScreen { static setupWaitTime(waitTime: number) { // Hide rocket when queing - if (getPref(PrefKey.LOADING_SCREEN_ROCKET) === LoadingScreenRocket.HIDE_QUEUE) { + if (getGlobalPref(GlobalPref.LOADING_SCREEN_ROCKET) === LoadingScreenRocket.HIDE_QUEUE) { LoadingScreen.hideRocket(); } @@ -151,7 +151,7 @@ export class LoadingScreen { LoadingScreen.orgWebTitle && (document.title = LoadingScreen.orgWebTitle); LoadingScreen.$waitTimeBox && LoadingScreen.$waitTimeBox.classList.add('bx-gone'); - if (getPref(PrefKey.LOADING_SCREEN_GAME_ART) && LoadingScreen.$bgStyle) { + if (getGlobalPref(GlobalPref.LOADING_SCREEN_GAME_ART) && LoadingScreen.$bgStyle) { const $rocketBg = document.querySelector('#game-stream rect[width="800"]'); $rocketBg && $rocketBg.addEventListener('transitionend', e => { LoadingScreen.$bgStyle.textContent += compressCss(` diff --git a/src/modules/mkb/key-helper.ts b/src/modules/mkb/key-helper.ts index 86e2306..282fbc5 100755 --- a/src/modules/mkb/key-helper.ts +++ b/src/modules/mkb/key-helper.ts @@ -1,4 +1,4 @@ -import { MouseButtonCode, WheelCode, type KeyCode } from "@/enums/mkb"; +import { MouseButtonCode, WheelCode } from "@/enums/mkb"; export const enum KeyModifier { CTRL = 1, diff --git a/src/modules/mkb/mkb-handler.ts b/src/modules/mkb/mkb-handler.ts index 9ba3857..fe8c8db 100755 --- a/src/modules/mkb/mkb-handler.ts +++ b/src/modules/mkb/mkb-handler.ts @@ -11,8 +11,8 @@ import { BxLogger } from "@utils/bx-logger"; import { PointerClient } from "./pointer-client"; import { NativeMkbHandler } from "./native-mkb-handler"; import { MkbHandler, MouseDataProvider } from "./base-mkb-handler"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref, StreamPref } from "@/enums/pref-keys"; +import { getGlobalPref, getStreamPref } from "@/utils/pref-utils"; import { GamepadKey, GamepadStick } from "@/enums/gamepad"; import { MkbPopup } from "./mkb-popup"; import type { MkbConvertedPresetData } from "@/types/presets"; @@ -134,7 +134,7 @@ export class EmulatedMkbHandler extends MkbHandler { private static readonly LOG_TAG = 'EmulatedMkbHandler'; static isAllowed() { - return getPref(PrefKey.MKB_ENABLED) && (AppInterface || !UserAgent.isMobile()); + return getGlobalPref(GlobalPref.MKB_ENABLED) && (AppInterface || !UserAgent.isMobile()); } private PRESET!: MkbConvertedPresetData | null; @@ -233,10 +233,10 @@ export class EmulatedMkbHandler extends MkbHandler { private vectorLength = (x: number, y: number): number => Math.sqrt(x ** 2 + y ** 2); resetXcloudGamepads() { - const index = getPref(PrefKey.MKB_P1_SLOT) - 1; + const index = getStreamPref(StreamPref.MKB_P1_SLOT) - 1; this.xCloudGamepad = generateVirtualControllerMapping(0, { - GamepadIndex: getPref(PrefKey.LOCAL_CO_OP_ENABLED) ? index : 0, + GamepadIndex: getStreamPref(StreamPref.LOCAL_CO_OP_ENABLED) ? index : 0, Dirty: true, }); this.VIRTUAL_GAMEPAD.index = index; @@ -590,7 +590,7 @@ export class EmulatedMkbHandler extends MkbHandler { this.isPolling = true; this.escKeyDownTime = -1; - window.BX_EXPOSED.toggleLocalCoOp(getPref(PrefKey.LOCAL_CO_OP_ENABLED)); + window.BX_EXPOSED.toggleLocalCoOp(getStreamPref(StreamPref.LOCAL_CO_OP_ENABLED)); this.resetXcloudGamepads(); window.navigator.getGamepads = this.patchedGetGamepads; @@ -650,7 +650,7 @@ export class EmulatedMkbHandler extends MkbHandler { }); if (EmulatedMkbHandler.isAllowed()) { - BxEventBus.Script.on('mkb.setting.updated', () => { + BxEventBus.Stream.on('mkb.setting.updated', () => { EmulatedMkbHandler.getInstance()?.refreshPresetData(); }); } diff --git a/src/modules/mkb/mkb-popup.ts b/src/modules/mkb/mkb-popup.ts index 93be796..49d27c9 100755 --- a/src/modules/mkb/mkb-popup.ts +++ b/src/modules/mkb/mkb-popup.ts @@ -25,7 +25,7 @@ export class MkbPopup { constructor() { this.render(); - BxEventBus.Script.on('keyboardShortcuts.updated', () => { + BxEventBus.Stream.on('keyboardShortcuts.updated', () => { const $newButton = this.createActivateButton(); this.$btnActivate.replaceWith($newButton); this.$btnActivate = $newButton; diff --git a/src/modules/mkb/mouse-cursor-hider.ts b/src/modules/mkb/mouse-cursor-hider.ts index 1bbf475..d6b072b 100755 --- a/src/modules/mkb/mouse-cursor-hider.ts +++ b/src/modules/mkb/mouse-cursor-hider.ts @@ -1,11 +1,11 @@ -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref } from "@/enums/pref-keys"; +import { getGlobalPref } from "@/utils/pref-utils"; export class MouseCursorHider { private static instance: MouseCursorHider | null | undefined; public static getInstance(): typeof MouseCursorHider['instance'] { if (typeof MouseCursorHider.instance === 'undefined') { - if (!getPref(PrefKey.MKB_ENABLED) && getPref(PrefKey.MKB_HIDE_IDLE_CURSOR)) { + if (!getGlobalPref(GlobalPref.MKB_ENABLED) && getGlobalPref(GlobalPref.MKB_HIDE_IDLE_CURSOR)) { MouseCursorHider.instance = new MouseCursorHider(); } else { MouseCursorHider.instance = null; diff --git a/src/modules/mkb/native-mkb-handler.ts b/src/modules/mkb/native-mkb-handler.ts index 97d7821..2a99ad7 100755 --- a/src/modules/mkb/native-mkb-handler.ts +++ b/src/modules/mkb/native-mkb-handler.ts @@ -4,8 +4,7 @@ import { AppInterface, STATES } from "@/utils/global"; import { MkbHandler } from "./base-mkb-handler"; import { t } from "@/utils/translation"; import { BxEvent } from "@/utils/bx-event"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref, StreamPref } from "@/enums/pref-keys"; import { BxLogger } from "@/utils/bx-logger"; import { MkbPopup } from "./mkb-popup"; import { KeyHelper } from "./key-helper"; @@ -13,7 +12,7 @@ import { StreamSettings } from "@/utils/stream-settings"; import { ShortcutAction } from "@/enums/shortcut-actions"; import { NativeMkbMode } from "@/enums/pref-values"; import { BxEventBus } from "@/utils/bx-event-bus"; -import type { NativeMouseData, XcloudInputChannel } from "@/utils/gamepad"; +import { getStreamPref, getGlobalPref } from "@/utils/pref-utils"; export class NativeMkbHandler extends MkbHandler { private static instance: NativeMkbHandler | null | undefined; @@ -31,7 +30,7 @@ export class NativeMkbHandler extends MkbHandler { private readonly LOG_TAG = 'NativeMkbHandler'; static isAllowed = () => { - return STATES.browser.capabilities.emulatedNativeMkb && getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON; + return STATES.browser.capabilities.emulatedNativeMkb && getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.ON; } private pointerClient: PointerClient | undefined; @@ -113,8 +112,8 @@ export class NativeMkbHandler extends MkbHandler { Toast.show('Cannot enable Mouse & Keyboard feature'); } - this.mouseVerticalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY); - this.mouseHorizontalMultiply = getPref(PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY); + this.mouseVerticalMultiply = getStreamPref(StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY); + this.mouseHorizontalMultiply = getStreamPref(StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY); window.addEventListener('keyup', this); diff --git a/src/modules/patcher/patcher.ts b/src/modules/patcher/patcher.ts index b0655cd..ec588bb 100755 --- a/src/modules/patcher/patcher.ts +++ b/src/modules/patcher/patcher.ts @@ -11,8 +11,8 @@ import codeGameCardIcons from "./patches/game-card-icons.js" with { type: "text" import codeLocalCoOpEnable from "./patches/local-co-op-enable.js" with { type: "text" }; import codeRemotePlayKeepAlive from "./patches/remote-play-keep-alive.js" with { type: "text" }; import codeVibrationAdjust from "./patches/vibration-adjust.js" with { type: "text" }; -import { PrefKey, StorageKey } from "@/enums/pref-keys.js"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref, StorageKey } from "@/enums/pref-keys.js"; +import { getGlobalPref } from "@/utils/pref-utils.js"; import { GamePassCloudGallery } from "@/enums/game-pass-gallery"; import { t } from "@/utils/translation"; import { BlockFeature, NativeMkbMode, TouchControllerMode, UiLayout, UiSection } from "@/enums/pref-values"; @@ -89,7 +89,7 @@ const PATCHES = { return false; } - const layout = getPref(PrefKey.UI_LAYOUT) === UiLayout.TV ? UiLayout.TV : UiLayout.DEFAULT; + const layout = getGlobalPref(GlobalPref.UI_LAYOUT) === UiLayout.TV ? UiLayout.TV : UiLayout.DEFAULT; return str.replace(text, `?"${layout}":"${layout}"`); }, @@ -189,7 +189,7 @@ remotePlayServerId: (window.BX_REMOTE_PLAY_CONFIG && window.BX_REMOTE_PLAY_CONFI str = PatcherUtils.replaceWith(str, setTimeoutIndex, tmp, tmpPatched); // Block gamepad stats collecting - if (getPref(PrefKey.BLOCK_TRACKING)) { + if (getGlobalPref(GlobalPref.BLOCK_TRACKING)) { codeBlock = codeBlock.replace('this.inputPollingIntervalStats.addValue', ''); codeBlock = codeBlock.replace('this.inputPollingDurationStats.addValue', ''); } @@ -377,9 +377,9 @@ if (window.BX_EXPOSED.stopTakRendering) { } let autoOffCode = ''; - if (getPref(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF) { + if (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF) { autoOffCode = 'return;'; - } else if (getPref(PrefKey.TOUCH_CONTROLLER_AUTO_OFF)) { + } else if (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_AUTO_OFF)) { autoOffCode = ` const gamepads = window.navigator.getGamepads(); let gamepadFound = false; @@ -434,7 +434,7 @@ e.guideUI = null; `; // Remove the TAK Edit button when the touch controller is disabled - if (getPref(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF) { + if (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF) { newCode += 'e.canShowTakHUD = false;'; } @@ -554,7 +554,7 @@ BxLogger.info('patchRemotePlayMkb', ${configsVar}); return false; } - const opacity = (getPref(PrefKey.TOUCH_CONTROLLER_DEFAULT_OPACITY) / 100).toFixed(1); + const opacity = (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY) / 100).toFixed(1); const newCode = `opacityMultiplier: ${opacity}`; str = str.replace(text, newCode); return str; @@ -790,7 +790,7 @@ true` + text; return false; } - const PREF_HIDE_SECTIONS = getPref(PrefKey.UI_HIDE_SECTIONS); + const PREF_HIDE_SECTIONS = getGlobalPref(GlobalPref.UI_HIDE_SECTIONS); const siglIds: GamePassCloudGallery[] = []; const sections: PartialRecord = { @@ -981,7 +981,7 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) { // Find index after { index = str.indexOf('{', index) + 1; - const blockFeatures = getPref(PrefKey.BLOCK_FEATURES); + const blockFeatures = getGlobalPref(GlobalPref.BLOCK_FEATURES); const filters = []; if (blockFeatures.includes(BlockFeature.NOTIFICATIONS_INVITES)) { filters.push('GameInvite', 'PartyInvite'); @@ -1097,7 +1097,7 @@ ${subsVar} = subs; // Find "return" keyword index = PatcherUtils.indexOf(str, 'return', index, 200); - const newCode = `${paramVar}.set('q', ${getPref(PrefKey.UI_IMAGE_QUALITY)});`; + const newCode = `${paramVar}.set('q', ${getGlobalPref(GlobalPref.UI_IMAGE_QUALITY)});`; str = PatcherUtils.insertAt(str, index, newCode); return str; @@ -1111,13 +1111,13 @@ ${subsVar} = subs; return false; } - str = PatcherUtils.insertAt(str, index, `&q=${getPref(PrefKey.UI_IMAGE_QUALITY)}`); + str = PatcherUtils.insertAt(str, index, `&q=${getGlobalPref(GlobalPref.UI_IMAGE_QUALITY)}`); return str; } }; let PATCH_ORDERS = PatcherUtils.filterPatches([ - ...(AppInterface && getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON ? [ + ...(AppInterface && getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.ON ? [ 'enableNativeMkb', 'disableAbsoluteMouse', ] : []), @@ -1126,7 +1126,7 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([ 'gameCardCustomIcons', // 'gameCardPassTitle', - ...(getPref(PrefKey.UI_IMAGE_QUALITY) < 90 ? [ + ...(getGlobalPref(GlobalPref.UI_IMAGE_QUALITY) < 90 ? [ 'setImageQuality', ] : []), @@ -1154,16 +1154,16 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([ 'supportLocalCoOp', 'overrideStorageGetSettings', - getPref(PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME) && 'patchSetCurrentFocus', + getGlobalPref(GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME) && 'patchSetCurrentFocus', - getPref(PrefKey.UI_LAYOUT) !== UiLayout.DEFAULT && 'websiteLayout', - getPref(PrefKey.GAME_FORTNITE_FORCE_CONSOLE) && 'forceFortniteConsole', + getGlobalPref(GlobalPref.UI_LAYOUT) !== UiLayout.DEFAULT && 'websiteLayout', + getGlobalPref(GlobalPref.GAME_FORTNITE_FORCE_CONSOLE) && 'forceFortniteConsole', ...(STATES.userAgent.capabilities.touch ? [ 'disableTouchContextMenu', ] : []), - ...(getPref(PrefKey.BLOCK_TRACKING) ? [ + ...(getGlobalPref(GlobalPref.BLOCK_TRACKING) ? [ 'disableAiTrack', 'disableTelemetry', @@ -1173,7 +1173,7 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([ 'disableTelemetryProvider', ] : []), - ...(getPref(PrefKey.REMOTE_PLAY_ENABLED) ? [ + ...(getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED) ? [ 'remotePlayKeepAlive', 'remotePlayDirectConnectUrl', 'remotePlayDisableAchievementToast', @@ -1188,7 +1188,7 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([ ] : []), ]); -const hideSections = getPref(PrefKey.UI_HIDE_SECTIONS); +const hideSections = getGlobalPref(GlobalPref.UI_HIDE_SECTIONS); let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ hideSections.includes(UiSection.NEWS) && 'ignoreNewsSection', hideSections.includes(UiSection.FRIENDS) && 'ignorePlayWithFriendsSection', @@ -1196,7 +1196,7 @@ let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ STATES.browser.capabilities.touch && hideSections.includes(UiSection.TOUCH) && 'ignorePlayWithTouchSection', hideSections.some(value => [UiSection.NATIVE_MKB, UiSection.MOST_POPULAR].includes(value)) && 'ignoreSiglSections', - ...(getPref(PrefKey.UI_IMAGE_QUALITY) < 90 ? [ + ...(getGlobalPref(GlobalPref.UI_IMAGE_QUALITY) < 90 ? [ 'setBackgroundImageQuality', ] : []), @@ -1206,8 +1206,6 @@ let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ ]); // Only when playing -// TODO: check this -// @ts-ignore let STREAM_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ 'exposeInputChannel', @@ -1221,34 +1219,34 @@ let STREAM_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ // 'exposeEventTarget', // Patch volume control for normal stream - getPref(PrefKey.AUDIO_VOLUME_CONTROL_ENABLED) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchAudioMediaStream', + getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED) && !getGlobalPref(GlobalPref.STREAM_COMBINE_SOURCES) && 'patchAudioMediaStream', // Patch volume control for combined audio+video stream - getPref(PrefKey.AUDIO_VOLUME_CONTROL_ENABLED) && getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'patchCombinedAudioVideoMediaStream', + getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED) && getGlobalPref(GlobalPref.STREAM_COMBINE_SOURCES) && 'patchCombinedAudioVideoMediaStream', // Skip feedback dialog - getPref(PrefKey.UI_DISABLE_FEEDBACK_DIALOG) && 'skipFeedbackDialog', + getGlobalPref(GlobalPref.UI_DISABLE_FEEDBACK_DIALOG) && 'skipFeedbackDialog', ...(STATES.userAgent.capabilities.touch ? [ - getPref(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'patchShowSensorControls', - getPref(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'exposeTouchLayoutManager', - (getPref(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF || getPref(PrefKey.TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer', - getPref(PrefKey.TOUCH_CONTROLLER_DEFAULT_OPACITY) !== 100 && 'patchTouchControlDefaultOpacity', - (getPref(PrefKey.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.OFF && (getPref(PrefKey.MKB_ENABLED) || getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON)) && 'patchBabylonRendererClass', + getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'patchShowSensorControls', + getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'exposeTouchLayoutManager', + (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF || getGlobalPref(GlobalPref.TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer', + getGlobalPref(GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY) !== 100 && 'patchTouchControlDefaultOpacity', + (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.OFF && (getGlobalPref(GlobalPref.MKB_ENABLED) || getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.ON)) && 'patchBabylonRendererClass', ] : []), BX_FLAGS.EnableXcloudLogging && 'enableConsoleLogging', 'patchPollGamepads', - getPref(PrefKey.STREAM_COMBINE_SOURCES) && 'streamCombineSources', + getGlobalPref(GlobalPref.STREAM_COMBINE_SOURCES) && 'streamCombineSources', - ...(getPref(PrefKey.REMOTE_PLAY_ENABLED) ? [ + ...(getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED) ? [ 'patchRemotePlayMkb', 'remotePlayConnectMode', ] : []), // Native MKB - ...(AppInterface && getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON ? [ + ...(AppInterface && getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.ON ? [ 'patchMouseAndKeyboardEnabled', 'disableNativeRequestPointerLock', ] : []), diff --git a/src/modules/patcher/patches/src/local-co-op-enable.ts b/src/modules/patcher/patches/src/local-co-op-enable.ts index 106c072..dc6000d 100644 --- a/src/modules/patcher/patches/src/local-co-op-enable.ts +++ b/src/modules/patcher/patches/src/local-co-op-enable.ts @@ -53,6 +53,9 @@ $this$.toggleLocalCoOp = (enable: boolean) => { continue; } + // Don't show toast + (gamepad as any)._noToast = true; + window.dispatchEvent(new GamepadEvent('gamepaddisconnected', { gamepad })); window.dispatchEvent(new GamepadEvent('gamepadconnected', { gamepad })); } diff --git a/src/modules/player/webgl2-player.ts b/src/modules/player/webgl2-player.ts index a872f05..4029345 100755 --- a/src/modules/player/webgl2-player.ts +++ b/src/modules/player/webgl2-player.ts @@ -1,8 +1,8 @@ import vertClarityBoost from "./shaders/clarity_boost.vert" with { type: "text" }; import fsClarityBoost from "./shaders/clarity_boost.fs" with { type: "text" }; import { BxLogger } from "@/utils/bx-logger"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { StreamPref } from "@/enums/pref-keys"; +import { getStreamPref } from "@/utils/pref-utils"; export class WebGL2Player { @@ -143,13 +143,13 @@ export class WebGL2Player { } private setupShaders() { - BxLogger.info(this.LOG_TAG, 'Setting up', getPref(PrefKey.VIDEO_POWER_PREFERENCE)); + BxLogger.info(this.LOG_TAG, 'Setting up', getStreamPref(StreamPref.VIDEO_POWER_PREFERENCE)); const gl = this.$canvas.getContext('webgl2', { isBx: true, antialias: true, alpha: false, - powerPreference: getPref(PrefKey.VIDEO_POWER_PREFERENCE), + powerPreference: getStreamPref(StreamPref.VIDEO_POWER_PREFERENCE), }) as WebGL2RenderingContext; this.gl = gl; diff --git a/src/modules/remote-play-manager.ts b/src/modules/remote-play-manager.ts index 40d1c28..662551d 100755 --- a/src/modules/remote-play-manager.ts +++ b/src/modules/remote-play-manager.ts @@ -5,8 +5,8 @@ import { t } from "@utils/translation"; import { localRedirect } from "@modules/ui/ui"; import { BxLogger } from "@utils/bx-logger"; import { HeaderSection } from "./ui/header"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref, setPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref } from "@/enums/pref-keys"; +import { getGlobalPref, setGlobalPref } from "@/utils/pref-utils"; import { RemotePlayDialog } from "./ui/dialog/remote-play-dialog"; export const enum RemotePlayConsoleState { @@ -37,7 +37,7 @@ export class RemotePlayManager { private static instance: RemotePlayManager | null | undefined; public static getInstance(): typeof RemotePlayManager['instance'] { if (typeof RemotePlayManager.instance === 'undefined') { - if (getPref(PrefKey.REMOTE_PLAY_ENABLED)) { + if (getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) { RemotePlayManager.instance = new RemotePlayManager(); } else { RemotePlayManager.instance = null; @@ -186,7 +186,7 @@ export class RemotePlayManager { play(serverId: string, resolution?: string) { if (resolution) { - setPref(PrefKey.REMOTE_PLAY_STREAM_RESOLUTION, resolution); + setGlobalPref(GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION, resolution, 'ui'); } STATES.remotePlay.config = { @@ -221,7 +221,7 @@ export class RemotePlayManager { } static detect() { - if (!getPref(PrefKey.REMOTE_PLAY_ENABLED)) { + if (!getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) { return; } diff --git a/src/modules/settings-manager.ts b/src/modules/settings-manager.ts new file mode 100644 index 0000000..db4504d --- /dev/null +++ b/src/modules/settings-manager.ts @@ -0,0 +1,345 @@ +import { GlobalPref, StreamPref, type AnyPref } from "@/enums/pref-keys"; +import { limitVideoPlayerFps, onChangeVideoPlayerType, updateVideoPlayer } from "./stream/stream-settings-utils"; +import { StreamStats } from "./stream/stream-stats"; +import { SoundShortcut } from "./shortcuts/sound-shortcut"; +import { STATES } from "@/utils/global"; +import { getGamePref, getStreamPref, hasGamePref, isStreamPref, setGameIdPref } from "@/utils/pref-utils"; +import { BxExposed } from "@/utils/bx-exposed"; +import { StreamSettings } from "@/utils/stream-settings"; +import { NativeMkbHandler } from "./mkb/native-mkb-handler"; +import { BxEventBus } from "@/utils/bx-event-bus"; +import { SettingElement } from "@/utils/setting-element"; +import { CE } from "@/utils/html"; +import { t } from "@/utils/translation"; +import { BxSelectElement } from "@/web-components/bx-select"; +import { XboxApi } from "@/utils/xbox-api"; +import { EmulatedMkbHandler } from "./mkb/mkb-handler"; + +type SettingType = Partial<{ + hidden: true; + onChange: () => void; + alwaysTriggerOnChange: boolean; // Always trigger onChange(), not just when playing + $element: HTMLElement; +}>; + +export class SettingsManager { + private static instance: SettingsManager; + public static getInstance = () => SettingsManager.instance ?? (SettingsManager.instance = new SettingsManager()); + + private $streamSettingsSelection!: HTMLElement; + private $tips!: HTMLElement; + private playingGameId: number = -1; + private targetGameId: number = -1; + + // @ts-ignore + private SETTINGS: Record = { + // [GlobalPref.VERSION_LATEST]: { hidden: true }, + // [GlobalPref.VERSION_LAST_CHECK]: { hidden: true }, + // [GlobalPref.VERSION_CURRENT]: { hidden: true }, + + [StreamPref.LOCAL_CO_OP_ENABLED]: { + onChange: () => { + BxExposed.toggleLocalCoOp(getStreamPref(StreamPref.LOCAL_CO_OP_ENABLED)); + }, + }, + [StreamPref.DEVICE_VIBRATION_MODE]: { + onChange: StreamSettings.refreshControllerSettings, + }, + [StreamPref.DEVICE_VIBRATION_INTENSITY]: { + onChange: StreamSettings.refreshControllerSettings, + }, + [StreamPref.CONTROLLER_POLLING_RATE]: { + onChange: StreamSettings.refreshControllerSettings, + }, + [StreamPref.CONTROLLER_SETTINGS]: { + onChange: StreamSettings.refreshControllerSettings, + }, + [StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: { + onChange: () => { + const value = getStreamPref(StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY); + NativeMkbHandler.getInstance()?.setHorizontalScrollMultiplier(value / 100); + }, + }, + [StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY]: { + onChange: () => { + const value = getStreamPref(StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY); + NativeMkbHandler.getInstance()?.setVerticalScrollMultiplier(value / 100); + }, + }, + [StreamPref.VIDEO_PLAYER_TYPE]: { + onChange: () => { + onChangeVideoPlayerType(); + + if (STATES.isPlaying) { + updateVideoPlayer(); + } + }, + alwaysTriggerOnChange: true, + }, + [StreamPref.VIDEO_POWER_PREFERENCE]: { + onChange: () => { + const streamPlayer = STATES.currentStream.streamPlayer; + if (!streamPlayer) { + return; + } + + streamPlayer.reloadPlayer(); + updateVideoPlayer(); + }, + }, + [StreamPref.VIDEO_PROCESSING]: { + onChange: updateVideoPlayer, + }, + [StreamPref.VIDEO_SHARPNESS]: { + onChange: updateVideoPlayer, + }, + [StreamPref.VIDEO_MAX_FPS]: { + onChange: () => { + const value = getStreamPref(StreamPref.VIDEO_MAX_FPS); + limitVideoPlayerFps(value); + }, + }, + [StreamPref.VIDEO_RATIO]: { + onChange: updateVideoPlayer, + }, + [StreamPref.VIDEO_BRIGHTNESS]: { + onChange: updateVideoPlayer, + }, + [StreamPref.VIDEO_CONTRAST]: { + onChange: updateVideoPlayer, + }, + [StreamPref.VIDEO_SATURATION]: { + onChange: updateVideoPlayer, + }, + [StreamPref.VIDEO_POSITION]: { + onChange: updateVideoPlayer, + }, + [StreamPref.AUDIO_VOLUME]: { + onChange: () => { + const value = getStreamPref(StreamPref.AUDIO_VOLUME); + SoundShortcut.setGainNodeVolume(value); + }, + }, + + [StreamPref.STATS_ITEMS]: { + onChange: StreamStats.refreshStyles, + }, + [StreamPref.STATS_QUICK_GLANCE_ENABLED]: { + onChange: () => { + const value = getStreamPref(StreamPref.STATS_QUICK_GLANCE_ENABLED); + const streamStats = StreamStats.getInstance(); + value ? streamStats.quickGlanceSetup() : streamStats.quickGlanceStop(); + }, + }, + [StreamPref.STATS_POSITION]: { + onChange: StreamStats.refreshStyles, + }, + [StreamPref.STATS_TEXT_SIZE]: { + onChange: StreamStats.refreshStyles, + }, + [StreamPref.STATS_OPACITY_ALL]: { + onChange: StreamStats.refreshStyles, + }, + [StreamPref.STATS_OPACITY_BACKGROUND]: { + onChange: StreamStats.refreshStyles, + }, + [StreamPref.STATS_CONDITIONAL_FORMATTING]: { + onChange: StreamStats.refreshStyles, + }, + + [StreamPref.MKB_P1_MAPPING_PRESET_ID]: { + onChange: StreamSettings.refreshMkbSettings, + }, + + [StreamPref.MKB_P1_SLOT]: { + onChange: () => { + EmulatedMkbHandler.getInstance()?.resetXcloudGamepads(); + }, + }, + + [StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID]: { + onChange: StreamSettings.refreshKeyboardShortcuts, + }, + }; + + constructor() { + // Trigger onChange event when a setting value is modified + BxEventBus.Stream.on('setting.changed', data => { + if (isStreamPref(data.settingKey)) { + this.updateStreamElement(data.settingKey); + } + }); + + BxEventBus.Stream.on('gameSettings.switched', ({ id }) => { + this.switchGameSettings(id); + }); + + this.renderStreamSettingsSelection(); + } + + private updateStreamElement(key: StreamPref, onChanges?: Set) { + const info = this.SETTINGS[key]; + + // Add event + if (info.onChange && (STATES.isPlaying || info.alwaysTriggerOnChange)) { + if (onChanges) { + // Save to a Set() + onChanges.add(info.onChange); + } else { + // Trigger onChange() + info.onChange(); + } + } + + // Update element + const $elm = info.$element; + if (!$elm) { + return; + } + + const value = getGamePref(this.targetGameId, key, true)!; + + if ('setValue' in $elm) { + ($elm as any).setValue(value); + } else { + ($elm as HTMLInputElement).value = value.toString(); + } + + this.updateDataset($elm, key as StreamPref); + } + + private switchGameSettings(id: number) { + setGameIdPref(id); + + // Don't re-apply settings if the game is the same + if (this.targetGameId === id) { + return; + } + + // Re-apply all stream settings + const onChanges: Set = new Set(); + const oldGameId = this.targetGameId; + this.targetGameId = id; + + let key: AnyPref; + for (key in this.SETTINGS) { + if (!isStreamPref(key)) { + continue; + } + + const oldValue = getGamePref(oldGameId, key, true, true); + const newValue = getGamePref(this.targetGameId, key, true, true); + + if (oldValue === newValue) { + continue; + } + + // Only apply Stream settings + this.updateStreamElement(key, onChanges); + } + + // BxLogger.warning('Settings Manager', onChanges); + onChanges.forEach(onChange => { + onChange && onChange(); + }); + + // Toggle tips if not playing anything + this.$tips.classList.toggle('bx-gone', id < 0); + } + + setElement(pref: AnyPref, $elm: HTMLElement) { + // Set empty object + if (!this.SETTINGS[pref]) { + this.SETTINGS[pref] = {}; + } + + this.updateDataset($elm, pref as StreamPref); + this.SETTINGS[pref].$element = $elm; + } + + getElement(pref: AnyPref, params?: any) { + // Set empty object + if (!this.SETTINGS[pref]) { + this.SETTINGS[pref] = {}; + } + + let $elm = this.SETTINGS[pref].$element; + + if (!$elm) { + // Render element + $elm = SettingElement.fromPref(pref, null, params)!; + this.SETTINGS[pref].$element = $elm; + } + + this.updateDataset($elm, pref as StreamPref); + return $elm; + } + + hasElement(pref: AnyPref) { + return !!this.SETTINGS[pref]?.$element; + } + + private updateDataset($elm: HTMLElement, pref: StreamPref) { + if (this.targetGameId === this.playingGameId && hasGamePref(this.playingGameId, pref)) { + $elm.dataset.override = 'true'; + } else { + delete $elm.dataset['override']; + } + } + + private renderStreamSettingsSelection() { + this.$tips = CE('p', { class: 'bx-gone' }, `⇐ Q ⟶: ${t('reset-highlighted-setting')}`); + + const $select = BxSelectElement.create(CE('select', false, + CE('optgroup', { label: t('settings-for') }, + CE('option', { value: -1 }, t('all-games')), + ), + ), true); + $select.addEventListener('input', e => { + const id = parseInt($select.value); + // $btn.disabled = id < 0; + BxEventBus.Stream.emit('gameSettings.switched', { id }); + }); + + this.$streamSettingsSelection = CE('div', { + class: 'bx-stream-settings-selection bx-gone', + _nearby: { orientation: 'vertical' }, + }, + CE('div', false, $select ), + this.$tips, + ); + + BxEventBus.Stream.on('xboxTitleId.changed', async ({ id }) => { + this.playingGameId = id; + setGameIdPref(id); + const $optGroup = $select.querySelector('optgroup')!; + + // Remove every options except the first one (All games) + while ($optGroup.childElementCount > 1) { + $optGroup.lastElementChild?.remove(); + } + + // Add current game to the selection + if (id >= 0) { + const title = id === 0 ? 'Xbox' : await XboxApi.getProductTitle(id); + $optGroup.appendChild(CE('option', { + value: id, + }, title)); + + $select.value = id.toString(); + } else { + $select.value = '-1'; + } + + BxEventBus.Stream.emit('gameSettings.switched', { id }); + }); + } + + getStreamSettingsSelection() { + return this.$streamSettingsSelection; + } + + getTargetGameId() { + return this.targetGameId; + } +} diff --git a/src/modules/shortcuts/renderer-shortcut.ts b/src/modules/shortcuts/renderer-shortcut.ts index dc80fa6..dde76c6 100755 --- a/src/modules/shortcuts/renderer-shortcut.ts +++ b/src/modules/shortcuts/renderer-shortcut.ts @@ -1,7 +1,7 @@ -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { StreamPref } from "@/enums/pref-keys"; import { limitVideoPlayerFps } from "../stream/stream-settings-utils"; import { BxEventBus } from "@/utils/bx-event-bus"; +import { getStreamPref } from "@/utils/pref-utils"; export class RendererShortcut { static toggleVisibility() { @@ -15,7 +15,7 @@ export class RendererShortcut { const isVisible = !$mediaContainer.classList.contains('bx-gone'); // Switch FPS - limitVideoPlayerFps(isVisible ? getPref(PrefKey.VIDEO_MAX_FPS) : 0); + limitVideoPlayerFps(isVisible ? getStreamPref(StreamPref.VIDEO_MAX_FPS) : 0); BxEventBus.Stream.emit('video.visibility.changed', { isVisible }); } } diff --git a/src/modules/shortcuts/shortcut-actions.ts b/src/modules/shortcuts/shortcut-actions.ts index 4541804..fd3c1c4 100755 --- a/src/modules/shortcuts/shortcut-actions.ts +++ b/src/modules/shortcuts/shortcut-actions.ts @@ -1,7 +1,7 @@ -import { PrefKey } from "@/enums/pref-keys"; +import { GlobalPref } from "@/enums/pref-keys"; import { ShortcutAction } from "@/enums/shortcut-actions"; import { AppInterface, STATES } from "@/utils/global"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { getGlobalPref } from "@/utils/pref-utils"; import { t } from "@/utils/translation"; type ShortcutActions = { @@ -46,7 +46,7 @@ export const SHORTCUT_ACTIONS: ShortcutActions = { [ShortcutAction.STREAM_SOUND_TOGGLE]: [t('sound'), t('toggle')], - ...(getPref(PrefKey.AUDIO_VOLUME_CONTROL_ENABLED) ? { + ...(getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED) ? { [ShortcutAction.STREAM_VOLUME_INC]: [t('volume'), t('increase')], [ShortcutAction.STREAM_VOLUME_DEC]: [t('volume'), t('decrease')], } : {}), diff --git a/src/modules/shortcuts/sound-shortcut.ts b/src/modules/shortcuts/sound-shortcut.ts index 6010d7e..7a147a7 100755 --- a/src/modules/shortcuts/sound-shortcut.ts +++ b/src/modules/shortcuts/sound-shortcut.ts @@ -2,9 +2,10 @@ import { t } from "@utils/translation"; import { STATES } from "@utils/global"; import { Toast } from "@utils/toast"; import { ceilToNearest, floorToNearest } from "@/utils/utils"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref, setPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref, StreamPref } from "@/enums/pref-keys"; +import { getGlobalPref } from "@/utils/pref-utils"; import { BxEventBus } from "@/utils/bx-event-bus"; +import { getStreamPref, setStreamPref } from "@/utils/pref-utils"; export enum SpeakerState { ENABLED, @@ -13,11 +14,11 @@ export enum SpeakerState { export class SoundShortcut { static adjustGainNodeVolume(amount: number): number { - if (!getPref(PrefKey.AUDIO_VOLUME_CONTROL_ENABLED)) { + if (!getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED)) { return 0; } - const currentValue = getPref(PrefKey.AUDIO_VOLUME); + const currentValue = getStreamPref(StreamPref.AUDIO_VOLUME); let nearestValue: number; if (amount > 0) { // Increase @@ -33,7 +34,7 @@ export class SoundShortcut { newValue = currentValue + amount; } - newValue = setPref(PrefKey.AUDIO_VOLUME, newValue, true); + newValue = setStreamPref(StreamPref.AUDIO_VOLUME, newValue, 'direct'); SoundShortcut.setGainNodeVolume(newValue); // Show toast @@ -47,14 +48,14 @@ export class SoundShortcut { } static muteUnmute() { - if (getPref(PrefKey.AUDIO_VOLUME_CONTROL_ENABLED) && STATES.currentStream.audioGainNode) { + if (getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED) && STATES.currentStream.audioGainNode) { const gainValue = STATES.currentStream.audioGainNode.gain.value; - const settingValue = getPref(PrefKey.AUDIO_VOLUME); + const settingValue = getStreamPref(StreamPref.AUDIO_VOLUME); let targetValue: number; if (settingValue === 0) { // settingValue is 0 => set to 100 targetValue = 100; - setPref(PrefKey.AUDIO_VOLUME, targetValue, true); + setStreamPref(StreamPref.AUDIO_VOLUME, targetValue, 'direct'); } else if (gainValue === 0) { // is being muted => set to settingValue targetValue = settingValue; } else { // not being muted => mute diff --git a/src/modules/stream-player.ts b/src/modules/stream-player.ts index 5bb50ba..3b6a842 100755 --- a/src/modules/stream-player.ts +++ b/src/modules/stream-player.ts @@ -4,18 +4,12 @@ import { CE } from "@/utils/html"; import { WebGL2Player } from "./player/webgl2-player"; import { ScreenshotManager } from "@/utils/screenshot-manager"; import { STATES } from "@/utils/global"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref, StreamPref } from "@/enums/pref-keys"; +import { getGlobalPref } from "@/utils/pref-utils"; import { BX_FLAGS } from "@/utils/bx-flags"; import { StreamPlayerType, StreamVideoProcessing, VideoPosition } from "@/enums/pref-values"; +import { getStreamPref } from "@/utils/pref-utils"; -export type StreamPlayerOptions = Partial<{ - processing: string, - sharpness: number, - saturation: number, - contrast: number, - brightness: number, -}>; export class StreamPlayer { private $video: HTMLVideoElement; @@ -98,7 +92,7 @@ export class StreamPlayer { } private resizePlayer() { - const PREF_RATIO = getPref(PrefKey.VIDEO_RATIO); + const PREF_RATIO = getStreamPref(StreamPref.VIDEO_RATIO); const $video = this.$video; const isNativeTouchGame = STATES.currentStream.titleInfo?.details.hasNativeTouchSupport; @@ -142,7 +136,7 @@ export class StreamPlayer { // Set position const $parent = $video.parentElement!; - const position = getPref(PrefKey.VIDEO_POSITION); + const position = getStreamPref(StreamPref.VIDEO_POSITION); $parent.style.removeProperty('padding-top'); $parent.dataset.position = position; @@ -269,7 +263,7 @@ export class StreamPlayer { } // Apply video filters to screenshots - if (isFullVersion() && getPref(PrefKey.SCREENSHOT_APPLY_FILTERS)) { + if (isFullVersion() && getGlobalPref(GlobalPref.SCREENSHOT_APPLY_FILTERS)) { ScreenshotManager.getInstance().updateCanvasFilters(filters); } diff --git a/src/modules/stream/stream-settings-utils.ts b/src/modules/stream/stream-settings-utils.ts index 51fa98a..7353b1a 100755 --- a/src/modules/stream/stream-settings-utils.ts +++ b/src/modules/stream/stream-settings-utils.ts @@ -1,24 +1,24 @@ import { STATES } from "@utils/global"; import { UserAgent } from "@utils/user-agent"; -import type { StreamPlayerOptions } from "../stream-player"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref, setPref } from "@/utils/settings-storages/global-settings-storage"; +import { StreamPref } from "@/enums/pref-keys"; import { StreamVideoProcessing, StreamPlayerType } from "@/enums/pref-values"; -import { escapeCssSelector } from "@/utils/html"; +import { getStreamPref, setStreamPref } from "@/utils/pref-utils"; +import { SettingsManager } from "../settings-manager"; export function onChangeVideoPlayerType() { - const playerType = getPref(PrefKey.VIDEO_PLAYER_TYPE); - const $videoProcessing = document.getElementById(`bx_setting_${escapeCssSelector(PrefKey.VIDEO_PROCESSING)}`) as HTMLSelectElement; - const $videoSharpness = document.getElementById(`bx_setting_${escapeCssSelector(PrefKey.VIDEO_SHARPNESS)}`) as HTMLElement; - const $videoPowerPreference = document.getElementById(`bx_setting_${escapeCssSelector(PrefKey.VIDEO_POWER_PREFERENCE)}`) as HTMLElement; - const $videoMaxFps = document.getElementById(`bx_setting_${escapeCssSelector(PrefKey.VIDEO_MAX_FPS)}`) as HTMLElement; - - if (!$videoProcessing) { + const playerType = getStreamPref(StreamPref.VIDEO_PLAYER_TYPE); + const settingsManager = SettingsManager.getInstance(); + if (!settingsManager.hasElement(StreamPref.VIDEO_PROCESSING)) { return; } let isDisabled = false; + const $videoProcessing = settingsManager.getElement(StreamPref.VIDEO_PROCESSING) as HTMLSelectElement; + const $videoSharpness = settingsManager.getElement(StreamPref.VIDEO_SHARPNESS); + const $videoPowerPreference = settingsManager.getElement(StreamPref.VIDEO_POWER_PREFERENCE); + const $videoMaxFps = settingsManager.getElement(StreamPref.VIDEO_MAX_FPS); + const $optCas = $videoProcessing.querySelector(`option[value=${StreamVideoProcessing.CAS}]`); if (playerType === StreamPlayerType.WEBGL2) { @@ -26,7 +26,7 @@ export function onChangeVideoPlayerType() { } else { // Only allow USM when player type is Video $videoProcessing.value = StreamVideoProcessing.USM; - setPref(PrefKey.VIDEO_PROCESSING, StreamVideoProcessing.USM); + setStreamPref(StreamPref.VIDEO_PROCESSING, StreamVideoProcessing.USM, 'direct'); $optCas && ($optCas.disabled = true); @@ -41,8 +41,6 @@ export function onChangeVideoPlayerType() { // Hide Power Preference setting if renderer isn't WebGL2 $videoPowerPreference.closest('.bx-settings-row')!.classList.toggle('bx-gone', playerType !== StreamPlayerType.WEBGL2); $videoMaxFps.closest('.bx-settings-row')!.classList.toggle('bx-gone', playerType !== StreamPlayerType.WEBGL2); - - updateVideoPlayer(); } @@ -58,17 +56,17 @@ export function updateVideoPlayer() { return; } - limitVideoPlayerFps(getPref(PrefKey.VIDEO_MAX_FPS)); + limitVideoPlayerFps(getStreamPref(StreamPref.VIDEO_MAX_FPS)); const options = { - processing: getPref(PrefKey.VIDEO_PROCESSING), - sharpness: getPref(PrefKey.VIDEO_SHARPNESS), - saturation: getPref(PrefKey.VIDEO_SATURATION), - contrast: getPref(PrefKey.VIDEO_CONTRAST), - brightness: getPref(PrefKey.VIDEO_BRIGHTNESS), + processing: getStreamPref(StreamPref.VIDEO_PROCESSING), + sharpness: getStreamPref(StreamPref.VIDEO_SHARPNESS), + saturation: getStreamPref(StreamPref.VIDEO_SATURATION), + contrast: getStreamPref(StreamPref.VIDEO_CONTRAST), + brightness: getStreamPref(StreamPref.VIDEO_BRIGHTNESS), } satisfies StreamPlayerOptions; - streamPlayer.setPlayerType(getPref(PrefKey.VIDEO_PLAYER_TYPE)); + streamPlayer.setPlayerType(getStreamPref(StreamPref.VIDEO_PLAYER_TYPE)); streamPlayer.updateOptions(options); streamPlayer.refreshPlayer(); } diff --git a/src/modules/stream/stream-stats.ts b/src/modules/stream/stream-stats.ts index d77fb44..a66b7bf 100755 --- a/src/modules/stream/stream-stats.ts +++ b/src/modules/stream/stream-stats.ts @@ -1,12 +1,12 @@ import { CE } from "@utils/html" import { t } from "@utils/translation" import { STATES } from "@utils/global" -import { PrefKey } from "@/enums/pref-keys" -import { getPref } from "@/utils/settings-storages/global-settings-storage" -import { StreamStatsCollector, type StreamStatGrade } from "@/utils/stream-stats-collector" +import { StreamPref } from "@/enums/pref-keys" +import { StreamStatsCollector } from "@/utils/stream-stats-collector" import { BxLogger } from "@/utils/bx-logger" import { StreamStat } from "@/enums/pref-values" import { BxEventBus } from "@/utils/bx-event-bus" +import { getStreamPref } from "@/utils/pref-utils"; export class StreamStats { @@ -164,7 +164,7 @@ export class StreamStats { return; } - const PREF_STATS_CONDITIONAL_FORMATTING = getPref(PrefKey.STATS_CONDITIONAL_FORMATTING); + const PREF_STATS_CONDITIONAL_FORMATTING = getStreamPref(StreamPref.STATS_CONDITIONAL_FORMATTING); let grade: StreamStatGrade = ''; // Collect stats @@ -192,12 +192,12 @@ export class StreamStats { } refreshStyles() { - const PREF_ITEMS = getPref(PrefKey.STATS_ITEMS); - const PREF_OPACITY_BG = getPref(PrefKey.STATS_OPACITY_BACKGROUND); + const PREF_ITEMS = getStreamPref(StreamPref.STATS_ITEMS); + const PREF_OPACITY_BG = getStreamPref(StreamPref.STATS_OPACITY_BACKGROUND); const $container = this.$container; $container.dataset.stats = '[' + PREF_ITEMS.join('][') + ']'; - $container.dataset.position = getPref(PrefKey.STATS_POSITION); + $container.dataset.position = getStreamPref(StreamPref.STATS_POSITION); if (PREF_OPACITY_BG === 0) { $container.style.removeProperty('background-color'); @@ -207,12 +207,12 @@ export class StreamStats { $container.style.backgroundColor = `rgba(0, 0, 0, ${PREF_OPACITY_BG}%)`; } - $container.style.opacity = getPref(PrefKey.STATS_OPACITY_ALL) + '%'; - $container.style.fontSize = getPref(PrefKey.STATS_TEXT_SIZE); + $container.style.opacity = getStreamPref(StreamPref.STATS_OPACITY_ALL) + '%'; + $container.style.fontSize = getStreamPref(StreamPref.STATS_TEXT_SIZE); } hideSettingsUi() { - if (this.isGlancing() && !getPref(PrefKey.STATS_QUICK_GLANCE_ENABLED)) { + if (this.isGlancing() && !getStreamPref(StreamPref.STATS_QUICK_GLANCE_ENABLED)) { this.stop(); } } @@ -240,8 +240,8 @@ export class StreamStats { static setupEvents() { BxEventBus.Stream.on('state.playing', () => { - const PREF_STATS_QUICK_GLANCE = getPref(PrefKey.STATS_QUICK_GLANCE_ENABLED); - const PREF_STATS_SHOW_WHEN_PLAYING = getPref(PrefKey.STATS_SHOW_WHEN_PLAYING); + const PREF_STATS_QUICK_GLANCE = getStreamPref(StreamPref.STATS_QUICK_GLANCE_ENABLED); + const PREF_STATS_SHOW_WHEN_PLAYING = getStreamPref(StreamPref.STATS_SHOW_WHEN_PLAYING); const streamStats = StreamStats.getInstance(); diff --git a/src/modules/touch-controller.ts b/src/modules/touch-controller.ts index 43a6519..c1c83af 100755 --- a/src/modules/touch-controller.ts +++ b/src/modules/touch-controller.ts @@ -4,8 +4,8 @@ import { BxEvent } from "@utils/bx-event"; import { NATIVE_FETCH } from "@utils/bx-flags"; import { t } from "@utils/translation"; import { BxLogger } from "@utils/bx-logger"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref } from "@/enums/pref-keys"; +import { getGlobalPref } from "@/utils/pref-utils"; import { TouchControllerStyleCustom, TouchControllerStyleStandard } from "@/enums/pref-values"; import { GhPagesUtils } from "@/utils/gh-pages"; import { BxEventBus } from "@/utils/bx-event-bus"; @@ -289,8 +289,8 @@ export class TouchController { TouchController.#$style = $style; - const PREF_STYLE_STANDARD = getPref(PrefKey.TOUCH_CONTROLLER_STYLE_STANDARD); - const PREF_STYLE_CUSTOM = getPref(PrefKey.TOUCH_CONTROLLER_STYLE_CUSTOM); + const PREF_STYLE_STANDARD = getGlobalPref(GlobalPref.TOUCH_CONTROLLER_STYLE_STANDARD); + const PREF_STYLE_CUSTOM = getGlobalPref(GlobalPref.TOUCH_CONTROLLER_STYLE_CUSTOM); BxEventBus.Stream.on('dataChannelCreated', payload => { const { dataChannel } = payload; diff --git a/src/modules/ui/dialog/profile-manger/controller-customizations-manager-dialog.ts b/src/modules/ui/dialog/profile-manger/controller-customizations-manager-dialog.ts index 717d5bc..d6314d0 100644 --- a/src/modules/ui/dialog/profile-manger/controller-customizations-manager-dialog.ts +++ b/src/modules/ui/dialog/profile-manger/controller-customizations-manager-dialog.ts @@ -5,8 +5,8 @@ import { t } from "@/utils/translation"; import { GamepadKey, GamepadKeyName } from "@/enums/gamepad"; import { ButtonStyle, CE, createButton, createSettingRow } from "@/utils/html"; import { BxSelectElement } from "@/web-components/bx-select"; -import { PrefKey } from "@/enums/pref-keys"; -import { getPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref } from "@/enums/pref-keys"; +import { getGlobalPref } from "@/utils/pref-utils"; import { BxEvent } from "@/utils/bx-event"; import { deepClone } from "@/utils/global"; import { StreamSettings } from "@/utils/stream-settings"; @@ -58,7 +58,7 @@ export class ControllerCustomizationsManagerDialog extends BaseProfileManagerDia } private render() { - const isControllerFriendly = getPref(PrefKey.UI_CONTROLLER_FRIENDLY); + const isControllerFriendly = getGlobalPref(GlobalPref.UI_CONTROLLER_FRIENDLY); const $rows = CE('div', { class: 'bx-buttons-grid' }); const $baseSelect = CE('select', { class: 'bx-full-width' }, @@ -117,7 +117,7 @@ export class ControllerCustomizationsManagerDialog extends BaseProfileManagerDia } // Map nearby elenemts for controller-friendly UI - if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) { + if (getGlobalPref(GlobalPref.UI_CONTROLLER_FRIENDLY)) { for (let i = 0; i < this.selectsOrder.length; i++) { const $select = this.selectsMap[this.selectsOrder[i] as unknown as GamepadKey] as NavigationElement; const directions = { @@ -257,7 +257,7 @@ export class ControllerCustomizationsManagerDialog extends BaseProfileManagerDia $label.classList.add('bx-horizontal-shaking'); // Focus select - if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) { + if (getGlobalPref(GlobalPref.UI_CONTROLLER_FRIENDLY)) { this.dialogManager.focus($select); } } diff --git a/src/modules/ui/dialog/profile-manger/mkb-mapping-manager-dialog.ts b/src/modules/ui/dialog/profile-manger/mkb-mapping-manager-dialog.ts index be88e6f..975f5bc 100755 --- a/src/modules/ui/dialog/profile-manger/mkb-mapping-manager-dialog.ts +++ b/src/modules/ui/dialog/profile-manger/mkb-mapping-manager-dialog.ts @@ -4,7 +4,7 @@ import { t } from "@/utils/translation"; import { MkbMappingPresetsTable } from "@/utils/local-db/mkb-mapping-presets-table"; import { GamepadKey, GamepadKeyName } from "@/enums/gamepad"; import { CE, createSettingRow } from "@/utils/html"; -import { MouseMapTo, type KeyCode } from "@/enums/mkb"; +import { MouseMapTo } from "@/enums/mkb"; import { BxKeyBindingButton, BxKeyBindingButtonFlag } from "@/web-components/bx-key-binding-button"; import { StreamSettings } from "@/utils/stream-settings"; import { BxNumberStepper } from "@/web-components/bx-number-stepper"; diff --git a/src/modules/ui/dialog/remote-play-dialog.ts b/src/modules/ui/dialog/remote-play-dialog.ts index 664590e..28a9062 100755 --- a/src/modules/ui/dialog/remote-play-dialog.ts +++ b/src/modules/ui/dialog/remote-play-dialog.ts @@ -1,8 +1,8 @@ import { ButtonStyle, CE, createButton } from "@/utils/html"; import { NavigationDialog, type NavigationElement } from "./navigation-dialog"; -import { PrefKey } from "@/enums/pref-keys"; +import { GlobalPref } from "@/enums/pref-keys"; import { BxIcon } from "@/utils/bx-icon"; -import { getPref, setPref } from "@/utils/settings-storages/global-settings-storage"; +import { getGlobalPref, setGlobalPref } from "@/utils/pref-utils"; import { t } from "@/utils/translation"; import { RemotePlayConsoleState, RemotePlayManager } from "@/modules/remote-play-manager"; import { BxSelectElement } from "@/web-components/bx-select"; @@ -40,7 +40,7 @@ export class RemotePlayDialog extends NavigationDialog { const $settingNote = CE('p', {}); - const currentResolution = getPref(PrefKey.REMOTE_PLAY_STREAM_RESOLUTION); + const currentResolution = getGlobalPref(GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION); let $resolutions : HTMLSelectElement | NavigationElement = CE('select', false, CE('option', { value: StreamResolution.DIM_720P }, '720p'), CE('option', { value: StreamResolution.DIM_1080P }, '1080p'), @@ -52,7 +52,7 @@ export class RemotePlayDialog extends NavigationDialog { const value = (e.target as HTMLSelectElement).value; $settingNote.textContent = value === '1080p' ? '✅ ' + t('can-stream-xbox-360-games') : '❌ ' + t('cant-stream-xbox-360-games'); - setPref(PrefKey.REMOTE_PLAY_STREAM_RESOLUTION, value); + setGlobalPref(GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION, value, 'ui'); }); ($resolutions as any).value = currentResolution; diff --git a/src/modules/ui/dialog/settings-dialog.ts b/src/modules/ui/dialog/settings-dialog.ts index 20fe35b..84f067a 100755 --- a/src/modules/ui/dialog/settings-dialog.ts +++ b/src/modules/ui/dialog/settings-dialog.ts @@ -1,14 +1,12 @@ import { isFullVersion } from "@macros/build" with { type: "macro" }; -import { limitVideoPlayerFps, onChangeVideoPlayerType, updateVideoPlayer } from "@/modules/stream/stream-settings-utils"; +import { onChangeVideoPlayerType } from "@/modules/stream/stream-settings-utils"; import { ButtonStyle, calculateSelectBoxes, CE, createButton, createSettingRow, createSvgIcon, escapeCssSelector, type BxButtonOptions } from "@/utils/html"; import { NavigationDialog, NavigationDirection } from "./navigation-dialog"; -import { SoundShortcut } from "@/modules/shortcuts/sound-shortcut"; -import { StreamStats } from "@/modules/stream/stream-stats"; import { TouchController } from "@/modules/touch-controller"; import { BxEvent } from "@/utils/bx-event"; import { BxIcon, type BxIconRaw } from "@/utils/bx-icon"; -import { STATES, AppInterface, deepClone, SCRIPT_VERSION, STORAGE, SCRIPT_VARIANT } from "@/utils/global"; +import { STATES, AppInterface, deepClone, SCRIPT_VERSION, SCRIPT_VARIANT } from "@/utils/global"; import { t, Translations } from "@/utils/translation"; import { BxSelectElement } from "@/web-components/bx-select"; import { setNearby } from "@/utils/navigation-utils"; @@ -17,8 +15,7 @@ import { UserAgentProfile } from "@/enums/user-agent"; import { UserAgent } from "@/utils/user-agent"; import { BX_FLAGS } from "@/utils/bx-flags"; import { clearAllData, copyToClipboard } from "@/utils/utils"; -import { PrefKey, StorageKey } from "@/enums/pref-keys"; -import { getPref, getPrefDefinition, setPref } from "@/utils/settings-storages/global-settings-storage"; +import { GlobalPref, StorageKey, StreamPref, type AnyPref } from "@/enums/pref-keys"; import { SettingElement } from "@/utils/setting-element"; import type { SettingDefinition, SuggestedSettingProfile } from "@/types/setting-definition"; import { FullscreenText } from "../fullscreen-text"; @@ -27,14 +24,14 @@ import { GamepadKey } from "@/enums/gamepad"; import { NativeMkbHandler } from "@/modules/mkb/native-mkb-handler"; import { ControllerExtraSettings } from "./settings/controller-extra"; import { SuggestionsSetting } from "./settings/suggestions"; -import { StreamSettings } from "@/utils/stream-settings"; import { MkbExtraSettings } from "./settings/mkb-extra"; -import { BxExposed } from "@/utils/bx-exposed"; import { BxEventBus } from "@/utils/bx-event-bus"; +import { getGlobalPref, getPrefInfo, getStreamPref, isStreamPref, setGlobalPref, STORAGE } from "@/utils/pref-utils"; +import { SettingsManager } from "@/modules/settings-manager"; type SettingTabSectionItem = Partial<{ - pref: PrefKey; + pref: AnyPref; multiLines: boolean; label: string; note: string | (() => HTMLElement) | HTMLElement; @@ -43,7 +40,7 @@ type SettingTabSectionItem = Partial<{ options: { [key: string]: string }; unsupported: boolean; unsupportedNote: string; - onChange: (e: any, value: number) => void; + // onChange: (e: any, value: number) => void; onCreated: (setting: SettingTabSectionItem, $control: any) => void; params: any; requiredVariants?: BuildVariant | Array; @@ -59,17 +56,15 @@ type SettingTabSection = { unsupportedNote?: HTMLElement | string | Text | null; helpUrl?: string; content?: HTMLElement; - lazyContent?: boolean | (() => HTMLElement); - items?: Array void) | false>; + items?: Array void) | false>; requiredVariants?: BuildVariant | Array; }; type SettingTab = { icon: BxIconRaw; group: SettingTabGroup, - items: Array | (() => Array); + items: Array; requiredVariants?: BuildVariant | Array; - lazyContent?: boolean; }; type SettingTabGroup = 'global' | 'stream' | 'controller' | 'mkb' | 'stats'; @@ -87,17 +82,20 @@ export class SettingsDialog extends NavigationDialog { private $btnGlobalReload!: HTMLButtonElement; private $noteGlobalReload!: HTMLElement; private $btnSuggestion!: HTMLDivElement; + private $streamSettingsSelection!: HTMLElement; private renderFullSettings: boolean; + protected boundOnContextMenu: any; - protected suggestedSettings: Record> = { + protected suggestedSettings: Record> = { recommended: {}, default: {}, lowest: {}, highest: {}, }; - protected suggestedSettingLabels: PartialRecord = {}; - protected settingElements: PartialRecord = {}; + protected settingLabels: PartialRecord = {}; + + protected settingsManager: SettingsManager; private readonly TAB_GLOBAL_ITEMS: Array = [{ group: 'general', @@ -106,7 +104,7 @@ export class SettingsDialog extends NavigationDialog { items: [ // Top buttons ($parent) => { - const PREF_LATEST_VERSION = getPref(PrefKey.VERSION_LATEST); + const PREF_LATEST_VERSION = getGlobalPref(GlobalPref.VERSION_LATEST); const topButtons = []; // "New version available" button @@ -188,57 +186,57 @@ export class SettingsDialog extends NavigationDialog { }, { - pref: PrefKey.SCRIPT_LOCALE, + pref: GlobalPref.SCRIPT_LOCALE, multiLines: true, }, - PrefKey.SERVER_BYPASS_RESTRICTION, - PrefKey.UI_CONTROLLER_FRIENDLY, - PrefKey.REMOTE_PLAY_ENABLED, + GlobalPref.SERVER_BYPASS_RESTRICTION, + GlobalPref.UI_CONTROLLER_FRIENDLY, + GlobalPref.REMOTE_PLAY_ENABLED, ], }, { group: 'server', label: t('server'), items: [ { - pref: PrefKey.SERVER_REGION, + pref: GlobalPref.SERVER_REGION, multiLines: true, }, { - pref: PrefKey.STREAM_PREFERRED_LOCALE, + pref: GlobalPref.STREAM_PREFERRED_LOCALE, multiLines: true, }, - PrefKey.SERVER_PREFER_IPV6, + GlobalPref.SERVER_PREFER_IPV6, ], }, { group: 'stream', label: t('stream'), items: [ - PrefKey.STREAM_RESOLUTION, - PrefKey.STREAM_CODEC_PROFILE, - PrefKey.STREAM_MAX_VIDEO_BITRATE, + GlobalPref.STREAM_RESOLUTION, + GlobalPref.STREAM_CODEC_PROFILE, + GlobalPref.STREAM_MAX_VIDEO_BITRATE, - PrefKey.AUDIO_VOLUME_CONTROL_ENABLED, + GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED, - PrefKey.SCREENSHOT_APPLY_FILTERS, + GlobalPref.SCREENSHOT_APPLY_FILTERS, - PrefKey.AUDIO_MIC_ON_PLAYING, - PrefKey.GAME_FORTNITE_FORCE_CONSOLE, - PrefKey.STREAM_COMBINE_SOURCES, + GlobalPref.AUDIO_MIC_ON_PLAYING, + GlobalPref.GAME_FORTNITE_FORCE_CONSOLE, + GlobalPref.STREAM_COMBINE_SOURCES, ], }, { requiredVariants: 'full', group: 'mkb', label: t('mouse-and-keyboard'), items: [ - PrefKey.NATIVE_MKB_MODE, + GlobalPref.NATIVE_MKB_MODE, { - pref: PrefKey.NATIVE_MKB_FORCED_GAMES, + pref: GlobalPref.NATIVE_MKB_FORCED_GAMES, multiLines: true, note: CE('a', { href: 'https://github.com/redphx/better-xcloud/discussions/574', target: '_blank' }, t('unofficial-game-list')), }, - PrefKey.MKB_ENABLED, - PrefKey.MKB_HIDE_IDLE_CURSOR, + GlobalPref.MKB_ENABLED, + GlobalPref.MKB_HIDE_IDLE_CURSOR, ], // Unsupported @@ -255,13 +253,13 @@ export class SettingsDialog extends NavigationDialog { label: t('touch-controller'), items: [ { - pref: PrefKey.TOUCH_CONTROLLER_MODE, + pref: GlobalPref.TOUCH_CONTROLLER_MODE, note: CE('a', { href: 'https://github.com/redphx/better-xcloud/discussions/241', target: '_blank' }, t('unofficial-game-list')), }, - PrefKey.TOUCH_CONTROLLER_AUTO_OFF, - PrefKey.TOUCH_CONTROLLER_DEFAULT_OPACITY, - PrefKey.TOUCH_CONTROLLER_STYLE_STANDARD, - PrefKey.TOUCH_CONTROLLER_STYLE_CUSTOM, + GlobalPref.TOUCH_CONTROLLER_AUTO_OFF, + GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY, + GlobalPref.TOUCH_CONTROLLER_STYLE_STANDARD, + GlobalPref.TOUCH_CONTROLLER_STYLE_CUSTOM, ], // Unsupported @@ -273,22 +271,22 @@ export class SettingsDialog extends NavigationDialog { group: 'ui', label: t('ui'), items: [ - PrefKey.UI_LAYOUT, - PrefKey.UI_IMAGE_QUALITY, - PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME, - PrefKey.UI_CONTROLLER_SHOW_STATUS, - PrefKey.UI_SIMPLIFY_STREAM_MENU, - PrefKey.UI_SKIP_SPLASH_VIDEO, - !AppInterface && PrefKey.UI_SCROLLBAR_HIDE, - PrefKey.UI_HIDE_SYSTEM_MENU_ICON, - PrefKey.UI_DISABLE_FEEDBACK_DIALOG, - PrefKey.UI_REDUCE_ANIMATIONS, + GlobalPref.UI_LAYOUT, + GlobalPref.UI_IMAGE_QUALITY, + GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME, + GlobalPref.UI_CONTROLLER_SHOW_STATUS, + GlobalPref.UI_SIMPLIFY_STREAM_MENU, + GlobalPref.UI_SKIP_SPLASH_VIDEO, + !AppInterface && GlobalPref.UI_SCROLLBAR_HIDE, + GlobalPref.UI_HIDE_SYSTEM_MENU_ICON, + GlobalPref.UI_DISABLE_FEEDBACK_DIALOG, + GlobalPref.UI_REDUCE_ANIMATIONS, { - pref: PrefKey.UI_HIDE_SECTIONS, + pref: GlobalPref.UI_HIDE_SECTIONS, multiLines: true, }, { - pref: PrefKey.BLOCK_FEATURES, + pref: GlobalPref.BLOCK_FEATURES, multiLines: true, }, ], @@ -297,28 +295,28 @@ export class SettingsDialog extends NavigationDialog { group: 'game-bar', label: t('game-bar'), items: [ - PrefKey.GAME_BAR_POSITION, + GlobalPref.GAME_BAR_POSITION, ], }, { group: 'loading-screen', label: t('loading-screen'), items: [ - PrefKey.LOADING_SCREEN_GAME_ART, - PrefKey.LOADING_SCREEN_SHOW_WAIT_TIME, - PrefKey.LOADING_SCREEN_ROCKET, + GlobalPref.LOADING_SCREEN_GAME_ART, + GlobalPref.LOADING_SCREEN_SHOW_WAIT_TIME, + GlobalPref.LOADING_SCREEN_ROCKET, ], }, { group: 'other', label: t('other'), items: [ - PrefKey.BLOCK_TRACKING, + GlobalPref.BLOCK_TRACKING, ], }, isFullVersion() && { group: 'advanced', label: t('advanced'), items: [ { - pref: PrefKey.USER_AGENT_PROFILE, + pref: GlobalPref.USER_AGENT_PROFILE, multiLines: true, onCreated: (setting, $control) => { const defaultUserAgent = window.navigator.orgUserAgent || window.navigator.userAgent; @@ -429,20 +427,17 @@ export class SettingsDialog extends NavigationDialog { label: t('audio'), helpUrl: 'https://better-xcloud.github.io/ingame-features/#audio', items: [{ - pref: PrefKey.AUDIO_VOLUME, - onChange: (e: any, value: number) => { - SoundShortcut.setGainNodeVolume(value); - }, + pref: StreamPref.AUDIO_VOLUME, params: { - disabled: !getPref(PrefKey.AUDIO_VOLUME_CONTROL_ENABLED), + disabled: !getGlobalPref(GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED), }, onCreated: (setting: SettingTabSectionItem, $elm: HTMLElement) => { const $range = $elm.querySelector('input[type=range')!; - BxEventBus.Script.on('setting.changed', payload => { - const { storageKey, settingKey, settingValue } = payload; - if (storageKey === StorageKey.GLOBAL && settingKey === PrefKey.AUDIO_VOLUME) { - $range.value = settingValue; + BxEventBus.Stream.on('setting.changed', payload => { + const { settingKey } = payload; + if (settingKey === StreamPref.AUDIO_VOLUME) { + $range.value = getStreamPref(settingKey).toString(); BxEvent.dispatch($range, 'input', { ignoreOnChange: true }); } }); @@ -452,47 +447,18 @@ export class SettingsDialog extends NavigationDialog { group: 'video', label: t('video'), helpUrl: 'https://better-xcloud.github.io/ingame-features/#video', - items: [{ - pref: PrefKey.VIDEO_PLAYER_TYPE, - onChange: onChangeVideoPlayerType, - }, { - pref: PrefKey.VIDEO_MAX_FPS, - onChange: e => { - limitVideoPlayerFps(parseInt(e.target.value)); - }, - }, { - pref: PrefKey.VIDEO_POWER_PREFERENCE, - onChange: () => { - const streamPlayer = STATES.currentStream.streamPlayer; - if (!streamPlayer) { - return; - } - - streamPlayer.reloadPlayer(); - updateVideoPlayer(); - }, - }, { - pref: PrefKey.VIDEO_PROCESSING, - onChange: updateVideoPlayer, - }, { - pref: PrefKey.VIDEO_RATIO, - onChange: updateVideoPlayer, - }, { - pref: PrefKey.VIDEO_POSITION, - onChange: updateVideoPlayer, - }, { - pref: PrefKey.VIDEO_SHARPNESS, - onChange: updateVideoPlayer, - }, { - pref: PrefKey.VIDEO_SATURATION, - onChange: updateVideoPlayer, - }, { - pref: PrefKey.VIDEO_CONTRAST, - onChange: updateVideoPlayer, - }, { - pref: PrefKey.VIDEO_BRIGHTNESS, - onChange: updateVideoPlayer, - }], + items: [ + StreamPref.VIDEO_PLAYER_TYPE, + StreamPref.VIDEO_MAX_FPS, + StreamPref.VIDEO_POWER_PREFERENCE, + StreamPref.VIDEO_PROCESSING, + StreamPref.VIDEO_RATIO, + StreamPref.VIDEO_POSITION, + StreamPref.VIDEO_SHARPNESS, + StreamPref.VIDEO_SATURATION, + StreamPref.VIDEO_CONTRAST, + StreamPref.VIDEO_BRIGHTNESS, + ], }]; private readonly TAB_CONTROLLER_ITEMS: Array = isFullVersion() ? [{ @@ -500,15 +466,12 @@ export class SettingsDialog extends NavigationDialog { label: t('controller'), helpUrl: 'https://better-xcloud.github.io/ingame-features/#controller', items: [ - { - pref: PrefKey.LOCAL_CO_OP_ENABLED, - onChange: () => { BxExposed.toggleLocalCoOp(getPref(PrefKey.LOCAL_CO_OP_ENABLED)); }, - }, { - pref: PrefKey.CONTROLLER_POLLING_RATE, - onChange: () => StreamSettings.refreshControllerSettings(), - }, ($parent => { - $parent.appendChild(ControllerExtraSettings.renderSettings.apply(this)); - })], + StreamPref.LOCAL_CO_OP_ENABLED, + StreamPref.CONTROLLER_POLLING_RATE, + ($parent => { + $parent.appendChild(ControllerExtraSettings.renderSettings.apply(this)); + }), + ], }, STATES.userAgent.capabilities.touch && { @@ -569,18 +532,16 @@ export class SettingsDialog extends NavigationDialog { group: 'device', label: t('device'), items: [{ - pref: PrefKey.DEVICE_VIBRATION_MODE, + pref: StreamPref.DEVICE_VIBRATION_MODE, multiLines: true, unsupported: !STATES.browser.capabilities.deviceVibration, - onChange: () => StreamSettings.refreshControllerSettings(), }, { - pref: PrefKey.DEVICE_VIBRATION_INTENSITY, + pref: StreamPref.DEVICE_VIBRATION_INTENSITY, unsupported: !STATES.browser.capabilities.deviceVibration, - onChange: () => StreamSettings.refreshControllerSettings(), }], }] : []; - private readonly TAB_MKB_ITEMS: (() => Array) = isFullVersion() ? () => [ + private readonly TAB_MKB_ITEMS: Array = isFullVersion() ? [ { requiredVariants: 'full', group: 'mkb', @@ -597,50 +558,25 @@ export class SettingsDialog extends NavigationDialog { requiredVariants: 'full', group: 'native-mkb', label: t('native-mkb'), - items: isFullVersion() ? [{ - pref: PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY, - onChange: (e: any, value: number) => { - NativeMkbHandler.getInstance()?.setVerticalScrollMultiplier(value / 100); - }, - }, { - pref: PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY, - onChange: (e: any, value: number) => { - NativeMkbHandler.getInstance()?.setHorizontalScrollMultiplier(value / 100); - }, - }] : [], - }] : () => []; + items: [ + StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY, + StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY, + ], + }] : []; private readonly TAB_STATS_ITEMS: Array = [{ group: 'stats', label: t('stream-stats'), helpUrl: 'https://better-xcloud.github.io/stream-stats/', - items: [{ - pref: PrefKey.STATS_SHOW_WHEN_PLAYING, - }, { - pref: PrefKey.STATS_QUICK_GLANCE_ENABLED, - onChange: (e: InputEvent) => { - const streamStats = StreamStats.getInstance(); - (e.target! as HTMLInputElement).checked ? streamStats.quickGlanceSetup() : streamStats.quickGlanceStop(); - }, - }, { - pref: PrefKey.STATS_ITEMS, - onChange: StreamStats.refreshStyles, - }, { - pref: PrefKey.STATS_POSITION, - onChange: StreamStats.refreshStyles, - }, { - pref: PrefKey.STATS_TEXT_SIZE, - onChange: StreamStats.refreshStyles, - }, { - pref: PrefKey.STATS_OPACITY_ALL, - onChange: StreamStats.refreshStyles, - }, { - pref: PrefKey.STATS_OPACITY_BACKGROUND, - onChange: StreamStats.refreshStyles, - }, { - pref: PrefKey.STATS_CONDITIONAL_FORMATTING, - onChange: StreamStats.refreshStyles, - }, + items: [ + StreamPref.STATS_SHOW_WHEN_PLAYING, + StreamPref.STATS_QUICK_GLANCE_ENABLED, + StreamPref.STATS_ITEMS, + StreamPref.STATS_POSITION, + StreamPref.STATS_TEXT_SIZE, + StreamPref.STATS_OPACITY_ALL, + StreamPref.STATS_OPACITY_BACKGROUND, + StreamPref.STATS_CONDITIONAL_FORMATTING, ], }]; @@ -668,7 +604,6 @@ export class SettingsDialog extends NavigationDialog { group: 'mkb', icon: BxIcon.NATIVE_MKB, items: this.TAB_MKB_ITEMS, - lazyContent: true, requiredVariants: 'full', }, @@ -683,6 +618,8 @@ export class SettingsDialog extends NavigationDialog { super(); BxLogger.info(this.LOG_TAG, 'constructor()'); + this.boundOnContextMenu = this.onContextMenu.bind(this); + this.settingsManager = SettingsManager.getInstance(); this.renderFullSettings = STATES.supportedRegion && STATES.isSignedIn; this.setupDialog(); @@ -696,13 +633,17 @@ export class SettingsDialog extends NavigationDialog { } // Trigger event - const $selectUserAgent = document.querySelector(`#bx_setting_${escapeCssSelector(PrefKey.USER_AGENT_PROFILE)}`); + const $selectUserAgent = document.querySelector(`#bx_setting_${escapeCssSelector(GlobalPref.USER_AGENT_PROFILE)}`); if ($selectUserAgent) { $selectUserAgent.disabled = true; BxEvent.dispatch($selectUserAgent, 'input', {}); $selectUserAgent.disabled = false; } }); + + BxEventBus.Stream.on('gameSettings.switched', ({ id }) => { + this.$tabContents.dataset.gameId = id.toString(); + }); } getDialog(): NavigationDialog { @@ -742,21 +683,6 @@ export class SettingsDialog extends NavigationDialog { 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]; - if (!settingTab) { - return; - } - - const items = (settingTab.items as Function)(); - const $tabContent = this.renderSettingsSection.call(this, settingTab, items); - this.$tabContents.appendChild($tabContent); - } - // Switch tab let $child: HTMLElement; const children = Array.from(this.$tabContents.children) as HTMLElement[]; @@ -767,12 +693,15 @@ export class SettingsDialog extends NavigationDialog { // Calculate size of controller-friendly select boxes calculateSelectBoxes($child as HTMLElement); - } else { + } else if ($child.dataset.tabGroup) { // Hide tab content $child.classList.add('bx-gone'); } } + // Toggle stream settings selection + this.$streamSettingsSelection.classList.toggle('bx-gone', $svg.dataset.group === 'global'); + // Highlight current tab button for (const $child of Array.from(this.$tabs.children)) { $child.classList.remove('bx-active'); @@ -785,10 +714,8 @@ export class SettingsDialog extends NavigationDialog { 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', this.onTabClicked); - return $svg; } @@ -803,8 +730,14 @@ export class SettingsDialog extends NavigationDialog { this.$btnGlobalReload.classList.add('bx-danger'); } + private onContextMenu(e: Event) { + e.preventDefault(); + const $elm = e.target; + $elm instanceof HTMLElement && this.resetHighlightedSetting($elm); + } + private renderServerSetting(setting: SettingTabSectionItem): HTMLElement { - let selectedValue = getPref(PrefKey.SERVER_REGION); + let selectedValue = getGlobalPref(GlobalPref.SERVER_REGION); const continents: Record { - setPref(setting.pref!, (e.target as HTMLSelectElement).value); + setGlobalPref(setting.pref! as GlobalPref, (e.target as HTMLSelectElement).value, 'ui'); this.onGlobalSettingChanged(e); }); @@ -889,13 +822,14 @@ export class SettingsDialog extends NavigationDialog { } private renderSettingRow(settingTab: SettingTab, $tabContent: HTMLElement, settingTabContent: SettingTabSection, setting: SettingTabSectionItem | string) { + // Convert pref key to object if (typeof setting === 'string') { setting = { - pref: setting as PrefKey, + pref: setting as AnyPref, } satisfies SettingTabSectionItem; } - const pref = setting.pref; + const pref = setting.pref!; let $control; if (setting.content) { @@ -905,13 +839,13 @@ export class SettingsDialog extends NavigationDialog { $control = setting.content; } } else if (!setting.unsupported) { - if (pref === PrefKey.SERVER_REGION) { + if (pref === GlobalPref.SERVER_REGION) { $control = this.renderServerSetting(setting); - } else if (pref === PrefKey.SCRIPT_LOCALE) { - $control = SettingElement.fromPref(pref, STORAGE.Global, async (e: Event) => { + } else if (pref === GlobalPref.SCRIPT_LOCALE) { + $control = SettingElement.fromPref(pref, async (e: Event) => { const newLocale = (e.target as HTMLSelectElement).value; - if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) { + if (getGlobalPref(GlobalPref.UI_CONTROLLER_FRIENDLY)) { let timeoutId = (e.target as any).timeoutId; timeoutId && window.clearTimeout(timeoutId); (e.target as any).timeoutId = window.setTimeout(() => { @@ -926,8 +860,8 @@ export class SettingsDialog extends NavigationDialog { this.onGlobalSettingChanged(e); }); - } else if (pref === PrefKey.USER_AGENT_PROFILE) { - $control = SettingElement.fromPref(PrefKey.USER_AGENT_PROFILE, STORAGE.Global, (e: Event) => { + } else if (pref === GlobalPref.USER_AGENT_PROFILE) { + $control = SettingElement.fromPref(GlobalPref.USER_AGENT_PROFILE, (e: Event) => { const $target = e.target as HTMLSelectElement; const value = $target.value as UserAgentProfile; let isCustom = value === UserAgentProfile.CUSTOM; @@ -943,25 +877,21 @@ export class SettingsDialog extends NavigationDialog { !(e.target as HTMLInputElement).disabled && this.onGlobalSettingChanged(e); }); } else { - let onChange = setting.onChange; - if (!onChange && settingTab.group === 'global') { - onChange = this.onGlobalSettingChanged; + $control = this.settingsManager.getElement(pref, setting.params); + if (settingTab.group === 'global') { + $control.addEventListener('input', this.onGlobalSettingChanged); } - - $control = SettingElement.fromPref(pref as PrefKey, STORAGE.Global, onChange, setting.params); } // Replace if it's non-controller friendly