From 15f6cab02858e9f339420955da8b06f91649dd12 Mon Sep 17 00:00:00 2001 From: heliguy4599 Date: Sun, 1 Oct 2023 08:48:19 -0400 Subject: [PATCH] Add a list of popular remotes This list will show when the user wants to add a new remote --- src/meson.build | 4 +- src/popular_remotes.blp | 62 ++++++++++++ src/popular_remotes_window.py | 179 ++++++++++++++++++++++++++++++++++ src/remotes.py | 117 +--------------------- src/right-large-symbolic.svg | 2 + src/warehouse.gresource.xml | 2 + 6 files changed, 253 insertions(+), 113 deletions(-) create mode 100644 src/popular_remotes.blp create mode 100644 src/popular_remotes_window.py create mode 100644 src/right-large-symbolic.svg diff --git a/src/meson.build b/src/meson.build index dc5eeac..a82f3c7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -8,6 +8,7 @@ blueprints = custom_target('blueprints', 'window.blp', 'orphans.blp', 'filter.blp', + 'popular_remotes.blp', ), output: '.', command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'], @@ -48,7 +49,8 @@ warehouse_sources = [ 'common.py', 'orphans.blp', 'filter_window.py', - 'filter.blp' + 'filter.blp', + 'popular_remotes_window.py', ] install_data(warehouse_sources, install_dir: moduledir) diff --git a/src/popular_remotes.blp b/src/popular_remotes.blp new file mode 100644 index 0000000..473e568 --- /dev/null +++ b/src/popular_remotes.blp @@ -0,0 +1,62 @@ +using Gtk 4.0; +using Adw 1; + +template PopularRemotesWindow : Adw.Window { + default-width: 500; + default-height: 450; + title: _("Add a Remote"); + + Adw.ToolbarView main_toolbar_view { + [top] + HeaderBar header_bar { + // [start] + // Button refresh_button { + // icon-name: "view-refresh-symbolic"; + // tooltip-text: _("Refresh the List of Installed Apps"); + // } + } + content: + Adw.ToastOverlay toast_overlay { + Stack main_stack { + Box main_box { + orientation: vertical; + Overlay main_overlay { + ScrolledWindow scrolled_window { + vexpand: true; + Adw.Clamp{ + Box { + orientation: vertical; + ListBox list_of_remotes { + margin-top: 6; + margin-bottom: 6; + margin-start: 12; + margin-end: 12; + hexpand: true; + valign: start; + selection-mode: none; + styles["boxed-list"] + } + ListBox custom_list { + margin-top: 6; + margin-bottom: 6; + margin-start: 12; + margin-end: 12; + hexpand: true; + valign: start; + selection-mode: none; + styles["boxed-list"] + } + } + } + } + } + } + Adw.StatusPage no_data { + icon-name: "check-plain-symbolic"; + title: _("No Leftover Data"); + description: _("There is no leftover user data"); + } + } + }; + } +} \ No newline at end of file diff --git a/src/popular_remotes_window.py b/src/popular_remotes_window.py new file mode 100644 index 0000000..5ea447f --- /dev/null +++ b/src/popular_remotes_window.py @@ -0,0 +1,179 @@ +from gi.repository import Gtk, Adw, GLib, Gdk, Gio +from .common import myUtils +import subprocess +import os +import pathlib +import re + +@Gtk.Template(resource_path="/io/github/flattool/Warehouse/popular_remotes.ui") +class PopularRemotesWindow(Adw.Window): + __gtype_name__ = "PopularRemotesWindow" + + list_of_remotes = Gtk.Template.Child() + custom_list = Gtk.Template.Child() + toast_overlay = Gtk.Template.Child() + + remotes = [ + ["elementary", "https://flatpak.elementary.io/repo.flatpakrepo", _("ElementoryOS's Apps")], + ["flathub", "https://dl.flathub.org/repo/flathub.flatpakrepo", _("The biggest repository of Flatpaks")], + ["flathub-beta", "https://flathub.org/beta-repo/flathub-beta.flatpakrepo", _("The beta branch of the biggest repository of Flatpaks")], + ["fedora", "oci+https://registry.fedoraproject.org", _("Flatpaks packaged by Fedora Linux")], + ["gnome-nightly", "https://nightly.gnome.org/gnome-nightly.flatpakrepo", _("Beta GNOME Apps and Runtimes")], + ["kdeapps", "https://distribute.kde.org/kdeapps.flatpakrepo", _("Beta KDE Apps and Runtimes")], + ] + + def on_add_response(self, _dialog, response_id, _function): + if response_id == "cancel": + return + + install_type = "--user" + if not self.add_as_user: + install_type = "--system" + + self.name_to_add = self.name_to_add.strip() + self.url_to_add = self.url_to_add.strip() + + command = ['flatpak-spawn', '--host', 'flatpak', 'remote-add', '--if-not-exists', self.name_to_add, self.url_to_add, install_type] + try: + subprocess.run(command, capture_output=True, check=True) + except Exception as e: + self.toast_overlay.add_toast(Adw.Toast.new(_("Could not add {}").format(self.name_to_add))) + print(e) + self.generate_list() + self.close() + + def add_handler(self, _widget, name="", link=""): + dialog = Adw.MessageDialog.new(self, _("Add Flatpak Remote")) + dialog.set_close_response("cancel") + dialog.add_response("cancel", _("Cancel")) + dialog.add_response("continue", _("Add")) + dialog.set_response_enabled("continue", False) + dialog.set_response_appearance("continue", Adw.ResponseAppearance.SUGGESTED) + + def name_update(widget): + is_enabled = True + self.name_to_add = widget.get_text() + name_pattern = re.compile(r'^[a-zA-Z\-]+$') + if not name_pattern.match(self.name_to_add): + is_enabled = False + + if is_enabled: + widget.remove_css_class("error") + else: + widget.add_css_class("error") + + if len(self.name_to_add) == 0: + is_enabled = False + + confirm_enabler(is_enabled) + + def url_update(widget): + is_enabled = True + self.url_to_add = widget.get_text() + url_pattern = re.compile(r'^[a-zA-Z0-9\-._~:/?#[\]@!$&\'()*+,;=]+$') + if not url_pattern.match(self.url_to_add): + is_enabled = False + + if is_enabled: + widget.remove_css_class("error") + else: + widget.add_css_class("error") + + if len(self.url_to_add) == 0: + is_enabled = False + + confirm_enabler(is_enabled) + + def confirm_enabler(is_enabled): + if len(self.name_to_add) == 0 or len(self.url_to_add) == 0: + is_enabled = False + dialog.set_response_enabled("continue", is_enabled) + + def set_user(widget): + self.add_as_user = widget.get_active() + + self.name_to_add = "" + self.url_to_add = "" + self.add_as_user = True + + info_box = Gtk.Box(orientation="vertical") + entry_list = Gtk.ListBox(selection_mode="none", margin_bottom=12) + entry_list.add_css_class("boxed-list") + + name_entry = Adw.EntryRow(title=_("Name")) + name_entry.set_text(name) + name_entry.connect("changed", name_update) + + url_entry = Adw.EntryRow(title=_("URL")) + url_entry.set_text(link) + url_entry.connect("changed", url_update) + + entry_list.append(name_entry) + entry_list.append(url_entry) + info_box.append(entry_list) + + install_type_list = Gtk.ListBox(selection_mode="none") + install_type_list.add_css_class("boxed-list") + + user_row = Adw.ActionRow(title=_("User"), subtitle=_("Remote will be available to only you")) + user_check = Gtk.CheckButton(active=True) + user_check.connect("toggled", set_user) + user_row.add_prefix(user_check) + user_row.set_activatable_widget(user_check) + + system_row = Adw.ActionRow(title=_("System"), subtitle=_("Remote will be available to every user on the system")) + system_check = Gtk.CheckButton() + system_row.add_prefix(system_check) + system_check.set_group(user_check) + system_row.set_activatable_widget(system_check) + + install_type_list.append(user_row) + install_type_list.append(system_row) + + info_box.append(install_type_list) + + dialog.set_extra_child(info_box) + dialog.connect("response", self.on_add_response, dialog.choose_finish) + Gtk.Window.present(dialog) + + if name != "": + name_update(name_entry) + if link != "": + url_update(url_entry) + + def generate_list(self): + self.host_remotes = self.my_utils.getHostRemotes() + self.list_of_remotes.remove_all() + self.custom_list.remove_all() + host_remotes_names = [] + for i in range(len(self.host_remotes)): + host_remotes_names.append(self.host_remotes[i][0]) + + for i in range(len(self.remotes)): + remote_row = Adw.ActionRow(activatable=True) + remote_row.set_title(self.remotes[i][0]) + remote_row.set_subtitle(self.remotes[i][2]) + image = Gtk.Image.new_from_icon_name("right-large-symbolic") + remote_row.add_suffix(image) + remote_row.connect("activated", self.add_handler, self.remotes[i][0], self.remotes[i][1]) + if self.remotes[i][0] not in host_remotes_names: + self.list_of_remotes.append(remote_row) + + image2 = Gtk.Image.new_from_icon_name("right-large-symbolic") + custom_remote = Adw.ActionRow(activatable=True) + custom_remote.set_title(_("Add a Custom Remote")) + custom_remote.add_suffix(image2) + custom_remote.connect("activated", self.add_handler) + self.custom_list.append(custom_remote) + + if not self.list_of_remotes.get_row_at_index(0): + self.list_of_remotes.set_visible(False) + + def __init__(self, parent_window, **kwargs): + super().__init__(**kwargs) + self.my_utils = myUtils(self) + + self.connect("close-request", lambda *_: parent_window.generate_list()) + self.set_modal(True) + self.set_transient_for(parent_window) + self.generate_list() \ No newline at end of file diff --git a/src/remotes.py b/src/remotes.py index da3e40d..15386e9 100644 --- a/src/remotes.py +++ b/src/remotes.py @@ -1,5 +1,6 @@ from gi.repository import Gtk, Adw, GLib, Gdk, Gio from .common import myUtils +from .popular_remotes_window import PopularRemotesWindow import subprocess import re @@ -22,117 +23,6 @@ class RemotesWindow(Adw.Window): data.append(row) return data - def on_add_response(self, _dialog, response_id, _function): - if response_id == "cancel": - return - - install_type = "--user" - if not self.add_as_user: - install_type = "--system" - - self.name_to_add = self.name_to_add.strip() - self.url_to_add = self.url_to_add.strip() - - command = ['flatpak-spawn', '--host', 'flatpak', 'remote-add', '--if-not-exists', self.name_to_add, self.url_to_add, install_type] - try: - subprocess.run(command, capture_output=True, check=True) - except Exception as e: - self.make_toast(_("Could not add {}").format(self.name_to_add)) - print(e) - self.generate_list() - - def add_handler(self, _widget): - dialog = Adw.MessageDialog.new(self, _("Add Flatpak Remote")) - dialog.set_close_response("cancel") - dialog.add_response("cancel", _("Cancel")) - dialog.add_response("continue", _("Add")) - dialog.set_response_enabled("continue", False) - dialog.set_response_appearance("continue", Adw.ResponseAppearance.SUGGESTED) - - def name_update(widget): - is_enabled = True - self.name_to_add = widget.get_text() - name_pattern = re.compile(r'^[a-zA-Z\-]+$') - if not name_pattern.match(self.name_to_add): - is_enabled = False - - if is_enabled: - widget.remove_css_class("error") - else: - widget.add_css_class("error") - - if len(self.name_to_add) == 0: - is_enabled = False - - confirm_enabler(is_enabled) - - def url_update(widget): - is_enabled = True - self.url_to_add = widget.get_text() - url_pattern = re.compile(r'^[a-zA-Z0-9\-._~:/?#[\]@!$&\'()*+,;=]+$') - if not url_pattern.match(self.url_to_add): - is_enabled = False - - if is_enabled: - widget.remove_css_class("error") - else: - widget.add_css_class("error") - - if len(self.url_to_add) == 0: - is_enabled = False - - confirm_enabler(is_enabled) - - def confirm_enabler(is_enabled): - if len(self.name_to_add) == 0 or len(self.url_to_add) == 0: - is_enabled = False - dialog.set_response_enabled("continue", is_enabled) - - def set_user(widget): - self.add_as_user = widget.get_active() - - self.name_to_add = "" - self.url_to_add = "" - self.add_as_user = True - - info_box = Gtk.Box(orientation="vertical") - entry_list = Gtk.ListBox(selection_mode="none", margin_bottom=12) - entry_list.add_css_class("boxed-list") - - name_entry = Adw.EntryRow(title=_("Name")) - name_entry.connect("changed", name_update) - - url_entry = Adw.EntryRow(title=_("URL")) - url_entry.connect("changed", url_update) - - entry_list.append(name_entry) - entry_list.append(url_entry) - info_box.append(entry_list) - - install_type_list = Gtk.ListBox(selection_mode="none") - install_type_list.add_css_class("boxed-list") - - user_row = Adw.ActionRow(title=_("User"), subtitle=_("Remote will be available to only you")) - user_check = Gtk.CheckButton(active=True) - user_check.connect("toggled", set_user) - user_row.add_prefix(user_check) - user_row.set_activatable_widget(user_check) - - system_row = Adw.ActionRow(title=_("System"), subtitle=_("Remote will be available to every user on the system")) - system_check = Gtk.CheckButton() - system_row.add_prefix(system_check) - system_check.set_group(user_check) - system_row.set_activatable_widget(system_check) - - install_type_list.append(user_row) - install_type_list.append(system_row) - - info_box.append(install_type_list) - - dialog.set_extra_child(info_box) - dialog.connect("response", self.on_add_response, dialog.choose_finish) - Gtk.Window.present(dialog) - def remove_on_response(self, _dialog, response_id, _function, index): if response_id == "cancel": return @@ -195,6 +85,9 @@ class RemotesWindow(Adw.Window): remove_button.connect("clicked", self.remove_handler, i) remote_row.add_suffix(copy_button) remote_row.add_suffix(remove_button) + + def showPopularRemotes(self, widget): + PopularRemotesWindow(self).present() def __init__(self, main_window, **kwargs): super().__init__(**kwargs) @@ -231,7 +124,7 @@ class RemotesWindow(Adw.Window): self.remotes_list.add_css_class("boxed-list") self.app_window = main_window - self.add_button.connect("clicked", self.add_handler) + self.add_button.connect("clicked", self.showPopularRemotes) # Window Stuffs self.set_title(self.window_title) diff --git a/src/right-large-symbolic.svg b/src/right-large-symbolic.svg new file mode 100644 index 0000000..c8d83ea --- /dev/null +++ b/src/right-large-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/warehouse.gresource.xml b/src/warehouse.gresource.xml index 0f6392e..5427eaf 100644 --- a/src/warehouse.gresource.xml +++ b/src/warehouse.gresource.xml @@ -4,6 +4,7 @@ window.ui orphans.ui filter.ui + popular_remotes.ui gtk/help-overlay.ui @@ -17,5 +18,6 @@ paper-filled-symbolic.svg plus-large-symbolic.svg funnel-symbolic.svg + right-large-symbolic.svg