@applicaster/zapp-react-dom-app
Version:
Zapp App Component for Applicaster's Quick Brick React Native App
224 lines (183 loc) • 6.46 kB
JavaScript
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,
};