Compare commits

..

24 Commits

Author SHA1 Message Date
e0b04f306f Bump version to 6.0.5 2024-12-12 06:55:00 +07:00
a3c948b070 Fix problem with Smart TV profile and Guide menu (#594) 2024-12-12 06:53:25 +07:00
4e736175b4 Fix Bx button in Guide menu not working 2024-12-12 06:46:41 +07:00
cb66340177 Fix not showing Bx button in unsupported page 2024-12-12 06:35:07 +07:00
9f5f7b9d2e Bump version to 6.0.4 2024-12-11 20:37:10 +07:00
d04742bc25 Update translations 2024-12-11 20:36:51 +07:00
ed871bbe83 Update dists 2024-12-11 18:59:31 +07:00
dca8ab9cf6 Fix stats texts 2024-12-11 18:59:24 +07:00
1bf2f41813 Fix not getting the correct candidate pair 2024-12-11 18:55:28 +07:00
0fb3b7b7f7 Pad stats 2024-12-11 18:08:28 +07:00
7709cceff0 Add stat's background opacity 2024-12-11 17:50:04 +07:00
f8b8012f5c Add "ignoreNewsSection" patch 2024-12-11 17:21:20 +07:00
1d8517a997 Update <select multiple> CSS 2024-12-11 07:51:41 +07:00
c893bb2a5d Block notifications 2024-12-11 07:25:48 +07:00
46469e3949 Fix Guide CSS in TV layout 2024-12-11 06:12:15 +07:00
d8a085d43f Update suggestion's styles 2024-12-10 21:53:57 +07:00
b84c464066 Add "Disable features" setting 2024-12-10 21:30:21 +07:00
f0549b388a Update dists 2024-12-10 20:55:12 +07:00
9c3b1bd908 Change background color of selected options in <select multiple> 2024-12-10 20:54:53 +07:00
d671be21ee Also disable Friends feature when blocking social features 2024-12-10 20:53:43 +07:00
11aefb34d1 Fix overriding features not working 2024-12-10 20:51:27 +07:00
597cc9782d Always show error log 2024-12-10 20:51:00 +07:00
61cfd3f8db Alert new server 2024-12-10 20:50:42 +07:00
a3d5d6a819 Add note for local co-op feature 2024-12-10 20:50:27 +07:00
27 changed files with 552 additions and 279 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
// ==UserScript== // ==UserScript==
// @name Better xCloud // @name Better xCloud
// @namespace https://github.com/redphx // @namespace https://github.com/redphx
// @version 6.0.3 // @version 6.0.5
// ==/UserScript== // ==/UserScript==

File diff suppressed because one or more lines are too long

View File

@ -7,11 +7,11 @@
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
html[data-xds-platform=tv] & { body[data-bx-media-type=tv] & {
flex-direction: column; flex-direction: column;
} }
html:not([data-xds-platform=tv]) & { body:not([data-bx-media-type=tv]) & {
flex-direction: row; flex-direction: row;
> button:first-of-type { > button:first-of-type {
@ -34,7 +34,7 @@
flex-direction: row; flex-direction: row;
gap: 12px; gap: 12px;
html[data-xds-platform=tv] & { body[data-bx-media-type=tv] & {
flex-direction: column; flex-direction: column;
button { button {
@ -42,7 +42,7 @@
} }
} }
html:not([data-xds-platform=tv]) & { body:not([data-bx-media-type=tv]) & {
button { button {
span { span {
display: none; display: none;

View File

@ -140,8 +140,28 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
font-family: var(--bx-normal-font) !important; font-family: var(--bx-normal-font) !important;
} }
select[multiple] { select[multiple], select[multiple]:focus {
overflow: auto; overflow: auto;
border: none;
option {
padding: 4px 6px;
&:checked {
color = #1a7bc0;
background: color linear-gradient(0deg, color 0%, color 100%);
&::before {
content: '';
font-size: 12px;
display: inline-block;
margin-right: 6px;
height: 100%;
line-height: 100%;
vertical-align: middle;
}
}
}
} }
/* Hide UI elements */ /* Hide UI elements */

View File

@ -324,18 +324,22 @@
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
background: #003861; background: #003861;
height: 45px;
align-items: center;
label { label {
flex: 1; flex: 1;
padding: 10px; align-content: center;
padding: 0 10px;
background: #004f87; background: #004f87;
height: 100%;
} }
span { span {
display: inline-block; display: inline-block;
align-self: center; align-self: center;
padding: 10px; padding: 10px;
width: 40px; width: 45px;
text-align: center; text-align: center;
} }

View File

@ -144,7 +144,7 @@ div[class^=StreamMenu-module__container] .bx-badges {
border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;
} }
&[data-transparent=true] { &[data-shadow=true] {
background: none; background: none;
filter: drop-shadow(1px 0 0 #000000f0) drop-shadow(-1px 0 0 #000000f0) drop-shadow(0 1px 0 #000000f0) drop-shadow(0 -1px 0 #000000f0); filter: drop-shadow(1px 0 0 #000000f0) drop-shadow(-1px 0 0 #000000f0) drop-shadow(0 1px 0 #000000f0) drop-shadow(0 -1px 0 #000000f0);
} }
@ -165,10 +165,10 @@ div[class^=StreamMenu-module__container] .bx-badges {
} }
span { span {
min-width: 60px;
display: inline-block; display: inline-block;
text-align: right; text-align: right;
vertical-align: middle; vertical-align: middle;
white-space: pre;
&[data-grade=good] { &[data-grade=good] {
color: #6bffff; color: #6bffff;
@ -181,9 +181,5 @@ div[class^=StreamMenu-module__container] .bx-badges {
&[data-grade=bad] { &[data-grade=bad] {
color: #ff5f5f; color: #ff5f5f;
} }
&:first-of-type {
min-width: 22px;
}
} }
} }

View File

@ -63,7 +63,7 @@ export const enum PrefKey {
SCREENSHOT_APPLY_FILTERS = 'screenshot.applyFilters', SCREENSHOT_APPLY_FILTERS = 'screenshot.applyFilters',
BLOCK_TRACKING = 'block.tracking', BLOCK_TRACKING = 'block.tracking',
BLOCK_SOCIAL_FEATURES = 'block.social', BLOCK_FEATURES = 'block.features',
LOADING_SCREEN_GAME_ART = 'loadingScreen.gameArt.show', LOADING_SCREEN_GAME_ART = 'loadingScreen.gameArt.show',
LOADING_SCREEN_SHOW_WAIT_TIME = 'loadingScreen.waitTime.show', LOADING_SCREEN_SHOW_WAIT_TIME = 'loadingScreen.waitTime.show',
@ -73,7 +73,6 @@ export const enum PrefKey {
UI_LAYOUT = 'ui.layout', UI_LAYOUT = 'ui.layout',
UI_SCROLLBAR_HIDE = 'ui.hideScrollbar', UI_SCROLLBAR_HIDE = 'ui.hideScrollbar',
UI_HIDE_SECTIONS = 'ui.hideSections', UI_HIDE_SECTIONS = 'ui.hideSections',
BYOG_DISABLED = 'feature.byog.disabled',
UI_GAME_CARD_SHOW_WAIT_TIME = 'ui.gameCard.waitTime.show', UI_GAME_CARD_SHOW_WAIT_TIME = 'ui.gameCard.waitTime.show',
UI_SIMPLIFY_STREAM_MENU = 'ui.streamMenu.simplify', UI_SIMPLIFY_STREAM_MENU = 'ui.streamMenu.simplify',
@ -104,8 +103,8 @@ export const enum PrefKey {
STATS_QUICK_GLANCE_ENABLED = 'stats.quickGlance.enabled', STATS_QUICK_GLANCE_ENABLED = 'stats.quickGlance.enabled',
STATS_POSITION = 'stats.position', STATS_POSITION = 'stats.position',
STATS_TEXT_SIZE = 'stats.textSize', STATS_TEXT_SIZE = 'stats.textSize',
STATS_TRANSPARENT = 'stats.transparent', STATS_OPACITY_ALL = 'stats.opacity.all',
STATS_OPACITY = 'stats.opacity', STATS_OPACITY_BACKGROUND = 'stats.opacity.background',
STATS_CONDITIONAL_FORMATTING = 'stats.colors', STATS_CONDITIONAL_FORMATTING = 'stats.colors',
REMOTE_PLAY_ENABLED = 'xhome.enabled', REMOTE_PLAY_ENABLED = 'xhome.enabled',

View File

@ -110,3 +110,11 @@ export const enum StreamVideoProcessing {
USM = 'usm', USM = 'usm',
CAS = 'cas', CAS = 'cas',
} }
export const enum BlockFeature {
CHAT = 'chat',
FRIENDS = 'friends',
BYOG = 'byog',
NOTIFICATIONS_INVITES = 'notifications-invites',
NOTIFICATIONS_ACHIEVEMENTS = 'notifications-achievements',
}

View File

@ -27,7 +27,7 @@ import { ScreenshotManager } from "./utils/screenshot-manager";
import { NativeMkbHandler } from "./modules/mkb/native-mkb-handler"; import { NativeMkbHandler } from "./modules/mkb/native-mkb-handler";
import { GuideMenu } from "./modules/ui/guide-menu"; import { GuideMenu } from "./modules/ui/guide-menu";
import { updateVideoPlayer } from "./modules/stream/stream-settings-utils"; import { updateVideoPlayer } from "./modules/stream/stream-settings-utils";
import { NativeMkbMode, TouchControllerMode, UiSection } from "./enums/pref-values"; import { BlockFeature, NativeMkbMode, TouchControllerMode, UiSection } from "./enums/pref-values";
import { HeaderSection } from "./modules/ui/header"; import { HeaderSection } from "./modules/ui/header";
import { GameTile } from "./modules/ui/game-tile"; import { GameTile } from "./modules/ui/game-tile";
import { ProductDetailsPage } from "./modules/ui/product-details"; import { ProductDetailsPage } from "./modules/ui/product-details";
@ -171,7 +171,7 @@ document.addEventListener('readystatechange', e => {
} }
// Hide "Play with Friends" skeleton section // Hide "Play with Friends" skeleton section
if (getPref<UiSection[]>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS)) { if (getPref<UiSection[]>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) || getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES).includes(BlockFeature.FRIENDS)) {
const $parent = document.querySelector('div[class*=PlayWithFriendsSkeleton]')?.closest<HTMLElement>('div[class*=HomePage-module]'); const $parent = document.querySelector('div[class*=PlayWithFriendsSkeleton]')?.closest<HTMLElement>('div[class*=HomePage-module]');
$parent && ($parent.style.display = 'none'); $parent && ($parent.style.display = 'none');
} }
@ -191,7 +191,7 @@ window.addEventListener('popstate', onHistoryChanged);
window.history.pushState = patchHistoryMethod('pushState'); window.history.pushState = patchHistoryMethod('pushState');
window.history.replaceState = patchHistoryMethod('replaceState'); window.history.replaceState = patchHistoryMethod('replaceState');
BxEventBus.Script.once('xcloudServerUnavailable', () => { BxEventBus.Script.once('xcloud.server.unavailable', () => {
STATES.supportedRegion = false; STATES.supportedRegion = false;
window.setTimeout(HeaderSection.watchHeader, 2000); window.setTimeout(HeaderSection.watchHeader, 2000);

View File

@ -1,7 +1,7 @@
import { AppInterface, SCRIPT_VERSION, STATES } from "@utils/global"; import { AppInterface, SCRIPT_VERSION, STATES } from "@utils/global";
import { BX_FLAGS } from "@utils/bx-flags"; import { BX_FLAGS } from "@utils/bx-flags";
import { BxLogger } from "@utils/bx-logger"; import { BxLogger } from "@utils/bx-logger";
import { hashCode, renderString } from "@utils/utils"; import { blockSomeNotifications, hashCode, renderString } from "@utils/utils";
import { BxEvent } from "@/utils/bx-event"; import { BxEvent } from "@/utils/bx-event";
import codeControllerShortcuts from "./patches/controller-shortcuts.js" with { type: "text" }; import codeControllerShortcuts from "./patches/controller-shortcuts.js" with { type: "text" };
@ -10,12 +10,11 @@ import codeLocalCoOpEnable from "./patches/local-co-op-enable.js" with { type: "
import codeRemotePlayEnable from "./patches/remote-play-enable.js" with { type: "text" }; import codeRemotePlayEnable from "./patches/remote-play-enable.js" with { type: "text" };
import codeRemotePlayKeepAlive from "./patches/remote-play-keep-alive.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 codeVibrationAdjust from "./patches/vibration-adjust.js" with { type: "text" };
import { FeatureGates } from "@/utils/feature-gates.js";
import { PrefKey, StorageKey } from "@/enums/pref-keys.js"; import { PrefKey, StorageKey } from "@/enums/pref-keys.js";
import { getPref } from "@/utils/settings-storages/global-settings-storage"; import { getPref } from "@/utils/settings-storages/global-settings-storage";
import { GamePassCloudGallery } from "@/enums/game-pass-gallery"; import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
import { t } from "@/utils/translation"; import { t } from "@/utils/translation";
import { NativeMkbMode, TouchControllerMode, UiLayout, UiSection } from "@/enums/pref-values"; import { BlockFeature, NativeMkbMode, TouchControllerMode, UiLayout, UiSection } from "@/enums/pref-values";
import { PatcherUtils } from "./patcher-utils.js"; import { PatcherUtils } from "./patcher-utils.js";
export type PatchName = keyof typeof PATCHES; export type PatchName = keyof typeof PATCHES;
@ -244,24 +243,6 @@ logFunc(logTag, '//', logMessage);
return str; return str;
}, },
// Override website's settings
overrideSettings(str: string) {
const index = str.indexOf(',EnableStreamGate:');
if (index < 0) {
return false;
}
// Find the next "},"
const endIndex = str.indexOf('},', index);
let newSettings = JSON.stringify(FeatureGates);
newSettings = newSettings.substring(1, newSettings.length - 1);
const newCode = ',' + newSettings;
str = PatcherUtils.insertAt(str, endIndex, newCode);
return str;
},
disableGamepadDisconnectedScreen(str: string) { disableGamepadDisconnectedScreen(str: string) {
const index = str.indexOf('"GamepadDisconnected_Title",'); const index = str.indexOf('"GamepadDisconnected_Title",');
if (index < 0) { if (index < 0) {
@ -713,6 +694,18 @@ true` + text;
return str; return str;
}, },
// Don't render News section
ignoreNewsSection(str: string) {
let index = str.indexOf('Logger("CarouselRow")');
index > -1 && (index = PatcherUtils.lastIndexOf(str, 'const ', index, 200));
if (index < 0) {
return false;
}
str = PatcherUtils.insertAt(str, index, 'return null;');
return str;
},
// Don't render "Play With Friends" sections // Don't render "Play With Friends" sections
ignorePlayWithFriendsSection(str: string) { ignorePlayWithFriendsSection(str: string) {
let index = str.indexOf('location:"PlayWithFriendsRow",'); let index = str.indexOf('location:"PlayWithFriendsRow",');
@ -954,7 +947,43 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
str = str.replace(text, text + 'return;'); str = str.replace(text, text + 'return;');
return str; return str;
} },
changeNotificationsSubscription(str: string) {
let text = ';buildSubscriptionQueryParamsForNotifications(';
let index = str.indexOf(text);
if (index < 0) {
return false;
}
index += text.length;
// Get parameter name
const subsVar = str[index];
// Find index after {
index = str.indexOf('{', index) + 1;
const blockFeatures = getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES);
const filters = [];
if (blockFeatures.includes(BlockFeature.NOTIFICATIONS_INVITES)) {
filters.push('GameInvite', 'PartyInvite');
}
if (blockFeatures.includes(BlockFeature.FRIENDS)) {
filters.push('Follower');
}
if (blockFeatures.includes(BlockFeature.NOTIFICATIONS_ACHIEVEMENTS)) {
filters.push('AchievementUnlock');
}
const newCode = `
let subs = ${subsVar};
subs = subs.filter(val => !${JSON.stringify(filters)}.includes(val));
${subsVar} = subs;
`;
str = PatcherUtils.insertAt(str, index, newCode);
return str;
},
}; };
let PATCH_ORDERS = PatcherUtils.filterPatches([ let PATCH_ORDERS = PatcherUtils.filterPatches([
@ -972,7 +1001,6 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([
'patchRequestInfoCrash', 'patchRequestInfoCrash',
'disableStreamGate', 'disableStreamGate',
'overrideSettings',
'broadcastPollingMode', 'broadcastPollingMode',
'patchGamepadPolling', 'patchGamepadPolling',
@ -1021,13 +1049,19 @@ let PATCH_ORDERS = PatcherUtils.filterPatches([
'enableConsoleLogging', 'enableConsoleLogging',
'enableXcloudLogger', 'enableXcloudLogger',
] : []), ] : []),
...(blockSomeNotifications() ? [
'changeNotificationsSubscription',
] : []),
]); ]);
const hideSections = getPref<UiSection[]>(PrefKey.UI_HIDE_SECTIONS);
let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([ let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([
getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) && 'ignorePlayWithFriendsSection', hideSections.includes(UiSection.NEWS) && 'ignoreNewsSection',
getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.ALL_GAMES) && 'ignoreAllGamesSection', hideSections.includes(UiSection.FRIENDS) && 'ignorePlayWithFriendsSection',
STATES.browser.capabilities.touch && getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.TOUCH) && 'ignorePlayWithTouchSection', hideSections.includes(UiSection.ALL_GAMES) && 'ignoreAllGamesSection',
(getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.NATIVE_MKB) || getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.MOST_POPULAR)) && 'ignoreSiglSections', STATES.browser.capabilities.touch && hideSections.includes(UiSection.TOUCH) && 'ignorePlayWithTouchSection',
hideSections.some(value => [UiSection.NATIVE_MKB, UiSection.MOST_POPULAR].includes(value)) && 'ignoreSiglSections',
]); ]);
// Only when playing // Only when playing

View File

@ -253,8 +253,9 @@ export class StreamBadges {
const allAudioCodecs: Record<string, RTCBasicStat> = {}; const allAudioCodecs: Record<string, RTCBasicStat> = {};
let audioCodecId; let audioCodecId;
const allCandidates: Record<string, string> = {}; const allCandidatePairs: Record<string, string> = {};
let candidateId; const allRemoteCandidates: Record<string, string> = {};
let candidatePairId;
stats.forEach((stat: RTCBasicStat) => { stats.forEach((stat: RTCBasicStat) => {
if (stat.type === 'codec') { if (stat.type === 'codec') {
@ -275,10 +276,12 @@ export class StreamBadges {
} else if (stat.kind === 'audio') { } else if (stat.kind === 'audio') {
audioCodecId = stat.codecId; audioCodecId = stat.codecId;
} }
} else if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') { } else if (stat.type === 'transport' && (stat as unknown as RTCTransportStats).selectedCandidatePairId) {
candidateId = stat.remoteCandidateId; candidatePairId = (stat as unknown as RTCTransportStats).selectedCandidatePairId;
} else if (stat.type === 'candidate-pair') {
allCandidatePairs[stat.id] = (stat as unknown as RTCIceCandidatePairStats).remoteCandidateId;
} else if (stat.type === 'remote-candidate') { } else if (stat.type === 'remote-candidate') {
allCandidates[stat.id] = stat.address; allRemoteCandidates[stat.id] = stat.address;
} }
}); });
@ -336,12 +339,12 @@ export class StreamBadges {
} }
// Get server type // Get server type
if (candidateId) { if (candidatePairId) {
BxLogger.info('candidate', candidateId, allCandidates); BxLogger.info('candidate', candidatePairId, allCandidatePairs);
// Server + Region // Server + Region
let text = ''; let text = '';
const isIpv6 = allCandidates[candidateId].includes(':'); const isIpv6 = allRemoteCandidates[allCandidatePairs[candidatePairId]].includes(':');
const server = this.serverInfo.server; const server = this.serverInfo.server;
if (server && server.region) { if (server && server.region) {

View File

@ -193,12 +193,21 @@ export class StreamStats {
refreshStyles() { refreshStyles() {
const PREF_ITEMS = getPref<StreamStat[]>(PrefKey.STATS_ITEMS); const PREF_ITEMS = getPref<StreamStat[]>(PrefKey.STATS_ITEMS);
const PREF_OPACITY_BG = getPref<number>(PrefKey.STATS_OPACITY_BACKGROUND);
const $container = this.$container; const $container = this.$container;
$container.dataset.stats = '[' + PREF_ITEMS.join('][') + ']'; $container.dataset.stats = '[' + PREF_ITEMS.join('][') + ']';
$container.dataset.position = getPref(PrefKey.STATS_POSITION); $container.dataset.position = getPref(PrefKey.STATS_POSITION);
$container.dataset.transparent = getPref(PrefKey.STATS_TRANSPARENT);
$container.style.opacity = getPref(PrefKey.STATS_OPACITY) + '%'; if (PREF_OPACITY_BG === 0) {
$container.style.removeProperty('background-color');
$container.dataset.shadow = 'true';
} else {
delete $container.dataset.shadow;
$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.fontSize = getPref(PrefKey.STATS_TEXT_SIZE);
} }

View File

@ -282,12 +282,14 @@ export class SettingsDialog extends NavigationDialog {
PrefKey.UI_HIDE_SYSTEM_MENU_ICON, PrefKey.UI_HIDE_SYSTEM_MENU_ICON,
PrefKey.UI_DISABLE_FEEDBACK_DIALOG, PrefKey.UI_DISABLE_FEEDBACK_DIALOG,
PrefKey.UI_REDUCE_ANIMATIONS, PrefKey.UI_REDUCE_ANIMATIONS,
PrefKey.BLOCK_SOCIAL_FEATURES,
PrefKey.BYOG_DISABLED,
{ {
pref: PrefKey.UI_HIDE_SECTIONS, pref: PrefKey.UI_HIDE_SECTIONS,
multiLines: true, multiLines: true,
}, },
{
pref: PrefKey.BLOCK_FEATURES,
multiLines: true,
},
], ],
}, { }, {
requiredVariants: 'full', requiredVariants: 'full',
@ -628,10 +630,10 @@ export class SettingsDialog extends NavigationDialog {
pref: PrefKey.STATS_TEXT_SIZE, pref: PrefKey.STATS_TEXT_SIZE,
onChange: StreamStats.refreshStyles, onChange: StreamStats.refreshStyles,
}, { }, {
pref: PrefKey.STATS_OPACITY, pref: PrefKey.STATS_OPACITY_ALL,
onChange: StreamStats.refreshStyles, onChange: StreamStats.refreshStyles,
}, { }, {
pref: PrefKey.STATS_TRANSPARENT, pref: PrefKey.STATS_OPACITY_BACKGROUND,
onChange: StreamStats.refreshStyles, onChange: StreamStats.refreshStyles,
}, { }, {
pref: PrefKey.STATS_CONDITIONAL_FORMATTING, pref: PrefKey.STATS_CONDITIONAL_FORMATTING,

View File

@ -8,6 +8,9 @@ import { SettingsDialog } from "./dialog/settings-dialog";
import { TrueAchievements } from "@/utils/true-achievements"; import { TrueAchievements } from "@/utils/true-achievements";
import { BxIcon } from "@/utils/bx-icon"; import { BxIcon } from "@/utils/bx-icon";
import { BxEventBus } from "@/utils/bx-event-bus"; import { BxEventBus } from "@/utils/bx-event-bus";
import { getPref } from "@/utils/settings-storages/global-settings-storage";
import { UiLayout } from "@/enums/pref-values";
import { PrefKey } from "@/enums/pref-keys";
export enum GuideMenuTab { export enum GuideMenuTab {
HOME = 'home', HOME = 'home',
@ -41,7 +44,7 @@ export class GuideMenu {
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY, style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY,
onClick: () => { onClick: () => {
// Wait until the Guide dialog is closed // Wait until the Guide dialog is closed
BxEventBus.Script.once('xcloudDialogDismissed', () => { BxEventBus.Script.once('dialog.dismissed', () => {
setTimeout(() => SettingsDialog.getInstance().show(), 50); setTimeout(() => SettingsDialog.getInstance().show(), 50);
}); });
@ -111,6 +114,11 @@ export class GuideMenu {
class: 'bx-guide-home-buttons', class: 'bx-guide-home-buttons',
}); });
// Set TV tag
if (STATES.userAgent.isTv || getPref<UiLayout>(PrefKey.UI_LAYOUT) === UiLayout.TV) {
document.body.dataset.bxMediaType = 'tv';
}
for (const $button of buttonsLayout) { for (const $button of buttonsLayout) {
if (!$button) { if (!$button) {
continue; continue;

View File

@ -78,7 +78,7 @@ export class BxEventBus<TEvents extends Record<string, any>> {
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'on', event, callback); BX_FLAGS.Debug && BxLogger.warning('EventBus', 'on', event, callback);
} }
once<K extends keyof TEvents>(event: string, callback: EventCallback<TEvents[K]>): void { once<K extends keyof TEvents>(event: K, callback: EventCallback<TEvents[K]>): void {
const wrapper = (...args: any[]) => { const wrapper = (...args: any[]) => {
// @ts-ignore // @ts-ignore
callback(...args); callback(...args);

View File

@ -12,6 +12,7 @@ import { TouchController } from "@/modules/touch-controller";
import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values"; import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values";
import { Patcher, type PatchPage } from "@/modules/patcher/patcher"; import { Patcher, type PatchPage } from "@/modules/patcher/patcher";
import { BxEventBus } from "./bx-event-bus"; import { BxEventBus } from "./bx-event-bus";
import { FeatureGates } from "./feature-gates";
export enum SupportedInputType { export enum SupportedInputType {
CONTROLLER = 'Controller', CONTROLLER = 'Controller',
@ -36,6 +37,15 @@ export const BxExposed = {
BxLogger.error(LOG_TAG, e); BxLogger.error(LOG_TAG, e);
} }
// Override feature gates
try {
for (const exp in FeatureGates) {
state.experiments.overrideFeatureGates[exp.toLocaleLowerCase()] = FeatureGates[exp];
}
} catch (e) {
BxLogger.error(LOG_TAG, e);
}
// Add list of games with custom layouts to the official list // Add list of games with custom layouts to the official list
try { try {
const sigls = state.xcloud.sigls; const sigls = state.xcloud.sigls;

View File

@ -7,12 +7,12 @@ const enum TextColor {
} }
export class BxLogger { export class BxLogger {
static info = (tag: string, ...args: any[]) => BxLogger.log(TextColor.INFO, tag, ...args); static info = (tag: string, ...args: any[]) => BX_FLAGS.Debug && BxLogger.log(TextColor.INFO, tag, ...args);
static warning = (tag: string, ...args: any[]) => BxLogger.log(TextColor.WARNING, tag, ...args); static warning = (tag: string, ...args: any[]) => BX_FLAGS.Debug && BxLogger.log(TextColor.WARNING, tag, ...args);
static error = (tag: string, ...args: any[]) => BxLogger.log(TextColor.ERROR, tag, ...args); static error = (tag: string, ...args: any[]) => BxLogger.log(TextColor.ERROR, tag, ...args);
private static log(color: string, tag: string, ...args: any) { private static log(color: string, tag: string, ...args: any) {
BX_FLAGS.Debug && console.log(`%c[BxC]`, `color:${color};font-weight:bold;`, tag, '//', ...args); console.log(`%c[BxC]`, `color:${color};font-weight:bold;`, tag, '//', ...args);
} }
} }

View File

@ -1,6 +1,6 @@
import { CE } from "@utils/html"; import { CE } from "@utils/html";
import { compressCss, renderStylus } from "@macros/build" with { type: "macro" }; import { compressCss, renderStylus } from "@macros/build" with { type: "macro" };
import { UiSection } from "@/enums/pref-values"; import { BlockFeature, UiSection } from "@/enums/pref-values";
import { PrefKey } from "@/enums/pref-keys"; import { PrefKey } from "@/enums/pref-keys";
import { getPref } from "./settings-storages/global-settings-storage"; import { getPref } from "./settings-storages/global-settings-storage";
@ -18,7 +18,7 @@ export function addCss() {
} }
// Hide BYOG section // Hide BYOG section
if (getPref(PrefKey.BYOG_DISABLED)) { if (getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES).includes(BlockFeature.BYOG)) {
selectorToHide.push('#BodyContent > div[class*=ByogRow-module__container___]'); selectorToHide.push('#BodyContent > div[class*=ByogRow-module__container___]');
} }
@ -39,7 +39,7 @@ export function addCss() {
} }
// Hide "Start a party" button in the Guide menu // Hide "Start a party" button in the Guide menu
if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES)) { if (getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES).includes(BlockFeature.FRIENDS)) {
selectorToHide.push('#gamepass-dialog-root div[class^=AchievementsPreview-module__container] + button[class*=HomeLandingPage-module__button]'); selectorToHide.push('#gamepass-dialog-root div[class^=AchievementsPreview-module__container] + button[class*=HomeLandingPage-module__button]');
} }

View File

@ -1,7 +1,7 @@
import { PrefKey } from "@/enums/pref-keys"; import { PrefKey } from "@/enums/pref-keys";
import { BX_FLAGS } from "./bx-flags"; import { BX_FLAGS } from "./bx-flags";
import { getPref } from "./settings-storages/global-settings-storage"; import { getPref } from "./settings-storages/global-settings-storage";
import { NativeMkbMode } from "@/enums/pref-values"; import { BlockFeature, NativeMkbMode } from "@/enums/pref-values";
export let FeatureGates: { [key: string]: boolean } = { export let FeatureGates: { [key: string]: boolean } = {
PwaPrompt: false, PwaPrompt: false,
@ -17,12 +17,17 @@ if (nativeMkbMode !== NativeMkbMode.DEFAULT) {
} }
// Disable chat feature // Disable chat feature
if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES)) { const blockFeatures = getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES);
if (blockFeatures.includes(BlockFeature.CHAT)) {
FeatureGates.EnableGuideChatTab = false; FeatureGates.EnableGuideChatTab = false;
} }
if (blockFeatures.includes(BlockFeature.FRIENDS)) {
FeatureGates.EnableFriendsAndFollowers = false;
}
// Disable BYOG feature // Disable BYOG feature
if (getPref(PrefKey.BYOG_DISABLED)) { if (blockFeatures.includes(BlockFeature.BYOG)) {
FeatureGates.EnableBYOG = false; FeatureGates.EnableBYOG = false;
FeatureGates.EnableBYOGPurchase = false; FeatureGates.EnableBYOGPurchase = false;
} }

View File

@ -11,7 +11,8 @@ import { XcloudInterceptor } from "./xcloud-interceptor";
import { PrefKey } from "@/enums/pref-keys"; import { PrefKey } from "@/enums/pref-keys";
import { getPref } from "./settings-storages/global-settings-storage"; import { getPref } from "./settings-storages/global-settings-storage";
import type { RemotePlayConsoleAddresses } from "@/types/network"; import type { RemotePlayConsoleAddresses } from "@/types/network";
import { StreamResolution } from "@/enums/pref-values"; import { BlockFeature, StreamResolution } from "@/enums/pref-values";
import { blockAllNotifications } from "./utils";
type RequestType = 'xcloud' | 'xhome'; type RequestType = 'xcloud' | 'xhome';
@ -128,23 +129,37 @@ export function interceptHttpRequests() {
// Clear Applications Insight buffers // Clear Applications Insight buffers
clearAllLogs(); clearAllLogs();
BLOCKED_URLS = BLOCKED_URLS.concat([ BLOCKED_URLS.push(
'https://arc.msn.com', 'https://arc.msn.com',
'https://browser.events.data.microsoft.com', 'https://browser.events.data.microsoft.com',
'https://dc.services.visualstudio.com', 'https://dc.services.visualstudio.com',
'https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io', 'https://2c06dea3f26c40c69b8456d319791fd0@o427368.ingest.sentry.io',
'https://mscom.demdex.net', 'https://mscom.demdex.net',
]); );
} }
if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES)) {
BLOCKED_URLS = BLOCKED_URLS.concat([ // 'https://notificationinbox.xboxlive.com',
// 'https://accounts.xboxlive.com/family/memberXuid',
const blockFeatures = getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES);
if (blockFeatures.includes(BlockFeature.CHAT)) {
BLOCKED_URLS.push(
'https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox',
);
}
if (blockFeatures.includes(BlockFeature.FRIENDS)) {
BLOCKED_URLS.push(
'https://peoplehub.xboxlive.com/users/me/people/social', 'https://peoplehub.xboxlive.com/users/me/people/social',
'https://peoplehub.xboxlive.com/users/me/people/recommendations', 'https://peoplehub.xboxlive.com/users/me/people/recommendations',
'https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox', );
// 'https://notificationinbox.xboxlive.com', }
// 'https://accounts.xboxlive.com/family/memberXuid',
]); // Block all notifications
if (blockAllNotifications()) {
BLOCKED_URLS.push(
'https://notificationinbox.xboxlive.com/',
);
} }
const xhrPrototype = XMLHttpRequest.prototype; const xhrPrototype = XMLHttpRequest.prototype;
@ -159,11 +174,13 @@ export function interceptHttpRequests() {
}; };
xhrPrototype.send = function(...arg) { xhrPrototype.send = function(...arg) {
for (const blocked of BLOCKED_URLS) { for (const url of BLOCKED_URLS) {
if ((this as any)._url.startsWith(blocked)) { if ((this as any)._url.startsWith(url)) {
if (blocked === 'https://dc.services.visualstudio.com') { if (url === 'https://dc.services.visualstudio.com') {
window.setTimeout(clearAllLogs, 1000); window.setTimeout(clearAllLogs, 1000);
} }
BxLogger.warning('Blocked URL', url);
return false; return false;
} }
} }
@ -195,6 +212,7 @@ export function interceptHttpRequests() {
// Check blocked URLs // Check blocked URLs
for (const blocked of BLOCKED_URLS) { for (const blocked of BLOCKED_URLS) {
if (url.startsWith(blocked)) { if (url.startsWith(blocked)) {
BxLogger.warning('Blocked URL', url);
return new Response('{"acc":1,"webResult":{}}', { return new Response('{"acc":1,"webResult":{}}', {
status: 200, status: 200,
statusText: '200 OK', statusText: '200 OK',

View File

@ -8,7 +8,7 @@ import { CE } from "../html";
import { t, SUPPORTED_LANGUAGES } from "../translation"; import { t, SUPPORTED_LANGUAGES } from "../translation";
import { UserAgent } from "../user-agent"; import { UserAgent } from "../user-agent";
import { BaseSettingsStore as BaseSettingsStorage } from "./base-settings-storage"; import { BaseSettingsStore as BaseSettingsStorage } from "./base-settings-storage";
import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerStyleStandard, TouchControllerStyleCustom, GameBarPosition, DeviceVibrationMode, NativeMkbMode, UiLayout, UiSection, StreamPlayerType, StreamVideoProcessing, VideoRatio, StreamStat, VideoPosition } from "@/enums/pref-values"; import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerStyleStandard, TouchControllerStyleCustom, GameBarPosition, DeviceVibrationMode, NativeMkbMode, UiLayout, UiSection, StreamPlayerType, StreamVideoProcessing, VideoRatio, StreamStat, VideoPosition, BlockFeature } from "@/enums/pref-values";
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table"; import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table"; import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
import { GhPagesUtils } from "../gh-pages"; import { GhPagesUtils } from "../gh-pages";
@ -321,10 +321,14 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
requiredVariants: 'full', requiredVariants: 'full',
label: t('enable-local-co-op-support'), label: t('enable-local-co-op-support'),
default: false, default: false,
note: () => CE<HTMLAnchorElement>('a', { note: () => CE('div', {},
href: 'https://github.com/redphx/better-xcloud/discussions/275', CE<HTMLAnchorElement>('a', {
target: '_blank', href: 'https://github.com/redphx/better-xcloud/discussions/275',
}, t('enable-local-co-op-support-note')), target: '_blank',
}, t('enable-local-co-op-support-note')),
CE('br'),
'⚠️ ' + t('unexpected-behavior'),
),
}, },
[PrefKey.UI_CONTROLLER_SHOW_STATUS]: { [PrefKey.UI_CONTROLLER_SHOW_STATUS]: {
@ -582,25 +586,29 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
}, },
}, },
[PrefKey.BYOG_DISABLED]: {
label: t('disable-byog-feature'),
default: false,
},
[PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME]: { [PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME]: {
requiredVariants: 'full', requiredVariants: 'full',
label: t('show-wait-time-in-game-card'), label: t('show-wait-time-in-game-card'),
default: true, default: true,
}, },
[PrefKey.BLOCK_SOCIAL_FEATURES]: {
label: t('disable-social-features'),
default: false,
},
[PrefKey.BLOCK_TRACKING]: { [PrefKey.BLOCK_TRACKING]: {
label: t('disable-xcloud-analytics'), label: t('disable-xcloud-analytics'),
default: false, default: false,
}, },
[PrefKey.BLOCK_FEATURES]: {
label: t('disable-features'),
default: [],
multipleOptions: {
[BlockFeature.CHAT]: t('chat'),
[BlockFeature.FRIENDS]: t('friends-followers'),
[BlockFeature.BYOG]: t('stream-your-own-game'),
[BlockFeature.NOTIFICATIONS_INVITES]: t('notifications') + ': ' + t('invites'),
[BlockFeature.NOTIFICATIONS_ACHIEVEMENTS]: t('notifications') + ': ' + t('achievements'),
},
},
[PrefKey.USER_AGENT_PROFILE]: { [PrefKey.USER_AGENT_PROFILE]: {
label: t('user-agent-profile'), label: t('user-agent-profile'),
note: '⚠️ ' + t('unexpected-behavior'), note: '⚠️ ' + t('unexpected-behavior'),
@ -821,11 +829,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
'1.1rem': t('large'), '1.1rem': t('large'),
}, },
}, },
[PrefKey.STATS_TRANSPARENT]: { [PrefKey.STATS_OPACITY_ALL]: {
label: t('transparent-background'),
default: false,
},
[PrefKey.STATS_OPACITY]: {
label: t('opacity'), label: t('opacity'),
default: 80, default: 80,
min: 50, min: 50,
@ -836,6 +840,17 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
ticks: 10, ticks: 10,
}, },
}, },
[PrefKey.STATS_OPACITY_BACKGROUND]: {
label: t('background-opacity'),
default: 100,
min: 0,
max: 100,
params: {
steps: 10,
suffix: '%',
ticks: 10,
},
},
[PrefKey.STATS_CONDITIONAL_FORMATTING]: { [PrefKey.STATS_CONDITIONAL_FORMATTING]: {
label: t('conditional-formatting'), label: t('conditional-formatting'),
default: false, default: false,

View File

@ -96,7 +96,7 @@ export class StreamStatsCollector {
current: -1, current: -1,
grades: [40, 75, 100], grades: [40, 75, 100],
toString() { toString() {
return this.current === -1 ? '???' : this.current.toString(); return this.current === -1 ? '???' : this.current.toString().padStart(3, ' ');
}, },
}, },
@ -104,7 +104,7 @@ export class StreamStatsCollector {
current: 0, current: 0,
grades: [30, 40, 60], grades: [30, 40, 60],
toString() { toString() {
return `${this.current.toFixed(1)}ms`; return `${this.current.toFixed(1)}ms`.padStart(6, ' ');
}, },
}, },
@ -112,14 +112,14 @@ export class StreamStatsCollector {
current: 0, current: 0,
toString() { toString() {
const maxFps = getPref<VideoMaxFps>(PrefKey.VIDEO_MAX_FPS); const maxFps = getPref<VideoMaxFps>(PrefKey.VIDEO_MAX_FPS);
return maxFps < 60 ? `${maxFps}/${this.current}` : this.current.toString(); return maxFps < 60 ? `${maxFps}/${this.current}`.padStart(5, ' ') : this.current.toString();
}, },
}, },
[StreamStat.BITRATE]: { [StreamStat.BITRATE]: {
current: 0, current: 0,
toString() { toString() {
return `${this.current.toFixed(1)} Mbps`; return `${this.current.toFixed(1)} Mbps`.padStart(9, ' ');
}, },
}, },
@ -128,7 +128,7 @@ export class StreamStatsCollector {
dropped: 0, dropped: 0,
toString() { toString() {
const framesDroppedPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(1); const framesDroppedPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(1);
return framesDroppedPercentage === '0.00' ? this.dropped.toString() : `${this.dropped} (${framesDroppedPercentage}%)`; return framesDroppedPercentage === '0.0' ? this.dropped.toString() : `${this.dropped} (${framesDroppedPercentage}%)`;
}, },
}, },
@ -137,7 +137,7 @@ export class StreamStatsCollector {
dropped: 0, dropped: 0,
toString() { toString() {
const packetsLostPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(1); const packetsLostPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(1);
return packetsLostPercentage === '0.00' ? this.dropped.toString() : `${this.dropped} (${packetsLostPercentage}%)`; return packetsLostPercentage === '0.0' ? this.dropped.toString() : `${this.dropped} (${packetsLostPercentage}%)`;
}, },
}, },
@ -146,14 +146,14 @@ export class StreamStatsCollector {
total: 0, total: 0,
grades: [6, 9, 12], grades: [6, 9, 12],
toString() { toString() {
return isNaN(this.current) ? '??ms' : `${this.current.toFixed(1)}ms`; return isNaN(this.current) ? '??ms' : `${this.current.toFixed(1)}ms`.padStart(6, ' ');
}, },
}, },
[StreamStat.DOWNLOAD]: { [StreamStat.DOWNLOAD]: {
total: 0, total: 0,
toString() { toString() {
return humanFileSize(this.total); return humanFileSize(this.total).padStart(8, ' ');
}, },
}, },
@ -201,6 +201,7 @@ export class StreamStatsCollector {
}; };
private lastVideoStat?: RTCInboundRtpStreamStats | null; private lastVideoStat?: RTCInboundRtpStreamStats | null;
private selectedCandidatePairId: string | null | undefined = null;
private constructor() { private constructor() {
BxLogger.info(this.LOG_TAG, 'constructor()'); BxLogger.info(this.LOG_TAG, 'constructor()');
@ -212,6 +213,22 @@ export class StreamStatsCollector {
return; return;
} }
// Find selected candidate
if (!this.selectedCandidatePairId) {
let found = false;
stats.forEach(stat => {
if (found || stat.type !== 'transport') {
return;
}
stat = (stat as unknown as RTCTransportStats);
if (stat.iceState === 'connected' && stat.selectedCandidatePairId) {
this.selectedCandidatePairId = (stat as unknown as RTCTransportStats).selectedCandidatePairId;
found = true;
}
});
}
stats.forEach(stat => { stats.forEach(stat => {
if (stat.type === 'inbound-rtp' && stat.kind === 'video') { if (stat.type === 'inbound-rtp' && stat.kind === 'video') {
// FPS // FPS
@ -256,7 +273,7 @@ export class StreamStatsCollector {
dt.current = dt.total / framesDecodedDiff * 1000; dt.current = dt.total / framesDecodedDiff * 1000;
this.lastVideoStat = stat; this.lastVideoStat = stat;
} else if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') { } else if (this.selectedCandidatePairId && stat.type === 'candidate-pair' && stat.id === this.selectedCandidatePairId) {
// Round Trip Time // Round Trip Time
const ping = this.currentStats[StreamStat.PING]; const ping = this.currentStats[StreamStat.PING];
ping.current = stat.currentRoundTripTime ? stat.currentRoundTripTime * 1000 : -1; ping.current = stat.currentRoundTripTime ? stat.currentRoundTripTime * 1000 : -1;

View File

@ -27,6 +27,7 @@ export const SUPPORTED_LANGUAGES = {
}; };
const Texts = { const Texts = {
"achievements": "Achievements",
"activate": "Activate", "activate": "Activate",
"activated": "Activated", "activated": "Activated",
"active": "Active", "active": "Active",
@ -42,6 +43,7 @@ const Texts = {
"auto": "Auto", "auto": "Auto",
"back-to-home": "Back to home", "back-to-home": "Back to home",
"back-to-home-confirm": "Do you want to go back to the home page (without disconnecting)?", "back-to-home-confirm": "Do you want to go back to the home page (without disconnecting)?",
"background-opacity": "Background opacity",
"battery": "Battery", "battery": "Battery",
"battery-saving": "Battery saving", "battery-saving": "Battery saving",
"better-xcloud": "Better xCloud", "better-xcloud": "Better xCloud",
@ -60,6 +62,7 @@ const Texts = {
"cancel": "Cancel", "cancel": "Cancel",
"cant-stream-xbox-360-games": "Can't stream Xbox 360 games", "cant-stream-xbox-360-games": "Can't stream Xbox 360 games",
"center": "Center", "center": "Center",
"chat": "Chat",
"clarity-boost": "Clarity boost", "clarity-boost": "Clarity boost",
"clarity-boost-warning": "These settings don't work when the Clarity Boost mode is ON", "clarity-boost-warning": "These settings don't work when the Clarity Boost mode is ON",
"clear": "Clear", "clear": "Clear",
@ -84,6 +87,8 @@ const Texts = {
"contrast": "Contrast", "contrast": "Contrast",
"controller": "Controller", "controller": "Controller",
"controller-friendly-ui": "Controller-friendly UI", "controller-friendly-ui": "Controller-friendly UI",
"controller-mapping": "Controller mapping",
"controller-mapping-in-game": "In-game controller mapping",
"controller-shortcuts": "Controller shortcuts", "controller-shortcuts": "Controller shortcuts",
"controller-shortcuts-connect-note": "Connect a controller to use this feature", "controller-shortcuts-connect-note": "Connect a controller to use this feature",
"controller-shortcuts-in-game": "In-game controller shortcuts", "controller-shortcuts-in-game": "In-game controller shortcuts",
@ -103,6 +108,7 @@ const Texts = {
"device-vibration-not-using-gamepad": "On when not using gamepad", "device-vibration-not-using-gamepad": "On when not using gamepad",
"disable": "Disable", "disable": "Disable",
"disable-byog-feature": "Disable \"Stream your own game\" feature", "disable-byog-feature": "Disable \"Stream your own game\" feature",
"disable-features": "Disable features",
"disable-home-context-menu": "Disable context menu in Home page", "disable-home-context-menu": "Disable context menu in Home page",
"disable-post-stream-feedback-dialog": "Disable post-stream feedback dialog", "disable-post-stream-feedback-dialog": "Disable post-stream feedback dialog",
"disable-social-features": "Disable social features", "disable-social-features": "Disable social features",
@ -127,6 +133,7 @@ const Texts = {
"force-native-mkb-games": "Force native Mouse & Keyboard for these games", "force-native-mkb-games": "Force native Mouse & Keyboard for these games",
"fortnite-allow-stw-mode": "Allows playing \"Save the World\" mode on mobile", "fortnite-allow-stw-mode": "Allows playing \"Save the World\" mode on mobile",
"fortnite-force-console-version": "Fortnite: force console version", "fortnite-force-console-version": "Fortnite: force console version",
"friends-followers": "Friends and followers",
"game-bar": "Game Bar", "game-bar": "Game Bar",
"getting-consoles-list": "Getting the list of consoles...", "getting-consoles-list": "Getting the list of consoles...",
"guide": "Guide", "guide": "Guide",
@ -148,6 +155,7 @@ const Texts = {
"import": "Import", "import": "Import",
"increase": "Increase", "increase": "Increase",
"install-android": "Better xCloud app for Android", "install-android": "Better xCloud app for Android",
"invites": "Invites",
"japan": "Japan", "japan": "Japan",
"jitter": "Jitter", "jitter": "Jitter",
"keyboard-key": "Keyboard key", "keyboard-key": "Keyboard key",
@ -165,6 +173,7 @@ const Texts = {
"lowest-quality": "Lowest quality", "lowest-quality": "Lowest quality",
"manage": "Manage", "manage": "Manage",
"map-mouse-to": "Map mouse to", "map-mouse-to": "Map mouse to",
"mapping": "Mapping",
"may-not-work-properly": "May not work properly!", "may-not-work-properly": "May not work properly!",
"menu": "Menu", "menu": "Menu",
"microphone": "Microphone", "microphone": "Microphone",
@ -203,6 +212,7 @@ const Texts = {
"no-consoles-found": "No consoles found", "no-consoles-found": "No consoles found",
"no-controllers-connected": "No controllers connected", "no-controllers-connected": "No controllers connected",
"normal": "Normal", "normal": "Normal",
"notifications": "Notifications",
"off": "Off", "off": "Off",
"official": "Official", "official": "Official",
"on": "On", "on": "On",
@ -328,6 +338,7 @@ const Texts = {
"stream": "Stream", "stream": "Stream",
"stream-settings": "Stream settings", "stream-settings": "Stream settings",
"stream-stats": "Stream stats", "stream-stats": "Stream stats",
"stream-your-own-game": "Stream your own game",
"stretch": "Stretch", "stretch": "Stretch",
"suggest-settings": "Suggest settings", "suggest-settings": "Suggest settings",
"suggest-settings-link": "Suggest recommended settings for this device", "suggest-settings-link": "Suggest recommended settings for this device",

View File

@ -97,7 +97,7 @@ export class TrueAchievements {
} }
this.updateIds(xboxTitleId); this.updateIds(xboxTitleId);
if (document.documentElement.dataset.xdsPlatform === 'tv') { if (document.body.dataset.mediaType === 'tv') {
$div.appendChild(this.$link); $div.appendChild(this.$link);
} else { } else {
$div.appendChild(this.$button); $div.appendChild(this.$button);

View File

@ -5,6 +5,7 @@ import { Toast } from "./toast";
import { PrefKey } from "@/enums/pref-keys"; import { PrefKey } from "@/enums/pref-keys";
import { getPref, setPref } from "./settings-storages/global-settings-storage"; import { getPref, setPref } from "./settings-storages/global-settings-storage";
import { LocalDb } from "./local-db/local-db"; import { LocalDb } from "./local-db/local-db";
import { BlockFeature } from "@/enums/pref-values";
/** /**
* Check for update * Check for update
@ -155,3 +156,19 @@ export function clearAllData() {
alert(t('clear-data-success')); alert(t('clear-data-success'));
} }
export function blockAllNotifications() {
const blockFeatures = getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES);
const blockAll = [BlockFeature.FRIENDS, BlockFeature.NOTIFICATIONS_ACHIEVEMENTS, BlockFeature.NOTIFICATIONS_INVITES].every(value => blockFeatures.includes(value));
return blockAll;
}
export function blockSomeNotifications() {
const blockFeatures = getPref<BlockFeature[]>(PrefKey.BLOCK_FEATURES);
if (blockAllNotifications()) {
return false;
}
const blockSome = [BlockFeature.FRIENDS, BlockFeature.NOTIFICATIONS_ACHIEVEMENTS, BlockFeature.NOTIFICATIONS_INVITES].some(value => blockFeatures.includes(value));
return blockSome;
}

View File

@ -82,6 +82,7 @@ export class XcloudInterceptor {
region.contintent = serverExtra[regionName][1]; region.contintent = serverExtra[regionName][1];
} else { } else {
region.contintent = 'other'; region.contintent = 'other';
BX_FLAGS.Debug && alert('New server: ' + shortName);
} }
} }