Make all frontend stored preferences global

Additional change:
* Delete some unused messages

Bug: T341278
Depends-On: I254d7d883771dbd394ba97799d731012f7aaaefc
Change-Id: Ic40da2af8ea46bd42cbebbdbceda2431bd8600ae
This commit is contained in:
Jon Robson 2024-06-05 19:17:00 -07:00 committed by Jdlrobson
parent c8074d2355
commit 0f594e0af7
9 changed files with 85 additions and 43 deletions

View file

@ -28,7 +28,7 @@
},
{
"resourceModule": "skins.vector.js",
"maxSize": "17.0 kB"
"maxSize": "17.5 kB"
},
{
"resourceModule": "skins.vector.legacy.js",

View file

@ -42,11 +42,6 @@
"vector-feature-custom-font-size-0-label": "Small",
"vector-feature-custom-font-size-1-label": "Standard",
"vector-feature-custom-font-size-2-label": "Large",
"skin-night-mode-name": "Color",
"skin-night-mode-description": "[Experimental version, high likelihood of experiencing issues] Reduces the light emitted by device screens.",
"skin-night-mode-0-label": "Light",
"skin-night-mode-1-label": "Dark",
"skin-night-mode-2-label": "Automatic",
"skin-theme-name": "Color",
"skin-theme-description": "[Experimental version, high likelihood of experiencing issues] Reduces the light emitted by device screens.",
"skin-theme-day-label": "Light",

View file

@ -59,11 +59,6 @@
"vector-feature-custom-font-size-0-label": "Label for small (legacy) font size. An item in a list that has the title {{msg-mw|Vector-feature-custom-font-size-name}} and that includes the following items:\n* {{msg-mw|vector-feature-custom-font-size-0-label}}\n* {{msg-mw|vector-feature-custom-font-size-1-label}}\n* {{msg-mw|vector-feature-custom-font-size-2-label}}",
"vector-feature-custom-font-size-1-label": "Label for standard font size. An item in a list that has the title {{msg-mw|Vector-feature-custom-font-size-name}} and that includes the following items:\n* {{msg-mw|vector-feature-custom-font-size-0-label}}\n* {{msg-mw|vector-feature-custom-font-size-1-label}}\n* {{msg-mw|vector-feature-custom-font-size-2-label}}",
"vector-feature-custom-font-size-2-label": "Label for large font size. An item in a list that has the title {{msg-mw|Vector-feature-custom-font-size-name}} and that includes the following items:\n* {{msg-mw|vector-feature-custom-font-size-0-label}}\n* {{msg-mw|vector-feature-custom-font-size-1-label}}\n* {{msg-mw|vector-feature-custom-font-size-2-label}}",
"skin-night-mode-name": "Label for setting that allows you to change theme.",
"skin-night-mode-description": "Description for dark/night mode.",
"skin-night-mode-0-label": "Label for light theme (standard mode).",
"skin-night-mode-1-label": "Label for night theme (dark/night mode).",
"skin-night-mode-2-label": "Label for night theme (automatic mode which respects any operating system setting).",
"skin-theme-name": "Label for setting that allows you to change theme. A title of a list that includes the following items:\n* {{msg-mw|skin-theme-os-label}}\n* {{msg-mw|skin-theme-day-label}}\n* {{msg-mw|skin-theme-night-label}}",
"skin-theme-description": "Description for dark/night mode.",
"skin-theme-day-label": "Label for light theme (standard mode). A title of a list that has the title {{msg-mw|skin-theme-name}}, and that includes the following items:\n* {{msg-mw|skin-theme-os-label}}\n* {{msg-mw|skin-theme-day-label}}\n* {{msg-mw|skin-theme-night-label}}",

View file

@ -521,8 +521,23 @@ class Hooks implements
*/
public function onGetPreferences( $user, &$prefs ): void {
$vectorPrefs = [
Constants::PREF_KEY_LIMITED_WIDTH => [
'type' => 'toggle',
'label-message' => 'vector-prefs-limited-width',
'section' => 'rendering/skin/skin-prefs',
'help-message' => 'vector-prefs-limited-width-help',
'hide-if' => [ '!==', 'skin', Constants::SKIN_NAME_MODERN ],
],
Constants::PREF_KEY_FONT_SIZE => [
'type' => 'api'
'type' => 'select',
'label-message' => 'vector-feature-custom-font-size-name',
'section' => 'rendering/skin/skin-prefs',
'options-messages' => [
'vector-feature-custom-font-size-0-label' => '0',
'vector-feature-custom-font-size-1-label' => '1',
'vector-feature-custom-font-size-2-label' => '2',
],
'hide-if' => [ '!==', 'skin', Constants::SKIN_NAME_MODERN ],
],
Constants::PREF_KEY_PAGE_TOOLS_PINNED => [
'type' => 'api'
@ -536,15 +551,17 @@ class Hooks implements
Constants::PREF_KEY_APPEARANCE_PINNED => [
'type' => 'api'
],
Constants::PREF_KEY_LIMITED_WIDTH => [
'type' => 'toggle',
'label-message' => 'vector-prefs-limited-width',
'section' => 'rendering/skin/skin-prefs',
'help-message' => 'vector-prefs-limited-width-help',
'hide-if' => [ '!==', 'skin', Constants::SKIN_NAME_MODERN ],
],
Constants::PREF_KEY_NIGHT_MODE => [
'type' => 'api'
'type' => 'select',
'label-message' => 'skin-theme-name',
'help-message' => 'skin-theme-description',
'section' => 'rendering/skin/skin-prefs',
'options-messages' => [
'skin-theme-day-label' => 'day',
'skin-theme-night-label' => 'night',
'skin-theme-os-label' => 'os',
],
'hide-if' => [ '!==', 'skin', Constants::SKIN_NAME_MODERN ],
],
];
$prefs += $vectorPrefs;

View file

@ -5,7 +5,11 @@
* @property {string} [type] defaults to radio. Supported: radio, switch
* @property {Function} [callback] callback executed after a client preference has been modified.
*/
let /** @type {MwApi} */ api;
/**
* @typedef {Object} UserPreferencesApi
* @property {Function} saveOptions
*/
/**
* @typedef {Object} PreferenceOption
* @property {string} label
@ -49,8 +53,9 @@ function getVisibleClientPreferences( config ) {
* @param {string} featureName
* @param {string} value
* @param {Record<string,ClientPreference>} config
* @param {UserPreferencesApi} [userPreferences]
*/
function toggleDocClassAndSave( featureName, value, config ) {
function toggleDocClassAndSave( featureName, value, config, userPreferences ) {
const pref = config[ featureName ];
const callback = pref.callback || ( () => {} );
if ( mw.user.isNamed() ) {
@ -64,8 +69,8 @@ function toggleDocClassAndSave( featureName, value, config ) {
document.documentElement.classList.add( `${ featureName }-clientpref-${ value }` );
// Ideally this should be taken care of via a single core helper function.
mw.util.debounce( () => {
api = api || new mw.Api();
api.saveOption( pref.preferenceKey, value ).then( () => {
userPreferences = userPreferences || new mw.Api();
userPreferences.saveOptions( { [ pref.preferenceKey ]: value } ).then( () => {
callback();
} );
}, 100 )();
@ -142,8 +147,9 @@ function makeExclusionNotice( featureName ) {
* @param {string} value
* @param {string} currentValue
* @param {Record<string,ClientPreference>} config
* @param {UserPreferencesApi} userPreferences
*/
function appendRadioToggle( parent, featureName, value, currentValue, config ) {
function appendRadioToggle( parent, featureName, value, currentValue, config, userPreferences ) {
const input = makeInputElement( 'radio', featureName, value );
input.classList.add( 'cdx-radio__input' );
if ( currentValue === value ) {
@ -165,7 +171,7 @@ function appendRadioToggle( parent, featureName, value, currentValue, config ) {
container.appendChild( label );
parent.appendChild( container );
input.addEventListener( 'change', () => {
toggleDocClassAndSave( featureName, value, config );
toggleDocClassAndSave( featureName, value, config, userPreferences );
} );
}
@ -175,8 +181,9 @@ function appendRadioToggle( parent, featureName, value, currentValue, config ) {
* @param {HTMLElement} labelElement
* @param {string} currentValue
* @param {Record<string,ClientPreference>} config
* @param {UserPreferencesApi} userPreferences
*/
function appendToggleSwitch( form, featureName, labelElement, currentValue, config ) {
function appendToggleSwitch( form, featureName, labelElement, currentValue, config, userPreferences ) {
const input = makeInputElement( 'checkbox', featureName, currentValue );
input.classList.add( 'cdx-toggle-switch__input' );
const switcher = document.createElement( 'span' );
@ -192,7 +199,7 @@ function appendToggleSwitch( form, featureName, labelElement, currentValue, conf
toggleSwitch.appendChild( switcher );
toggleSwitch.appendChild( label );
input.addEventListener( 'change', () => {
toggleDocClassAndSave( featureName, input.checked ? '1' : '0', config );
toggleDocClassAndSave( featureName, input.checked ? '1' : '0', config, userPreferences );
} );
form.appendChild( toggleSwitch );
}
@ -221,9 +228,10 @@ const getFeatureLabelMsg = ( featureName ) => mw.message( `${ featureName }-name
*
* @param {string} featureName
* @param {Record<string,ClientPreference>} config
* @param {UserPreferencesApi} userPreferences
* @return {Element|null}
*/
function makeControl( featureName, config ) {
function makeControl( featureName, config, userPreferences ) {
const pref = config[ featureName ];
const isExcluded = isFeatureExcluded( featureName );
@ -242,13 +250,13 @@ function makeControl( featureName, config ) {
switch ( type ) {
case 'radio':
pref.options.forEach( ( value ) => {
appendRadioToggle( form, featureName, value, String( currentValue ), config );
appendRadioToggle( form, featureName, value, String( currentValue ), config, userPreferences );
} );
break;
case 'switch': {
const labelElement = document.createElement( 'label' );
labelElement.textContent = getFeatureLabelMsg( featureName ).text();
appendToggleSwitch( form, featureName, labelElement, String( currentValue ), config );
appendToggleSwitch( form, featureName, labelElement, String( currentValue ), config, userPreferences );
break;
} default:
throw new Error( 'Unknown client preference! Only switch or radio are supported.' );
@ -266,8 +274,9 @@ function makeControl( featureName, config ) {
* @param {Element} parent
* @param {string} featureName
* @param {Record<string,ClientPreference>} config
* @param {UserPreferencesApi} userPreferences
*/
function makeClientPreference( parent, featureName, config ) {
function makeClientPreference( parent, featureName, config, userPreferences ) {
const labelMsg = getFeatureLabelMsg( featureName );
// If the user is not debugging messages and no language exists,
// exit as its a hidden client preference.
@ -305,7 +314,7 @@ function makeClientPreference( parent, featureName, config ) {
}
parent.appendChild( portlet );
const row = makeControl( featureName, config );
const row = makeControl( featureName, config, userPreferences );
if ( row ) {
const tmp = mw.util.addPortletLink( id, '', '' );
// create a dummy link
@ -324,16 +333,18 @@ function makeClientPreference( parent, featureName, config ) {
*
* @param {string} selector of element to fill with client preferences
* @param {Record<string,ClientPreference>} config
* @param {UserPreferencesApi} [userPreferences]
* @return {Promise<Node>}
*/
function render( selector, config ) {
function render( selector, config, userPreferences ) {
const node = document.querySelector( selector );
if ( !node ) {
return Promise.reject();
}
return new Promise( ( resolve ) => {
getVisibleClientPreferences( config ).forEach( ( pref ) => {
makeClientPreference( node, pref, config );
userPreferences = userPreferences || new mw.Api();
makeClientPreference( node, pref, config, userPreferences );
} );
mw.requestIdleCallback( () => {
resolve( node );
@ -345,8 +356,9 @@ function render( selector, config ) {
* @param {string} clickSelector what to click
* @param {string} renderSelector where to render
* @param {Record<string,ClientPreference>} config
* @param {UserPreferencesApi} [userPreferences]
*/
function bind( clickSelector, renderSelector, config ) {
function bind( clickSelector, renderSelector, config, userPreferences ) {
let enhanced = false;
const chk = /** @type {HTMLInputElement} */ (
document.querySelector( clickSelector )
@ -354,15 +366,18 @@ function bind( clickSelector, renderSelector, config ) {
if ( !chk ) {
return;
}
if ( !userPreferences ) {
userPreferences = new mw.Api();
}
if ( chk.checked ) {
render( renderSelector, config );
render( renderSelector, config, userPreferences );
enhanced = true;
} else {
chk.addEventListener( 'input', () => {
if ( enhanced ) {
return;
}
render( renderSelector, config );
render( renderSelector, config, userPreferences );
enhanced = true;
} );
}

View file

@ -1,3 +1,5 @@
const userPreferences = require( './userPreferences.js' );
/**
* T365083 - Disable night mode if night mode gadget is enabled
*
@ -60,8 +62,7 @@ function alterDisableLink( container ) {
disableOptions[ `gadget-${ gadgetName }` ] = 0;
} );
const api = new mw.Api();
api.saveOptions( disableOptions ).then( () => {
userPreferences.saveOptions( disableOptions ).then( () => {
window.location.reload();
} );
} );

View file

@ -1,7 +1,7 @@
/** @interface MwApi */
let /** @type {MwApi} */ api;
const debounce = require( /** @type {string} */ ( 'mediawiki.util' ) ).debounce;
const userPreferences = require( './userPreferences.js' );
/**
* Saves preference to user preferences and/or cookies.
@ -24,8 +24,9 @@ function save( feature, enabled ) {
}
} else {
debounce( () => {
api = api || new mw.Api();
api.saveOption( 'vector-' + feature, enabled ? 1 : 0 );
userPreferences.saveOptions( {
[ `vector-${ feature }` ]: enabled ? 1 : 0
} );
}, 500 )();
}
}

View file

@ -0,0 +1,17 @@
let /** @type {MwApi} */ api;
/**
* @param {Object<string,string|number>} options
* @return {JQuery.Promise<Object>}
*/
function saveOptions( options ) {
api = api || new mw.Api();
// @ts-ignore
return api.saveOptions( options, {
global: 'update'
} );
}
module.exports = {
saveOptions
};

View file

@ -415,6 +415,7 @@
"resources/skins.vector.js/sectionObserver.js",
"resources/skins.vector.js/deferUntilFrame.js",
"resources/skins.vector.js/pinnableElement.js",
"resources/skins.vector.js/userPreferences.js",
"resources/skins.vector.js/features.js",
"resources/skins.vector.js/limitedWidthToggle.js",
"resources/skins.vector.js/popupNotification.js",