* Copyright (C) 2022 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import cockpit from "cockpit";
import { getKeyboardConfigurationAction, getKeyboardLayoutsAction, getLanguageAction, getLanguagesAction, setLanguageKickstartedAction } from "../actions/localization-actions.js";
import { convertToCockpitLang, getLangCookie, setLangCookie } from "../helpers/language.js";
import { debug, error } from "../helpers/log.js";
import { _callClient, _getProperty, _setProperty } from "./helpers.js";
import { setLocale } from "./boss.js";
const OBJECT_PATH = "/org/fedoraproject/Anaconda/Modules/Localization";
const INTERFACE_NAME = "org.fedoraproject.Anaconda.Modules.Localization";
const callClient = (...args) => {
return _callClient(LocalizationClient, OBJECT_PATH, INTERFACE_NAME, ...args);
};
const setProperty = (...args) => {
return _setProperty(LocalizationClient, OBJECT_PATH, INTERFACE_NAME, ...args);
};
const getProperty = (...args) => {
return _getProperty(LocalizationClient, OBJECT_PATH, INTERFACE_NAME, ...args);
};
export class LocalizationClient {
constructor (address, dispatch) {
if (LocalizationClient.instance && (!address || LocalizationClient.instance.address === address)) {
return LocalizationClient.instance;
}
LocalizationClient.instance?.client.close();
LocalizationClient.instance = this;
this.client = cockpit.dbus(
INTERFACE_NAME,
{ address, bus: "none", superuser: "try" }
);
this.address = address;
this.dispatch = dispatch;
}
async init (args = {}) {
this.client.addEventListener("close", () => error("Localization client closed"));
this.startEventMonitor();
await this.initData(args);
}
async initData ({ automatedInstall = false } = {}) {
const languageKickstarted = await getLanguageKickstarted();
this.dispatch(setLanguageKickstartedAction({
languageKickstarted: Boolean(languageKickstarted)
}));
await this.dispatch(getLanguageAction());
if (automatedInstall && languageKickstarted) {
const language = await getLanguage();
applyKickstartLanguage(language);
}
await this.dispatch(getLanguagesAction());
await this.dispatch(getKeyboardLayoutsAction());
await this.dispatch(getKeyboardConfigurationAction());
}
stopEventMonitor () {
this._subscription?.remove();
}
startEventMonitor () {
this._subscription = this.client.subscribe(
{ },
async (path, iface, signal, args) => {
switch (signal) {
case "CompositorSelectedLayoutChanged":
case "CompositorLayoutsChanged":
await this.dispatch(getKeyboardConfigurationAction());
break;
case "PropertiesChanged":
if (args[0] === INTERFACE_NAME && Object.hasOwn(args[1], "Language")) {
await this.dispatch(getLanguageAction());
* Currently, a race condition in the backend) causes the Language property to update,
* but the returned KeyboardLayouts still are translated with the previous locale.
* Workaround this by dispatching the KeyboardLayouts action with small delay.
*/
setTimeout(async () => {
this.dispatch(getKeyboardConfigurationAction());
this.dispatch(getKeyboardLayoutsAction());
}, 500);
} else {
debug(`Unhandled signal on ${path}: ${iface}.${signal}`, JSON.stringify(args));
}
break;
default:
debug(`Unhandled signal on ${path}: ${iface}.${signal}`, JSON.stringify(args));
}
});
}
}
* @returns {Promise} Resolves a list of language ids
*/
export const getLanguages = () => {
return callClient("GetLanguages", []);
};
* @returns {Promise} The language the system will use
*/
export const getLanguage = () => {
return getProperty("Language");
};
* @param {string} lang Language id
*
* @returns {Promise} Resolves a language data object
*/
export const getLanguageData = ({ lang }) => {
return callClient("GetLanguageData", [lang]);
};
* @param {string} lang Language id
*
* @returns {Promise} Resolves a list of locales ids
*/
export const getLocales = ({ lang }) => {
return callClient("GetLocales", [lang]);
};
* @returns {Promise} Resolves a list of common locales id's.
*/
export const getCommonLocales = () => {
return callClient("GetCommonLocales");
};
* @param {string} lang Locale id
*
* @returns {Promise} Resolves a locale data object
*/
export const getLocaleData = ({ locale }) => {
return callClient("GetLocaleData", [locale]);
};
* @param {string} lang Language id
*/
export const setLanguage = ({ lang }) => {
return setProperty("Language", cockpit.variant("s", lang));
};
export const setXLayouts = ({ layouts }) => {
return setProperty("XLayouts", cockpit.variant("as", layouts));
};
export const setCompositorLayouts = ({ layouts }) => {
return callClient("SetCompositorLayouts", [layouts, []]);
};
export const getKeyboardConfiguration = async ({ onFail, onSuccess }) => {
const task = await callClient("GetKeyboardConfigurationWithTask");
const taskProxy = new LocalizationClient().client.proxy(
"org.fedoraproject.Anaconda.Task",
task
);
const getTaskResult = async () => {
const result = await taskProxy.GetResult();
return onSuccess(result.v);
};
const addEventListeners = () => {
taskProxy.addEventListener("Stopped", () => taskProxy.Finish().catch(onFail));
taskProxy.addEventListener("Succeeded", getTaskResult);
};
taskProxy.wait(() => {
addEventListeners();
taskProxy.Start().catch(onFail);
});
};
* @returns {Promise} Resolves a list of locale keyboards
*/
export const getKeyboardLayouts = async () => {
const keyboards = await callClient("GetKeyboardLayouts", []);
return keyboards;
};
* @returns {Promise<string[]>} Current X keyboard layouts
*/
export const getXLayouts = () => {
return getProperty("XLayouts");
};
* @returns {Promise<boolean>} Whether the language was set in a kickstart
*/
export const getLanguageKickstarted = () => {
return getProperty("LanguageKickstarted");
};
* Apply a kickstart language to the running installer UI.
* Mirrors LanguageSelector.componentDidMount() logic.
*/
const applyKickstartLanguage = (language) => {
const cockpitLang = convertToCockpitLang({ lang: language });
if (getLangCookie() !== cockpitLang) {
setLangCookie({ cockpitLang });
window.location.reload(true);
}
setLocale({ locale: language });
};
export const setXKeyboardDefaults = async () => {
await setXLayouts({ layouts: [] });
await callClient("SetXKeyboardDefaults");
};