All files / ext.wikilambda.app/store/stores languages.js

94.11% Statements 208/221
87.09% Branches 27/31
100% Functions 12/12
94.11% Lines 208/221

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 19x 19x 19x 19x 19x 19x 126x 126x 126x 126x 126x 126x 126x 126x 72x 72x 72x 72x         72x 126x 126x 126x 126x 126x 126x 126x 126x 36x 36x 36x 36x       36x 126x 126x 126x 126x 126x 126x 126x 126x 33x 126x 126x 126x 126x 126x 126x 126x 126x 20x 20x 20x 126x 126x 126x 126x 126x 126x 126x 126x 9x 9x 9x 9x 9x 9x 9x 19x 19x 19x 19x 9x 9x 9x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 8x 4x 4x 4x 4x 126x 126x 126x 126x 126x 126x 126x 126x 133x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 126x 4x 4x 4x 4x 4x 9x 9x 9x 9x 9x 6x 6x 9x 9x 9x 1x 1x 4x 4x 4x 4x 4x 4x 1x 1x 3x 3x 3x 3x 3x 4x     4x 4x 3x 3x 3x 4x 3x 3x 3x 3x 3x 3x         3x 3x 3x 3x 4x 3x 3x 3x 3x 4x 126x 126x  
/*!
 * WikiLambda Vue editor: Pinia store for language-related state, actions, mutations and getters
 *
 * Single source of truth for language preferences, code↔zid mapping, fallback,
 * and ensuring/fetching. Library stays the generic cache for ZObjects and other
 * auxiliary data.
 *
 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
 * @license MIT
 */
'use strict';
 
const Constants = require( '../../Constants.js' );
const LabelData = require( '../classes/LabelData.js' );
const apiUtils = require( '../../utils/apiUtils.js' );
 
module.exports = {
	state: {
		/**
		 * Map of the available language zids in the store indexed by language code
		 */
		languages: {},
		/**
		 * Collection of in-flight language-code-to-ZID requests
		 * code: promise
		 */
		languageCodeRequests: {}
	},
 
	getters: {
		/**
		 * Returns the language zid given a language ISO code if the
		 * object has been fetched and is stored in the state.
		 *
		 * @param {Object} state
		 * @return {Function}
		 */
		getLanguageZidOfCode: function ( state ) {
			/**
			 * @param {string} code
			 * @return {string|undefined}
			 */
			const findLanguageZid = ( code ) => state.languages[ code ];
			return findLanguageZid;
		},
 
		/**
		 * Get user preferred language code, from config variable zlang
		 *
		 * @return {string}
		 */
		getUserLangCode: function () {
			// If wikilambda config is set up, return zlang
			if ( this.getWikilambdaConfig.zlang ) {
				return this.getWikilambdaConfig.zlang;
			}
			// Else return userLang only if it's a valid language code
			// or default to English if it's not:
			const userLang = mw.config.get( 'wgUserLanguage' );
			const contentLang = mw.config.get( 'wgPageContentLanguage' );
			return $.uls.data.languages[ userLang ] ? userLang : contentLang;
		},
 
		/**
		 * Get user preferred language zid, from config variable zlangZid
		 *
		 * @return {string}
		 */
		getUserLangZid: function () {
			// If wikilambda config is set up, return zlangZid
			if ( this.getWikilambdaConfig.zlangZid ) {
				return this.getWikilambdaConfig.zlangZid;
			}
			// Else return the Zid for getUserLangCode if it has been fetched
			// or default to English if it hasn't:
			const langZid = this.getLanguageZidOfCode( this.getUserLangCode );
			return langZid || Constants.Z_NATURAL_LANGUAGE_ENGLISH;
		},
 
		/**
		 * Return user requested lang, which might not be a valid WF language.
		 *
		 * @return {string}
		 */
		getUserRequestedLang: function () {
			return mw.language.getFallbackLanguageChain()[ 0 ];
		},
 
		/**
		 * Return the list of fallback languages in their Zid representations.
		 *
		 * @return {Array}
		 */
		getFallbackLanguageZids: function () {
			return mw.language.getFallbackLanguageChain()
				.map( ( code ) => this.getLanguageZidOfCode( code ) )
				.filter( ( zid ) => !!zid );
		},
 
		/**
		 * Get `LabelData` object for a given language code.
		 *
		 * @return {Function} A function that accepts a language code and returns its `LabelData`.
		 */
		getLabelDataForLangCode: function () {
			/**
			 * Build a `LabelData` object for the specified language code.
			 *
			 * @param {string} langCode The language code to retrieve the `LabelData` for.
			 * @return {LabelData} The `LabelData` object containing label, language code, and directionality.
			 */
			const findLabelDataForLangCode = ( langCode ) => new LabelData(
				null,
				null,
				langCode,
				this.getLanguageIsoCodeOfZLang( langCode )
			);
			return findLabelDataForLangCode;
		}
	},
 
	actions: {
		/**
		 * Set or clear the in-flight request promise for a language code.
		 *
		 * @param {Object} payload
		 * @param {string} payload.code Language code
		 * @param {Promise|null} payload.request Promise for the in-flight request, or null to clear
		 */
		setLanguageCodeRequest: function ( payload ) {
			if ( payload.request ) {
				this.languageCodeRequests[ payload.code ] = payload.request;
			} else {
				delete this.languageCodeRequests[ payload.code ];
			}
		},
 
		/**
		 * @param {Object} payload
		 * @param {string} payload.code
		 * @param {string} payload.zid
		 */
		setLanguageCode: function ( payload ) {
			this.languages[ payload.code ] = payload.zid;
		},
 
		/**
		 * Orchestrates the call to the language-zids API to map language codes to ZLanguage ZIDs,
		 * then fetches those ZObjects. Codes already in state or in-flight are not requested again.
		 *
		 * * Codes are requested only once.
		 * * Every code is stored along with its request while it's being fetched.
		 * * Once it's fetched, the request is cleared.
		 * * The returning promise resolves when all requested codes have been handled.
		 *
		 * @param {Object} payload
		 * @param {string[]} payload.codes Array of language codes to ensure are fetched
		 * @return {Promise}
		 */
		ensureLanguageCodes: function ( payload ) {
			let requestCodes = [];
			const allPromises = [];
			const { codes = [] } = payload;
 
			codes.forEach( ( code ) => {
				// Ignore if:
				// * Language code is empty
				// * Language code is already known (ZID stored)
				// * Language code is already being fetched
				if ( code && !this.languages[ code ] && !this.languageCodeRequests[ code ] ) {
					requestCodes.push( code );
				}
				// Capture pending promise to await if:
				// * Language code is waiting to be fetched
				if ( code in this.languageCodeRequests ) {
					allPromises.push( this.languageCodeRequests[ code ] );
				}
			} );
 
			// Keep only unique values
			requestCodes = [ ...new Set( requestCodes ) ];
 
			if ( !requestCodes.length ) {
				return Promise.all( allPromises );
			}
 
			// Fetch the language codes
			const batchPromise = apiUtils.fetchLanguageZids( { codes: requestCodes } ).then( ( entries ) => {
				const newZids = [];
				entries.forEach( ( entry ) => {
					if ( !entry || !entry.code || !entry.zid ) {
						return;
					}
					this.setLanguageCode( { code: entry.code, zid: entry.zid } );
					newZids.push( entry.zid );
				} );
				// Once it's back, unset active request for these codes
				requestCodes.forEach( ( code ) => {
					this.setLanguageCodeRequest( { code, request: null } );
				} );
				// Fetch the ZObjects for the new ZIDs
				if ( newZids.length > 0 ) {
					this.fetchZids( { zids: newZids } );
				}
			} ).catch( ( error ) => {
				requestCodes.forEach( ( code ) => {
					this.setLanguageCodeRequest( { code, request: null } );
				} );
				throw error;
			} );
 
			// Set active request for each code in this batch
			requestCodes.forEach( ( code ) => {
				this.setLanguageCodeRequest( { code, request: batchPromise } );
			} );
			allPromises.push( batchPromise );
 
			return Promise.all( allPromises );
		}
	}
};