From 7e75ffb5a35a73dc623924e5d4e03d2d475181d0 Mon Sep 17 00:00:00 2001 From: heliguy Date: Sat, 30 Mar 2024 00:06:47 -0400 Subject: [PATCH] Add ability to pin and unpin runtimes --- src/app_row_widget.py | 63 ++++++++++++++++++++++++++++++++++++++++++- src/common.py | 14 +++++++++- src/window.py | 53 ++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/app_row_widget.py b/src/app_row_widget.py index 323a8c7..716322d 100644 --- a/src/app_row_widget.py +++ b/src/app_row_widget.py @@ -27,12 +27,19 @@ class AppRow(Adw.ActionRow): if self.mask_label.get_visible() == True: self.info_button.set_visible(True) + return + + if self.pin_label.get_visible() == True: + self.info_button.set_visible(True) + return if self.eol_app_label.get_visible() == True: self.info_button.set_visible(True) + return if self.eol_runtime_label.get_visible() == True: self.info_button.set_visible(True) + return def set_masked(self, is_masked): self.mask_label.set_visible(is_masked) @@ -83,6 +90,18 @@ class AppRow(Adw.ActionRow): ) self.mask_label.add_css_class("warning") + self.pin_label = Gtk.Label( + label=_("Auto Removal Disabled"), + visible=False, + hexpand=True, + wrap=True, + valign=Gtk.Align.CENTER, + tooltip_text=_("{} is pinned and will not be auto removed even when it's required by no app").format( + self.app_name + ) + ) + self.pin_label.add_css_class("warning") + self.eol_app_label = Gtk.Label( label=_("App EOL"), visible=False, @@ -128,6 +147,7 @@ class AppRow(Adw.ActionRow): self.info_button.add_css_class("flat") info_box.append(self.mask_label) + info_box.append(self.pin_label) self.add_suffix(self.info_button) properties_button = Gtk.Button( @@ -161,6 +181,16 @@ class AppRow(Adw.ActionRow): advanced_menu_model = Gio.Menu() self.add_suffix(self.row_menu) + self.is_pinned = False + + if "user" in self.install_type: + if f"runtime/{self.app_ref}" in parent_window.user_pins: + self.is_pinned = True + + if "system" in self.install_type: + if f"runtime/{self.app_ref}" in parent_window.system_pins: + self.is_pinned = True + parent_window.create_action( ("copy-name" + str(index)), lambda *_, name=self.app_name, toast=_( @@ -284,6 +314,31 @@ class AppRow(Adw.ActionRow): ) advanced_menu_model.append_item(unmask_item) + if "runtime" in parent_window.host_flatpaks[index][12]: + parent_window.create_action( + ("pin" + str(index)), + lambda *_, d=self.app_id, type=self.install_type, index=index: parent_window.pin_flatpak( + self + ), + ) + pin_item = Gio.MenuItem.new(_("Disable Auto Removal"), f"win.pin{index}") + pin_item.set_attribute_value( + "hidden-when", GLib.Variant.new_string("action-disabled") + ) + advanced_menu_model.append_item(pin_item) + + parent_window.create_action( + ("unpin" + str(index)), + lambda *_, d=self.app_id, type=self.install_type, index=index: parent_window.pin_flatpak( + self + ), + ) + unpin_item = Gio.MenuItem.new(_("Enable Auto Removal"), f"win.unpin{index}") + unpin_item.set_attribute_value( + "hidden-when", GLib.Variant.new_string("action-disabled") + ) + advanced_menu_model.append_item(unpin_item) + if "runtime" not in parent_window.host_flatpaks[index][12]: parent_window.create_action( ("snapshot" + str(index)), @@ -314,7 +369,13 @@ class AppRow(Adw.ActionRow): else: parent_window.lookup_action(f"unmask{index}").set_enabled(False) + if self.is_runtime and self.is_pinned: + parent_window.lookup_action(f"pin{index}").set_enabled(False) + elif self.is_runtime: + parent_window.lookup_action(f"unpin{index}").set_enabled(False) + self.pin_label.set_visible(self.is_pinned) + row_menu_model.append_section(None, advanced_menu_model) self.row_menu.set_menu_model(row_menu_model) - self.info_button_show_or_hide() + self.info_button_show_or_hide() \ No newline at end of file diff --git a/src/common.py b/src/common.py index a62e260..3259260 100644 --- a/src/common.py +++ b/src/common.py @@ -119,7 +119,7 @@ class myUtils: return list - def get_host_pins(self): + def get_host_system_pins(self): output = subprocess.run( ["flatpak-spawn", "--host", "flatpak", "pin"], capture_output=True, @@ -131,6 +131,18 @@ class myUtils: data[i] = data[i].strip() return data + def get_host_user_pins(self): + output = subprocess.run( + ["flatpak-spawn", "--host", "flatpak", "pin", "--user"], + capture_output=True, + text=True, + env=self.new_env, + ).stdout + data = output.strip().split("\n") + for i in range(len(data)): + data[i] = data[i].strip() + return data + def get_host_remotes(self): output = subprocess.run( [ diff --git a/src/window.py b/src/window.py index a6c3de7..90e882a 100644 --- a/src/window.py +++ b/src/window.py @@ -487,6 +487,56 @@ class WarehouseWindow(Adw.ApplicationWindow): dialog.connect("response", on_continue) dialog.present() + def pin_flatpak(self, row): + def thread(*args): + command = f"flatpak-spawn --host flatpak pin --{row.install_type} runtime/{row.app_ref}" + if row.is_pinned: + command += " --remove" + response = subprocess.run( + command, + capture_output=True, + text=True, + shell=True + ).stderr + if response != "" and row.is_pinned: + GLib.idle_add(self.toast_overlay.add_toast(Adw.Toast.new(_("Could not enable auto removal")))) + return + elif response != "": + GLib.idle_add(self.toast_overlay.add_toast(Adw.Toast.new(_("Could not disable auto removal")))) + return + row.is_pinned = not row.is_pinned + GLib.idle_add(lambda *_, row=row: self.lookup_action(f"pin{row.index}").set_enabled(not row.is_pinned)) + GLib.idle_add(lambda *_, row=row: self.lookup_action(f"unpin{row.index}").set_enabled(row.is_pinned)) + GLib.idle_add(lambda *_, row=row: row.pin_label.set_visible(row.is_pinned)) + GLib.idle_add(lambda *_, row=row: row.info_button_show_or_hide()) + + def callback(*args): + print("done") + + def on_continue(dialog, response): + if response == "cancel": + return + + task = Gio.Task.new(None, None, None) + task.run_in_thread(thread) + + if row.is_pinned: + on_continue(self, None) + else: + dialog = Adw.AlertDialog.new( + _( + "Disable Automatic Removal for {}?" + ).format(row.app_name), + _( + "This will pin {} ensuring it well never be removed automatically, even if no app depends on it." + ).format(row.app_name), + ) + dialog.add_response("cancel", _("Cancel")) + dialog.set_close_response("cancel") + dialog.connect("response", on_continue) + dialog.add_response("continue", _("Disable Auto Removal")) + dialog.present(self) + def copy_item(self, to_copy, to_toast=None): self.clipboard.set(to_copy) if to_toast: @@ -864,6 +914,9 @@ class WarehouseWindow(Adw.ApplicationWindow): "is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT ) + self.system_pins = self.my_utils.get_host_system_pins() + self.user_pins = self.my_utils.get_host_user_pins() + self.new_env = dict(os.environ) self.new_env["LC_ALL"] = "C"