mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-07 16:17:20 +02:00
352 lines
12 KiB
TypeScript
352 lines
12 KiB
TypeScript
import { States, AppInterface, SCRIPT_HOME, SCRIPT_VERSION } from "../../utils/global";
|
|
import { CE, createButton, Icon, ButtonStyle } from "../../utils/html";
|
|
import { getPreferredServerRegion } from "../../utils/region";
|
|
import { UserAgent, UserAgentProfile } from "../../utils/user-agent";
|
|
import { getPref, Preferences, PrefKey, setPref, toPrefElement } from "../preferences";
|
|
import { t } from "../translation";
|
|
|
|
const SETTINGS_UI = {
|
|
'Better xCloud': {
|
|
items: [
|
|
PrefKey.BETTER_XCLOUD_LOCALE,
|
|
PrefKey.REMOTE_PLAY_ENABLED,
|
|
],
|
|
},
|
|
|
|
[t('server')]: {
|
|
items: [
|
|
PrefKey.SERVER_REGION,
|
|
PrefKey.STREAM_PREFERRED_LOCALE,
|
|
PrefKey.PREFER_IPV6_SERVER,
|
|
],
|
|
},
|
|
|
|
[t('stream')]: {
|
|
items: [
|
|
PrefKey.STREAM_TARGET_RESOLUTION,
|
|
PrefKey.STREAM_CODEC_PROFILE,
|
|
PrefKey.GAME_FORTNITE_FORCE_CONSOLE,
|
|
PrefKey.AUDIO_MIC_ON_PLAYING,
|
|
PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG,
|
|
|
|
PrefKey.SCREENSHOT_BUTTON_POSITION,
|
|
PrefKey.SCREENSHOT_APPLY_FILTERS,
|
|
|
|
PrefKey.AUDIO_ENABLE_VOLUME_CONTROL,
|
|
PrefKey.STREAM_COMBINE_SOURCES,
|
|
],
|
|
},
|
|
|
|
[t('local-co-op')]: {
|
|
items: [
|
|
PrefKey.LOCAL_CO_OP_ENABLED,
|
|
],
|
|
},
|
|
|
|
[t('mouse-and-keyboard')]: {
|
|
items: [
|
|
PrefKey.MKB_ENABLED,
|
|
PrefKey.MKB_HIDE_IDLE_CURSOR,
|
|
],
|
|
},
|
|
|
|
[t('touch-controller')]: {
|
|
note: !States.hasTouchSupport ? '⚠️ ' + t('device-unsupported-touch') : null,
|
|
unsupported: !States.hasTouchSupport,
|
|
items: [
|
|
PrefKey.STREAM_TOUCH_CONTROLLER,
|
|
PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF,
|
|
PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD,
|
|
PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM,
|
|
],
|
|
},
|
|
|
|
[t('loading-screen')]: {
|
|
items: [
|
|
PrefKey.UI_LOADING_SCREEN_GAME_ART,
|
|
PrefKey.UI_LOADING_SCREEN_WAIT_TIME,
|
|
PrefKey.UI_LOADING_SCREEN_ROCKET,
|
|
],
|
|
},
|
|
|
|
[t('ui')]: {
|
|
items: [
|
|
PrefKey.UI_LAYOUT,
|
|
PrefKey.STREAM_SIMPLIFY_MENU,
|
|
PrefKey.SKIP_SPLASH_VIDEO,
|
|
!AppInterface && PrefKey.UI_SCROLLBAR_HIDE,
|
|
PrefKey.HIDE_DOTS_ICON,
|
|
PrefKey.REDUCE_ANIMATIONS,
|
|
],
|
|
},
|
|
|
|
[t('other')]: {
|
|
items: [
|
|
PrefKey.BLOCK_SOCIAL_FEATURES,
|
|
PrefKey.BLOCK_TRACKING,
|
|
],
|
|
},
|
|
|
|
[t('advanced')]: {
|
|
items: [
|
|
PrefKey.USER_AGENT_PROFILE,
|
|
],
|
|
},
|
|
};
|
|
|
|
|
|
export function setupSettingsUi() {
|
|
// Avoid rendering the Settings multiple times
|
|
if (document.querySelector('.bx-settings-container')) {
|
|
return;
|
|
}
|
|
|
|
const PREF_PREFERRED_REGION = getPreferredServerRegion();
|
|
const PREF_LATEST_VERSION = getPref(PrefKey.LATEST_VERSION);
|
|
|
|
let $reloadBtnWrapper: HTMLButtonElement;
|
|
|
|
// Setup Settings UI
|
|
const $container = CE<HTMLElement>('div', {
|
|
'class': 'bx-settings-container bx-gone',
|
|
});
|
|
|
|
let $updateAvailable;
|
|
|
|
const $wrapper = CE<HTMLElement>('div', {'class': 'bx-settings-wrapper'},
|
|
CE<HTMLElement>('div', {'class': 'bx-settings-title-wrapper'},
|
|
CE('a', {
|
|
'class': 'bx-settings-title',
|
|
'href': SCRIPT_HOME,
|
|
'target': '_blank',
|
|
}, 'Better xCloud ' + SCRIPT_VERSION),
|
|
createButton({icon: Icon.QUESTION, label: t('help'), url: 'https://better-xcloud.github.io/features/'}),
|
|
)
|
|
);
|
|
$updateAvailable = CE('a', {
|
|
'class': 'bx-settings-update bx-gone',
|
|
'href': 'https://github.com/redphx/better-xcloud/releases',
|
|
'target': '_blank',
|
|
});
|
|
|
|
$wrapper.appendChild($updateAvailable);
|
|
|
|
// Show new version indicator
|
|
if (PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION) {
|
|
$updateAvailable.textContent = `🌟 Version ${PREF_LATEST_VERSION} available`;
|
|
$updateAvailable.classList.remove('bx-gone');
|
|
}
|
|
|
|
// Show link to Android app
|
|
if (!AppInterface) {
|
|
const userAgent = UserAgent.getDefault().toLowerCase();
|
|
if (userAgent.includes('android')) {
|
|
const $btn = createButton({
|
|
label: '🔥 ' + t('install-android'),
|
|
style: ButtonStyle.FULL_WIDTH | ButtonStyle.FOCUSABLE,
|
|
url: 'https://better-xcloud.github.io/android',
|
|
});
|
|
|
|
$wrapper.appendChild($btn);
|
|
}
|
|
}
|
|
|
|
const onChange = (e: Event) => {
|
|
if (!$reloadBtnWrapper) {
|
|
return;
|
|
}
|
|
|
|
$reloadBtnWrapper.classList.remove('bx-gone');
|
|
|
|
if ((e.target as HTMLElement).id === 'bx_setting_' + PrefKey.BETTER_XCLOUD_LOCALE) {
|
|
// Update locale
|
|
refreshLocale();
|
|
|
|
const $btn = $reloadBtnWrapper.firstElementChild! as HTMLButtonElement;
|
|
$btn.textContent = t('settings-reloading');
|
|
$btn.click();
|
|
}
|
|
};
|
|
|
|
// Render settings
|
|
for (let groupLabel in SETTINGS_UI) {
|
|
const $group = CE('span', {'class': 'bx-settings-group-label'}, groupLabel);
|
|
|
|
// Render note
|
|
if (SETTINGS_UI[groupLabel].note) {
|
|
const $note = CE('b', {}, SETTINGS_UI[groupLabel].note);
|
|
$group.appendChild($note);
|
|
}
|
|
|
|
$wrapper.appendChild($group);
|
|
|
|
// Don't render settings if this is an unsupported feature
|
|
if (SETTINGS_UI[groupLabel].unsupported) {
|
|
continue;
|
|
}
|
|
|
|
const settingItems = SETTINGS_UI[groupLabel].items
|
|
for (let settingId in settingItems) {
|
|
// Don't render custom settings
|
|
if (!settingId) {
|
|
continue;
|
|
}
|
|
|
|
const setting = Preferences.SETTINGS[settingId];
|
|
if (!setting) {
|
|
continue;
|
|
}
|
|
|
|
let settingLabel = setting.label;
|
|
let settingNote = setting.note || '';
|
|
|
|
// Add Experimental text
|
|
if (setting.experimental) {
|
|
settingLabel = '🧪 ' + settingLabel;
|
|
if (!settingNote) {
|
|
settingNote = t('experimental')
|
|
} else {
|
|
settingNote = `${t('experimental')}: ${settingNote}`
|
|
}
|
|
}
|
|
|
|
let $control;
|
|
let $inpCustomUserAgent: HTMLInputElement;
|
|
let labelAttrs = {};
|
|
|
|
if (settingId === PrefKey.USER_AGENT_PROFILE) {
|
|
let defaultUserAgent = (window.navigator as any).orgUserAgent || window.navigator.userAgent;
|
|
$inpCustomUserAgent = CE('input', {
|
|
'type': 'text',
|
|
'placeholder': defaultUserAgent,
|
|
'class': 'bx-settings-custom-user-agent',
|
|
});
|
|
$inpCustomUserAgent.addEventListener('change', e => {
|
|
setPref(PrefKey.USER_AGENT_CUSTOM, (e.target as HTMLInputElement).value.trim());
|
|
onChange(e);
|
|
});
|
|
|
|
$control = toPrefElement(PrefKey.USER_AGENT_PROFILE, (e: Event) => {
|
|
const value = (e.target as HTMLInputElement).value;
|
|
let isCustom = value === UserAgentProfile.CUSTOM;
|
|
let userAgent = UserAgent.get(value);
|
|
|
|
$inpCustomUserAgent.value = userAgent;
|
|
$inpCustomUserAgent.readOnly = !isCustom;
|
|
$inpCustomUserAgent.disabled = !isCustom;
|
|
|
|
onChange(e);
|
|
});
|
|
} else if (settingId === PrefKey.SERVER_REGION) {
|
|
let selectedValue;
|
|
|
|
$control = CE<HTMLSelectElement>('select', {id: `bx_setting_${settingId}`});
|
|
$control.name = $control.id;
|
|
|
|
$control.addEventListener('change', e => {
|
|
setPref(settingId, (e.target as HTMLSelectElement).value);
|
|
onChange(e);
|
|
});
|
|
|
|
selectedValue = PREF_PREFERRED_REGION;
|
|
|
|
setting.options = {};
|
|
for (let regionName in States.serverRegions) {
|
|
const region = States.serverRegions[regionName];
|
|
let value = regionName;
|
|
|
|
let label = `${region.shortName} - ${regionName}`;
|
|
if (region.isDefault) {
|
|
label += ` (${t('default')})`;
|
|
value = 'default';
|
|
|
|
if (selectedValue === regionName) {
|
|
selectedValue = 'default';
|
|
}
|
|
}
|
|
|
|
setting.options[value] = label;
|
|
}
|
|
|
|
for (let value in setting.options) {
|
|
const label = setting.options[value];
|
|
|
|
const $option = CE('option', {value: value}, label);
|
|
$control.appendChild($option);
|
|
}
|
|
|
|
// Select preferred region
|
|
$control.value = selectedValue;
|
|
} else {
|
|
if (settingId === PrefKey.BETTER_XCLOUD_LOCALE) {
|
|
$control = toPrefElement(settingId, (e: Event) => {
|
|
localStorage.setItem('better_xcloud_locale', (e.target as HTMLSelectElement).value);
|
|
onChange(e);
|
|
});
|
|
} else {
|
|
$control = toPrefElement(settingId, onChange);
|
|
}
|
|
labelAttrs = {'for': $control.id, 'tabindex': 0};
|
|
}
|
|
|
|
// Disable unsupported settings
|
|
if (setting.unsupported) {
|
|
($control as HTMLInputElement).disabled = true;
|
|
}
|
|
|
|
const $label = CE('label', labelAttrs, settingLabel);
|
|
if (settingNote) {
|
|
$label.appendChild(CE('b', {}, settingNote));
|
|
}
|
|
const $elm = CE<HTMLElement>('div', {'class': 'bx-settings-row'},
|
|
$label,
|
|
$control
|
|
);
|
|
|
|
$wrapper.appendChild($elm);
|
|
|
|
// Add User-Agent input
|
|
if (settingId === PrefKey.USER_AGENT_PROFILE) {
|
|
$wrapper.appendChild($inpCustomUserAgent!);
|
|
// Trigger 'change' event
|
|
$control.dispatchEvent(new Event('change'));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Setup Reload button
|
|
const $reloadBtn = createButton({
|
|
label: t('settings-reload'),
|
|
style: ButtonStyle.DANGER | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
|
|
onClick: e => {
|
|
window.location.reload();
|
|
$reloadBtn.disabled = true;
|
|
$reloadBtn.textContent = t('settings-reloading');
|
|
},
|
|
});
|
|
$reloadBtn.setAttribute('tabindex', '0');
|
|
|
|
$reloadBtnWrapper = CE<HTMLButtonElement>('div', {'class': 'bx-settings-reload-button-wrapper bx-gone'}, $reloadBtn);
|
|
$wrapper.appendChild($reloadBtnWrapper);
|
|
|
|
// Donation link
|
|
const $donationLink = CE('a', {'class': 'bx-donation-link', href: 'https://ko-fi.com/redphx', target: '_blank'}, `❤️ ${t('support-better-xcloud')}`);
|
|
$wrapper.appendChild($donationLink);
|
|
|
|
// Show Game Pass app version
|
|
try {
|
|
const appVersion = (document.querySelector('meta[name=gamepass-app-version]') as HTMLMetaElement).content;
|
|
const appDate = new Date((document.querySelector('meta[name=gamepass-app-date]') as HTMLMetaElement).content).toISOString().substring(0, 10);
|
|
$wrapper.appendChild(CE<HTMLElement>('div', {'class': 'bx-settings-app-version'}, `xCloud website version ${appVersion} (${appDate})`));
|
|
} catch (e) {}
|
|
|
|
$container.appendChild($wrapper);
|
|
|
|
// Add Settings UI to the web page
|
|
const $pageContent = document.getElementById('PageContent');
|
|
$pageContent?.parentNode?.insertBefore($container, $pageContent);
|
|
}
|
|
function refreshLocale() {
|
|
throw new Error("Function not implemented.");
|
|
}
|