🏡 index : quine-global/quine-anaconda-webui.git

/*
 * Copyright (C) 2025 Red Hat, Inc.
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

import cockpit from "cockpit";

import {
    getPayloadEnvironmentsAction,
    getPayloadGroupsAction,
    getPayloadPackagesSelectionAction,
} from "../actions/payload-dnf-actions.js";

import { _getProperty, _setProperty, objectFromDbus, objectToDbus } from "./helpers.js";

import { PayloadsClient } from "./payloads.js";

const INTERFACE_NAME = "org.fedoraproject.Anaconda.Modules.Payloads.Payload.DNF";

const callClient = (method, args = []) => {
    const payload = PayloadDNFClient.instance.payload;
    return new PayloadsClient().client.call(
        payload,
        INTERFACE_NAME,
        method,
        args
    ).then(res => res[0]);
};

const getProperty = (...args) => {
    const payload = PayloadDNFClient.instance.payload;
    return _getProperty(PayloadsClient, payload, INTERFACE_NAME, ...args);
};

const setProperty = (...args) => {
    const payload = PayloadDNFClient.instance.payload;
    return _setProperty(PayloadsClient, payload, INTERFACE_NAME, ...args);
};

export class PayloadDNFClient {
    constructor (client, dispatch, payload) {
        if (PayloadDNFClient.instance && PayloadDNFClient.instance.payload === payload) {
            return PayloadDNFClient.instance;
        }

        PayloadDNFClient.instance = this;

        // Reuse the PayloadsClient's DBus client
        this.client = client;
        this.dispatch = dispatch;
        this.payload = payload;
        this._lastEnvironment = null;
    }

    async init (args = {}) {
        this.startEventMonitor();

        await this.initData(args);
    }

    async initData ({ automatedInstall = false } = {}) {
        await this.dispatch(getPayloadEnvironmentsAction());
        await this.dispatch(getPayloadPackagesSelectionAction());

        let selection = await getPackagesSelection();
        const packagesKickstarted = await getPackagesKickstarted();

        const kickstarted = automatedInstall && packagesKickstarted;
        if (!kickstarted && !selection?.environment) {
            const defaultEnv = await getDefaultEnvironment();
            if (defaultEnv) {
                await setPackagesSelection({ environment: defaultEnv });
                await this.dispatch(getPayloadPackagesSelectionAction());
                selection = await getPackagesSelection();
            }
        }

        // Fetch groups for initial environment
        const environment = selection?.environment;
        this._lastEnvironment = environment;
        if (environment) {
            await this.dispatch(getPayloadGroupsAction(environment));
        }
    }

    _handleEnvironmentChange (environment) {
        // Fetch groups when environment changes
        if (environment !== this._lastEnvironment) {
            this._lastEnvironment = environment;
            this.dispatch(getPayloadGroupsAction(environment));
        }
    }

    stopEventMonitor () {
        this._subscription?.remove();
    }

    startEventMonitor () {
        this._subscription = this.client.subscribe(
            { },
            (path, iface, signal, args) => {
                switch (signal) {
                case "PropertiesChanged":
                    if (path === this.payload &&
                        args[0] === INTERFACE_NAME &&
                        Object.hasOwn(args[1], "PackagesSelection")) {
                        if (args[1].PackagesSelection.v.environment.v) {
                            this._handleEnvironmentChange(args[1].PackagesSelection.v.environment.v);
                        }
                        this.dispatch(getPayloadPackagesSelectionAction());
                    }
                    break;
                }
            }
        );
    }
}

export const getDefaultEnvironment = async () => {
    return callClient("GetDefaultEnvironment", []);
};

export const getEnvironments = async () => {
    return callClient("GetEnvironments", []);
};

export const resolveEnvironment = async (environmentSpec) => {
    return callClient("ResolveEnvironment", [environmentSpec]);
};

export const getEnvironmentData = async (environmentSpec) => {
    const structure = await callClient("GetEnvironmentData", [environmentSpec]);
    return objectFromDbus(structure);
};

export const getGroupData = async (groupSpec) => {
    const structure = await callClient("GetGroupData", [groupSpec]);
    return objectFromDbus(structure);
};

export const getPackagesSelection = async () => {
    const structure = await getProperty("PackagesSelection");
    return objectFromDbus(structure);
};

export const getPackagesKickstarted = async () => {
    return getProperty("PackagesKickstarted");
};

export const setPackagesSelection = async ({ environment, groups } = {}) => {
    const currentSelection = await getPackagesSelection();

    const updatedSelection = {
        ...currentSelection,
    };

    if (environment !== undefined) {
        updatedSelection.environment = environment || "";
    }

    if (groups !== undefined) {
        updatedSelection.groups = groups;
    }

    const structure = objectToDbus(updatedSelection);

    return setProperty("PackagesSelection", cockpit.variant("a{sv}", structure));
};