🏡 index : quine-global-labs/bazaar.git

/*
 * bz-new-permissions-dialog.vala
 *
 * Copyright 2026 Alexander Vanhee
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

public class Bz.NewPermissionsDialog : Adw.Dialog {
    private Dex.Promise promise;
    private bool responded = false;

    private NewPermissionsDialog (Bz.UpdatePermissionInfo[] infos) {
        Object ();

        content_width = 420;
        title =  _("New Permissions");

        var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 12) {
            margin_top = 12,
            margin_bottom = 8,
            margin_start = 18,
            margin_end = 18,
        };

        if (infos.length == 1)
            build_single_layout (box, infos[0]);
        else
            build_multi_layout (box, infos);

        var scrolled_window = new Gtk.ScrolledWindow () {
            hscrollbar_policy = Gtk.PolicyType.NEVER,
            vexpand = true,
            propagate_natural_height = true,
            child = box,
        };

        var confirm_button = new Gtk.Button.with_mnemonic (_("_Accept")) {
            halign = Gtk.Align.CENTER,
            margin_top = 12,
            margin_bottom = 18,
        };
        confirm_button.add_css_class ("suggested-action");
        confirm_button.add_css_class ("pill");
        confirm_button.clicked.connect (() => {
            responded = true;
            promise.resolve_boolean (true);
            close ();
        });

        var toolbar_view = new Adw.ToolbarView ();
        toolbar_view.add_top_bar (new Adw.HeaderBar () {show_title = infos.length > 1});
        toolbar_view.content = scrolled_window;
        toolbar_view.add_bottom_bar (confirm_button);

        child = toolbar_view;

        promise = new Dex.Promise ();

        this.closed.connect (() => {
            if (!responded) {
                responded = true;
                promise.resolve_boolean (false);
            }
        });
    }

    private void build_single_layout (Gtk.Box box, Bz.UpdatePermissionInfo info) {
        var status_page = new Adw.StatusPage () {
            vexpand = false,
        };
        status_page.add_css_class ("compact");

        var permissions_list = new Gtk.ListBox () {
            selection_mode = Gtk.SelectionMode.NONE,
        };
        permissions_list.add_css_class ("boxed-list");

        var group = new Adw.PreferencesGroup ();
        group.add (permissions_list);

        box.append (status_page);
        box.append (group);

        status_page.paintable = info.entry.icon_paintable;

        uint n_items = populate_permissions_list (permissions_list, info);

        status_page.title = status_page.title = ngettext (
            "New Permission for %s",
            "New Permissions for %s",
            n_items
        ).printf (info.entry.title);
        status_page.description = ngettext (
            "This update requires the following additional permission",
            "This update requires the following additional permissions",
            n_items
        );
    }

    private void build_multi_layout (Gtk.Box box, Bz.UpdatePermissionInfo[] infos) {
        box.spacing = 24;
        foreach (var info in infos) {
            var entry = info.entry;

            var app_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 8);

            var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12) {
                valign = Gtk.Align.CENTER,
                margin_start = 8,
            };

            if (entry.icon_paintable != null) {
                var icon = new Gtk.Image.from_paintable (entry.icon_paintable) {
                    pixel_size = 48,
                };
                header_box.append (icon);
            }

            var title_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) {
                valign = Gtk.Align.CENTER,
            };

            var title_label = new Gtk.Label (entry.title) {
                halign = Gtk.Align.START,
                xalign = 0,
            };
            title_label.add_css_class ("heading");
            title_box.append (title_label);

            if (entry.developer != null) {
                var developer_label = new Gtk.Label (entry.developer) {
                    halign = Gtk.Align.START,
                    xalign = 0,
                };
                developer_label.add_css_class ("dim-label");
                title_box.append (developer_label);
            }

            header_box.append (title_box);

            var permissions_list = new Gtk.ListBox () {
                selection_mode = Gtk.SelectionMode.NONE,
            };
            permissions_list.add_css_class ("boxed-list");
            populate_permissions_list (permissions_list, info);

            app_box.append (header_box);
            app_box.append (permissions_list);

            box.append (app_box);
        }
    }

    private uint populate_permissions_list (Gtk.ListBox list, Bz.UpdatePermissionInfo info) {
        var permissions = info.additional_permissions;
        uint n_items = 0;

        if (permissions != null) {
            var model = Bz.SafetyCalculator.analyze_permissions (permissions, false);
            n_items = model.get_n_items ();

            for (int level = Bz.Importance.IMPORTANT; level >= Bz.Importance.UNIMPORTANT; level--) {
                for (uint j = 0; j < n_items; j++) {
                    var row_data = (Bz.SafetyRow) model.get_item (j);

                    if (row_data.importance != level)
                        continue;

                    var row = Bz.context_row_new (
                        row_data.icon_name,
                        row_data.importance,
                        row_data.title,
                        row_data.subtitle
                    );
                    list.append (row);
                }
            }
        }

        return n_items;
    }

    public static Dex.Future present_and_wait (Gtk.Widget parent, Bz.UpdatePermissionInfo[] infos) {
        var dialog = new NewPermissionsDialog (infos);
        dialog.present (parent);
        return dialog.promise;
    }
}