UNPKG

@applicaster/zapp-react-dom-app

Version:

Zapp App Component for Applicaster's Quick Brick React Native App

224 lines (183 loc) • 6.46 kB
import * as R from "ramda"; import { persistLanguageSelection } from "../../App/Loader/utils"; import { getAppData } from "../AppData"; import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick"; import { createLogger } from "@applicaster/zapp-react-native-utils/logger"; const { log_error, log_debug, log_warning } = createLogger({ category: "QuickBrickCommunicationModule", subsystem: "zapp-react-dom-app", }); const QuickBrickEventEmitter = {}; export const QuickBrickEvents = (function (eventEmitter) { let listeners = []; function unsubscribe(listener) { return function () { listeners = R.reject(R.is(listener), listeners); }; } function subscribe(listener) { listeners.push(listener); return unsubscribe(listener); } eventEmitter.sendEvent = function (event, payload) { // we are waiting for both listeners to be registered // before we actually fire the QUICK_BRICK_READY event // One comes from SplashLoader, the other one from the // "native splash" // because of defer script loading, the native splash // can sometimes subscribe to QB events after the // QUICK_BRICK_READY event is fired. if ( listeners.length < 2 && event === QUICK_BRICK_EVENTS.QUICK_BRICK_READY ) { setTimeout(() => { eventEmitter.sendEvent(event, payload); }, 100); return; } if (event === QUICK_BRICK_EVENTS.FORCE_APP_RELOAD) { window.location.reload(); return; } R.forEach((listener) => { listener(event, payload); }, listeners); }; return { subscribe, }; })(QuickBrickEventEmitter); try { window.QuickBrickEvents = QuickBrickEvents; } catch (e) { // eslint-disable-next-line no-console console.warn("cannot assign QuickBrickEvents to window", e.message); } export function quickBrickEvent(eventName, payload) { QuickBrickEventEmitter.sendEvent(eventName, payload); } const initialProps = {}; // required for legacy plugins; /** * Retrieves the browser locale, which represents the device locale for SmartTVs. * * @returns {string|undefined} The browser locale string (e.g., "en-US") or undefined if an error occurs. * * TODO: We can later get more accurate locale data with device APIs, if needed */ export function getBrowserLocale() { try { return window.navigator.language; } catch (e) { log_error("Could not determine locale", e); } } /** * Updates the UI language, persists the selection, and reloads the application. * This method is used by the QuickBrick Language Selector to change the app's language. * * @param {string} uiLanguage - en-UK, en-US, etc. * @returns {Promise<void>} * * Usage: plugins/quick-brick-language-selector/src/utils/index.ts */ export const setAppLanguage = async (uiLanguage) => { await persistLanguageSelection(uiLanguage); window.location.reload(); }; const buildTimeData = getAppData(); /** * This method retrieves the all of the language data we need from the build data, and the browser locale. * @returns localeData * @returns localeData.uiLanguage - The language code to use for the app. * @returns localeData.countryLocale - The country locale code. * @returns localeData.languageLocale - The language locale code. * @returns localeData.languageCode - The language code. */ export const getMatchingLanguageFromBuildData = () => { // Locale is a build time value we get from Zapp, so we can use it as a fallback const { locale, languages } = buildTimeData || {}; const browserLocale = getBrowserLocale(); if (!Array.isArray(languages)) { log_error("Missing required 'languages' from the build time data.", { buildTimeData, }); return { uiLanguage: locale, languageCode: locale, countryLocale: "", languageLocale: locale, }; } const userSelectedLanguage = window.localStorage.getItem( "applicaster.v2_::_uiLanguage" ); if (userSelectedLanguage) { if (languages.find((lang) => lang === userSelectedLanguage)) { const [languageCode, countryLocale] = userSelectedLanguage.split("-"); return { uiLanguage: userSelectedLanguage, languageCode, countryLocale: countryLocale || "", languageLocale: userSelectedLanguage, }; } log_debug( "User selected language is not present in available localizations, selecting language using browser or device locale", { userSelectedLanguage, languages } ); } if (!browserLocale) { log_warning( "Could not use browser or device locale to determine language, falling back to default locale", { browserLocale, locale } ); return { uiLanguage: locale, languageCode: locale, countryLocale: "", languageLocale: locale, }; } // Exact match const [browserLanguageCode, browserCountryLocale] = browserLocale.split("-"); const browserLocaleMatch = languages.find((lang) => lang === browserLocale); if (browserLocaleMatch) { return { uiLanguage: browserLocaleMatch, languageCode: browserLanguageCode, countryLocale: browserCountryLocale || "", languageLocale: browserLocaleMatch, }; } // If there is no user-selected language, or the user-selected language is not found in the list of supported languages, // and the browser locale is also not found in the list of supported languages, // then fallback to the default language from the list of supported languages, while preserving the country locale from the browser. const defaultLanguage = languages[0]; if (defaultLanguage) { const [languageCode, countryLocale] = defaultLanguage.split("-"); const defaultLocale = { uiLanguage: defaultLanguage, languageCode, countryLocale: countryLocale || "", languageLocale: defaultLanguage, }; log_debug( "Locale not found in the list of supported languages, falling back to the default language locale", { defaultLocale, browserLocale, languages } ); return defaultLocale; } }; const localeData = getMatchingLanguageFromBuildData(); /** * QuickBrickCommunicationModule object is a Polyfill that mimicks how we interface with the Native platforms */ export const QuickBrickCommunicationModule = { quickBrickEvent, initialProps, ...buildTimeData, ...localeData, platform: buildTimeData.deviceTarget, setAppLanguage, };