diff --git a/po/POTFILES.in b/po/POTFILES.in index eeffd344..dc217582 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,17 +15,18 @@ src/bz-app-size-dialog.blp src/bz-app-size-dialog.c src/bz-app-tile.blp src/bz-app-tile.c -src/bz-apps-page.blp -src/bz-apps-page.c src/bz-application-map-factory.c src/bz-application.c +src/bz-apps-page.blp +src/bz-apps-page.c src/bz-appstream-parser.c src/bz-async-texture.c src/bz-backend.c +src/bz-bundle-install-dialog.c src/bz-content-provider.c +src/bz-context-tile-callbacks.c src/bz-context-tile.blp src/bz-context-tile.c -src/bz-context-tile-callbacks.c src/bz-curated-view.blp src/bz-curated-view.c src/bz-data-graph.c @@ -38,23 +39,23 @@ src/bz-donations-dialog.c src/bz-download-worker.c src/bz-dynamic-list-view.c src/bz-entry-cache-manager.c -src/bz-entry-group.c src/bz-entry-group-util.c +src/bz-entry-group.c src/bz-entry-inspector.blp src/bz-entry-inspector.c src/bz-entry-selection-row.blp src/bz-entry-selection-row.c src/bz-entry.c src/bz-env.c -src/bz-error.c src/bz-error-dialog.blp src/bz-error-dialog.c -src/bz-favorite-button.c +src/bz-error.c src/bz-favorite-button.blp -src/bz-favorites-tile.c -src/bz-favorites-tile.blp -src/bz-favorites-page.c +src/bz-favorite-button.c src/bz-favorites-page.blp +src/bz-favorites-page.c +src/bz-favorites-tile.blp +src/bz-favorites-tile.c src/bz-featured-carousel.blp src/bz-featured-tile.blp src/bz-flathub-category-section.c @@ -101,15 +102,15 @@ src/bz-rich-app-tile.c src/bz-safety-calculator.c src/bz-safety-dialog.blp src/bz-safety-dialog.c -src/bz-screenshot.c src/bz-screenshot-page.blp +src/bz-screenshot.c src/bz-screenshots-carousel.blp src/bz-search-engine.c -src/bz-search-pill-list.c src/bz-search-filter-popover.blp src/bz-search-filter-popover.c src/bz-search-page.blp src/bz-search-page.c +src/bz-search-pill-list.c src/bz-section-view.blp src/bz-section-view.c src/bz-serializable.c @@ -126,14 +127,14 @@ src/bz-transaction-tile.c src/bz-transaction.c src/bz-updates-card.blp src/bz-updates-card.c -src/bz-user-data-page.c src/bz-user-data-page.blp -src/bz-user-data-tile.c +src/bz-user-data-page.c src/bz-user-data-tile.blp +src/bz-user-data-tile.c src/bz-window.blp src/bz-window.c src/bz-world-map.c src/bz-yaml-parser.c src/dl-worker.c -src/shortcuts-dialog.blp src/main.c +src/shortcuts-dialog.blp diff --git a/src/bazaar.gresource.xml b/src/bazaar.gresource.xml index ef4d6466..240f6629 100644 --- a/src/bazaar.gresource.xml +++ b/src/bazaar.gresource.xml @@ -18,19 +18,20 @@ bz-addon-tile.ui bz-addons-dialog.ui - bz-donations-dialog.ui bz-age-rating-dialog.ui + bz-all-apps-page.ui bz-app-size-dialog.ui bz-app-tile.ui - bz-all-apps-page.ui bz-apps-page.ui bz-appstream-description-render.ui - bz-curated-view.ui + bz-bundle-install-dialog.ui bz-category-tile.ui bz-context-tile.ui bz-curated-app-tile.ui + bz-curated-view.ui bz-decorated-screenshot.ui bz-developer-badge.ui + bz-donations-dialog.ui bz-entry-inspector.ui bz-entry-selection-row.ui bz-error-dialog.ui @@ -45,8 +46,8 @@ bz-hardware-support-dialog.ui bz-inspector.ui bz-install-controls.ui - bz-library-page.ui bz-installed-tile.ui + bz-library-page.ui bz-license-dialog.ui bz-login-page.ui bz-preferences-dialog.ui @@ -58,12 +59,12 @@ bz-safety-dialog.ui bz-screenshot-page.ui bz-screenshots-carousel.ui - bz-search-page.ui bz-search-filter-popover.ui + bz-search-page.ui bz-section-view.ui bz-stats-dialog.ui - bz-transaction-tile.ui bz-transaction-list-dialog.ui + bz-transaction-tile.ui bz-updates-card.ui bz-user-data-page.ui bz-user-data-tile.ui diff --git a/src/bz-application.c b/src/bz-application.c index 61c1b659..1647393d 100644 --- a/src/bz-application.c +++ b/src/bz-application.c @@ -33,6 +33,7 @@ #include "bz-appstream-parser.h" #include "bz-auth-state.h" #include "bz-backend-notification.h" +#include "bz-bundle-install-dialog.h" #include "bz-content-provider.h" #include "bz-donations-dialog.h" #include "bz-download-worker.h" @@ -1246,7 +1247,17 @@ enumerate_disk_entries_fiber (GWeakRef *wr) future = g_ptr_array_index (futures, i); value = dex_future_get_value (future, &local_error); if (value != NULL) - g_ptr_array_add (entries, g_value_dup_object (value)); + { + g_autoptr (BzEntry) entry = NULL; + + entry = g_value_dup_object (value); + if (BZ_IS_FLATPAK_ENTRY (entry) && + bz_flatpak_entry_get_bundle_path (BZ_FLATPAK_ENTRY (entry)) != NULL) + /* refrain from restoring bundle entries */ + continue; + + g_ptr_array_add (entries, g_steal_pointer (&entry)); + } else { g_warning ("Unable to retrieve cached entry: %s", local_error->message); @@ -1363,6 +1374,17 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) kind = bz_backend_notification_get_kind (notif); switch (kind) { + case BZ_BACKEND_NOTIFICATION_KIND_PRESENT_ID: + { + const char *id = NULL; + + id = bz_backend_notification_get_generic_id (notif); + if (id == NULL) + break; + + open_generic_id (self, id); + } + break; case BZ_BACKEND_NOTIFICATION_KIND_ERROR: { const char *error = NULL; @@ -1542,11 +1564,13 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) } break; case BZ_BACKEND_NOTIFICATION_KIND_ERROR: - case BZ_BACKEND_NOTIFICATION_KIND_TELL_INCOMING: - case BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY: - case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START: - case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH: case BZ_BACKEND_NOTIFICATION_KIND_EXTERNAL_CHANGE: + case BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES: + case BZ_BACKEND_NOTIFICATION_KIND_PRESENT_ID: + case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH: + case BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START: + case BZ_BACKEND_NOTIFICATION_KIND_REPLACE_ENTRY: + case BZ_BACKEND_NOTIFICATION_KIND_TELL_INCOMING: default: g_assert_not_reached (); }; @@ -1564,8 +1588,10 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) } } break; + case BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES: case BZ_BACKEND_NOTIFICATION_KIND_EXTERNAL_CHANGE: { + g_autoptr (GListModel) repos = NULL; g_autoptr (GHashTable) installed_set = NULL; g_autoptr (GPtrArray) diff_reads = NULL; GHashTableIter old_iter = { 0 }; @@ -1574,6 +1600,18 @@ respond_to_flatpak_fiber (RespondToFlatpakData *data) bz_state_info_set_background_task_label (self->state, _ ("Refreshing…")); + repos = dex_await_object ( + bz_backend_list_repositories (BZ_BACKEND (self->flatpak), NULL), + &local_error); + + if (repos != NULL) + bz_state_info_set_repositories (self->state, repos); + else + { + g_warning ("Failed to enumerate repositories: %s", local_error->message); + g_clear_error (&local_error); + } + installed_set = dex_await_boxed ( bz_backend_retrieve_install_ids ( BZ_BACKEND (self->flatpak), NULL), @@ -1763,7 +1801,6 @@ open_flatpakref_fiber (OpenFlatpakrefData *data) GFile *file = data->file; g_autoptr (GError) local_error = NULL; g_autoptr (DexFuture) future = NULL; - GtkWindow *window = NULL; const GValue *value = NULL; bz_weak_get_or_return_reject (self, data->self); @@ -1772,25 +1809,47 @@ open_flatpakref_fiber (OpenFlatpakrefData *data) future = bz_backend_load_local_package (BZ_BACKEND (self->flatpak), file, NULL); dex_await (dex_ref (future), NULL); - window = gtk_application_get_active_window (GTK_APPLICATION (self)); - if (window == NULL) - window = new_window (self); - value = dex_future_get_value (future, &local_error); - if (value != NULL) + if (value == NULL) { - if (G_VALUE_HOLDS_OBJECT (value)) - { - BzEntry *entry = NULL; + GtkWindow *window = NULL; - entry = g_value_get_object (value); - bz_window_show_entry (BZ_WINDOW (window), entry); - } - else - open_generic_id (self, g_value_get_string (value)); + window = gtk_application_get_active_window (GTK_APPLICATION (self)); + if (window == NULL) + window = new_window (self); + + bz_show_error_for_widget ( + GTK_WIDGET (window), + _ ("Failed to open file"), + local_error->message); + + return dex_future_new_for_error (g_steal_pointer (&local_error)); + } + + if (G_VALUE_HOLDS_OBJECT (value)) + { + GtkWindow *window = NULL; + BzEntry *entry = NULL; + BzBundleInstallDialog *install_ui = NULL; + AdwDialog *dialog = NULL; + + window = gtk_application_get_active_window (GTK_APPLICATION (self)); + if (window == NULL) + window = new_window (self); + + entry = g_value_get_object (value); + install_ui = g_object_new ( + BZ_TYPE_BUNDLE_INSTALL_DIALOG, + "state", self->state, + "entry", entry, + NULL); + + dialog = adw_dialog_new (); + adw_dialog_set_follows_content_size (dialog, TRUE); + adw_dialog_set_child (dialog, GTK_WIDGET (install_ui)); + + adw_dialog_present (dialog, GTK_WIDGET (window)); } - else - bz_show_error_for_widget (GTK_WIDGET (window), _ ("Failed to open .flatpakref"), local_error->message); return dex_future_new_true (); } @@ -2003,9 +2062,7 @@ ensure_group_and_add (BzApplication *self, group = g_hash_table_lookup (self->ids_to_groups, id); if (group != NULL) - { - bz_entry_group_add (group, entry, eol_runtime, ignore_eol); - } + bz_entry_group_add (group, entry, eol_runtime, ignore_eol); else { g_autoptr (BzEntryGroup) new_group = NULL; @@ -3201,11 +3258,46 @@ static void open_flatpakref_take (BzApplication *self, GFile *file) { + g_autoptr (GError) local_error = NULL; + gboolean result = FALSE; g_autofree char *path = NULL; g_autoptr (OpenFlatpakrefData) data = NULL; path = g_file_get_path (file); - g_info ("Loading flatpakref at %s...", path); + if (path != NULL) + /* We must do this synchronously so we don't lose access to a portal file */ + { + g_autofree char *basename = NULL; + g_autofree char *module_dir = NULL; + g_autofree char *staging = NULL; + g_autofree char *dest = NULL; + g_autoptr (GFile) copied = NULL; + + basename = g_file_get_basename (file); + module_dir = bz_dup_module_dir (); + staging = g_build_filename (module_dir, "bundle-staging", NULL); + g_mkdir_with_parents (staging, 0755); + dest = g_build_filename (staging, basename, NULL); + copied = g_file_new_for_path (dest); + + result = g_file_copy ( + file, copied, + G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, + NULL, NULL, NULL, + &local_error); + if (result) + { + g_clear_object (&file); + file = g_steal_pointer (&copied); + } + else + { + g_warning ("Failed to copy bundle to %s : %s", + dest, local_error->message); + g_clear_error (&local_error); + g_clear_object (&copied); + } + } data = open_flatpakref_data_new (); data->self = bz_track_weak (self); diff --git a/src/bz-backend-notification.txt b/src/bz-backend-notification.txt index 3816118f..50b32a77 100644 --- a/src/bz-backend-notification.txt +++ b/src/bz-backend-notification.txt @@ -4,7 +4,7 @@ parent-prefix=g parent-name=object author=AUTOGEN -enum=bz backend_notification_kind error tell_incoming replace_entry remote_sync_start remote_sync_finish install_done update_done remove_done external_change +enum=bz backend_notification_kind error tell_incoming replace_entry invalidate_remotes remote_sync_start remote_sync_finish install_done update_done remove_done external_change present_id include="bz-entry.h" @@ -14,4 +14,5 @@ property=n_incoming int G_TYPE_INT int property=entry BzEntry BZ_TYPE_ENTRY object property=version char G_TYPE_STRING string property=remote_name char G_TYPE_STRING string +property=generic_id char G_TYPE_STRING string property=unique_id char G_TYPE_STRING string diff --git a/src/bz-bundle-install-dialog.blp b/src/bz-bundle-install-dialog.blp new file mode 100644 index 00000000..e8ac79c7 --- /dev/null +++ b/src/bz-bundle-install-dialog.blp @@ -0,0 +1,51 @@ +using Gtk 4.0; +using Adw 1; + +template $BzBundleInstallDialog: Adw.BreakpointBin { + width-request: 360; + height-request: 450; + child: Adw.ToolbarView { + bottom-bar-style: raised_border; + + [top] + Adw.HeaderBar { + title-widget: Label { + label: _("Bundle Installation"); + }; + } + + content: Box { + orientation: vertical; + spacing: 5; + + Image { + paintable: bind template.entry as <$BzEntry>.icon-paintable; + pixel-size: 128; + } + Label { + label: bind template.entry as <$BzEntry>.title; + wrap: true; + } + Label { + label: bind template.entry as <$BzEntry>.developer; + wrap: true; + } + Label { + label: bind template.entry as <$BzEntry>.description; + wrap: true; + } + + Button install_btn { + styles [ + "suggested-action", + ] + halign: center; + label: _("Install Bundle"); + clicked => $install_cb(template); + } + + Label status_lbl { + } + }; + }; +} diff --git a/src/bz-bundle-install-dialog.c b/src/bz-bundle-install-dialog.c new file mode 100644 index 00000000..18df1d35 --- /dev/null +++ b/src/bz-bundle-install-dialog.c @@ -0,0 +1,260 @@ +/* bz-bundle-install-dialog.c + * + * Copyright 2026 Eva M + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "bz-bundle-install-dialog.h" +#include "bz-env.h" +#include "bz-template-callbacks.h" +#include "bz-util.h" + +struct _BzBundleInstallDialog +{ + AdwBreakpointBin parent_instance; + + BzStateInfo *state; + BzEntry *entry; + + GtkButton *install_btn; + GtkLabel *status_lbl; +}; + +G_DEFINE_FINAL_TYPE (BzBundleInstallDialog, bz_bundle_install_dialog, ADW_TYPE_BREAKPOINT_BIN); + +enum +{ + PROP_0, + + PROP_STATE, + PROP_ENTRY, + + LAST_PROP +}; +static GParamSpec *props[LAST_PROP] = { 0 }; + +static DexFuture * +install_fiber (GWeakRef *wr); + +static void +bz_bundle_install_dialog_dispose (GObject *object) +{ + BzBundleInstallDialog *self = BZ_BUNDLE_INSTALL_DIALOG (object); + + g_clear_pointer (&self->state, g_object_unref); + g_clear_pointer (&self->entry, g_object_unref); + + G_OBJECT_CLASS (bz_bundle_install_dialog_parent_class)->dispose (object); +} + +static void +bz_bundle_install_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + BzBundleInstallDialog *self = BZ_BUNDLE_INSTALL_DIALOG (object); + + switch (prop_id) + { + case PROP_STATE: + g_value_set_object (value, bz_bundle_install_dialog_get_state (self)); + break; + case PROP_ENTRY: + g_value_set_object (value, bz_bundle_install_dialog_get_entry (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +bz_bundle_install_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + BzBundleInstallDialog *self = BZ_BUNDLE_INSTALL_DIALOG (object); + + switch (prop_id) + { + case PROP_STATE: + bz_bundle_install_dialog_set_state (self, g_value_get_object (value)); + break; + case PROP_ENTRY: + bz_bundle_install_dialog_set_entry (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +install_cb (BzBundleInstallDialog *self, + GtkButton *button) +{ + if (self->entry == NULL || + self->state == NULL) + return; + + gtk_widget_set_sensitive (GTK_WIDGET (self->install_btn), FALSE); + dex_future_disown (dex_scheduler_spawn ( + dex_scheduler_get_default (), + bz_get_dex_stack_size (), + (DexFiberFunc) install_fiber, + bz_track_weak (self), + bz_weak_release)); +} + +static void +bz_bundle_install_dialog_class_init (BzBundleInstallDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->set_property = bz_bundle_install_dialog_set_property; + object_class->get_property = bz_bundle_install_dialog_get_property; + object_class->dispose = bz_bundle_install_dialog_dispose; + + props[PROP_STATE] = + g_param_spec_object ( + "state", + NULL, NULL, + BZ_TYPE_STATE_INFO, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + props[PROP_ENTRY] = + g_param_spec_object ( + "entry", + NULL, NULL, + BZ_TYPE_ENTRY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, LAST_PROP, props); + + gtk_widget_class_set_template_from_resource (widget_class, "/io/github/kolunmi/Bazaar/bz-bundle-install-dialog.ui"); + bz_widget_class_bind_all_util_callbacks (widget_class); + + gtk_widget_class_bind_template_child (widget_class, BzBundleInstallDialog, install_btn); + gtk_widget_class_bind_template_child (widget_class, BzBundleInstallDialog, status_lbl); + gtk_widget_class_bind_template_callback (widget_class, install_cb); +} + +static void +bz_bundle_install_dialog_init (BzBundleInstallDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +BzBundleInstallDialog * +bz_bundle_install_dialog_new (void) +{ + return g_object_new (BZ_TYPE_BUNDLE_INSTALL_DIALOG, NULL); +} + +BzStateInfo * +bz_bundle_install_dialog_get_state (BzBundleInstallDialog *self) +{ + g_return_val_if_fail (BZ_IS_BUNDLE_INSTALL_DIALOG (self), NULL); + return self->state; +} + +BzEntry * +bz_bundle_install_dialog_get_entry (BzBundleInstallDialog *self) +{ + g_return_val_if_fail (BZ_IS_BUNDLE_INSTALL_DIALOG (self), NULL); + return self->entry; +} + +void +bz_bundle_install_dialog_set_state (BzBundleInstallDialog *self, + BzStateInfo *state) +{ + g_return_if_fail (BZ_IS_BUNDLE_INSTALL_DIALOG (self)); + g_return_if_fail (state == NULL || BZ_IS_STATE_INFO (state)); + + if (state == self->state) + return; + + g_clear_pointer (&self->state, g_object_unref); + if (state != NULL) + self->state = g_object_ref (state); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_STATE]); +} + +void +bz_bundle_install_dialog_set_entry (BzBundleInstallDialog *self, + BzEntry *entry) +{ + g_return_if_fail (BZ_IS_BUNDLE_INSTALL_DIALOG (self)); + g_return_if_fail (entry == NULL || BZ_IS_ENTRY (entry)); + + if (entry == self->entry) + return; + + g_clear_pointer (&self->entry, g_object_unref); + if (entry != NULL) + self->entry = g_object_ref (entry); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ENTRY]); +} + +static DexFuture * +install_fiber (GWeakRef *wr) +{ + g_autoptr (BzBundleInstallDialog) self = NULL; + g_autoptr (GError) local_error = NULL; + g_autoptr (BzTransaction) transaction = NULL; + g_autoptr (BzTransactionManager) ts_manager = NULL; + + bz_weak_get_or_return_reject (self, wr); + + if (self->entry == NULL || + self->state == NULL) + return dex_future_new_false (); + + transaction = bz_transaction_new_full ( + &self->entry, 1, + NULL, 0, + NULL, 0); + + ts_manager = g_object_ref (bz_state_info_get_transaction_manager (self->state)); + + gtk_label_set_label (self->status_lbl, _ ("Installing")); + dex_await ( + bz_transaction_manager_add ( + ts_manager, transaction), + &local_error); + + if (local_error != NULL) + { + gtk_widget_add_css_class (GTK_WIDGET (self->status_lbl), "error"); + gtk_label_set_label (self->status_lbl, local_error->message); + } + else + { + gtk_widget_add_css_class (GTK_WIDGET (self->status_lbl), "success"); + gtk_label_set_label (self->status_lbl, _ ("Installed!")); + } + + return dex_future_new_true (); +} + +/* End of bz-bundle-install-dialog.c */ diff --git a/src/bz-bundle-install-dialog.h b/src/bz-bundle-install-dialog.h new file mode 100644 index 00000000..abb18984 --- /dev/null +++ b/src/bz-bundle-install-dialog.h @@ -0,0 +1,52 @@ +/* bz-bundle-install-dialog.h + * + * Copyright 2026 Eva M + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "bz-entry.h" +#include "bz-state-info.h" + +G_BEGIN_DECLS + +#define BZ_TYPE_BUNDLE_INSTALL_DIALOG (bz_bundle_install_dialog_get_type ()) +G_DECLARE_FINAL_TYPE (BzBundleInstallDialog, bz_bundle_install_dialog, BZ, BUNDLE_INSTALL_DIALOG, AdwBreakpointBin) + +BzBundleInstallDialog * +bz_bundle_install_dialog_new (void); + +BzStateInfo * +bz_bundle_install_dialog_get_state (BzBundleInstallDialog *self); + +BzEntry * +bz_bundle_install_dialog_get_entry (BzBundleInstallDialog *self); + +void +bz_bundle_install_dialog_set_state (BzBundleInstallDialog *self, + BzStateInfo *state); + +void +bz_bundle_install_dialog_set_entry (BzBundleInstallDialog *self, + BzEntry *entry); + +G_END_DECLS + +/* End of bz-bundle-install-dialog.h */ diff --git a/src/bz-entry-group.c b/src/bz-entry-group.c index 1e939db6..8700c543 100644 --- a/src/bz-entry-group.c +++ b/src/bz-entry-group.c @@ -23,18 +23,29 @@ #include "bz-entry-group.h" #include "bz-env.h" -#include "bz-flatpak-entry.h" #include "bz-io.h" #include "bz-util.h" +typedef enum +{ + ENTRY_INSTALLABLE = 1 << 0, + ENTRY_INSTALLABLE_AVAILABLE = 1 << 1, + ENTRY_UPDATABLE = 1 << 2, + ENTRY_UPDATABLE_AVAILABLE = 1 << 3, + ENTRY_REMOVABLE = 1 << 4, + ENTRY_REMOVABLE_AVAILABLE = 1 << 5, +} EntryStateFlags; + struct _BzEntryGroup { GObject parent_instance; BzApplicationMapFactory *factory; - GtkStringList *unique_ids; - GtkStringList *installed_versions; + GtkStringList *unique_ids; + GtkStringList *installed_versions; + GArray *state_flags; + char *id; char *title; char *developer; @@ -142,8 +153,11 @@ bz_entry_group_dispose (GObject *object) dex_clear (&self->user_data_size_future); dex_clear (&self->reap_user_data_future); g_clear_object (&self->factory); + g_clear_object (&self->unique_ids); g_clear_object (&self->installed_versions); + g_clear_pointer (&self->state_flags, g_array_unref); + g_clear_pointer (&self->id, g_free); g_clear_pointer (&self->title, g_free); g_clear_pointer (&self->developer, g_free); @@ -491,7 +505,9 @@ bz_entry_group_init (BzEntryGroup *self) { self->unique_ids = gtk_string_list_new (NULL); self->installed_versions = gtk_string_list_new (NULL); - self->max_usefulness = -1; + self->state_flags = g_array_new (FALSE, TRUE, sizeof (gint32)); + + self->max_usefulness = -1; g_weak_ref_init (&self->ui_entry, NULL); self->standalone_ui_entry = NULL; g_mutex_init (&self->mutex); @@ -910,6 +926,7 @@ bz_entry_group_add (BzEntryGroup *self, gboolean is_searchable = FALSE; AsContentRating *content_rating = NULL; gboolean is_addon = FALSE; + gint32 state_flags = 0; g_return_if_fail (BZ_IS_ENTRY_GROUP (self)); g_return_if_fail (BZ_IS_ENTRY (entry)); @@ -1131,39 +1148,60 @@ bz_entry_group_add (BzEntryGroup *self, } } - if (existing == G_MAXUINT) + if (existing != G_MAXUINT) { - if (bz_entry_is_installed (entry)) + gint32 previous_state_flags = 0; + + /* revert the old state if we are replacing */ + + previous_state_flags = g_array_index (self->state_flags, gint32, existing); + if (previous_state_flags & ENTRY_INSTALLABLE) + self->installable--; + if (previous_state_flags & ENTRY_INSTALLABLE_AVAILABLE) + self->installable_available--; + if (previous_state_flags & ENTRY_UPDATABLE) + self->updatable--; + if (previous_state_flags & ENTRY_UPDATABLE_AVAILABLE) + self->updatable_available--; + if (previous_state_flags & ENTRY_REMOVABLE) + self->removable--; + if (previous_state_flags & ENTRY_REMOVABLE_AVAILABLE) + self->removable_available--; + } + + if (bz_entry_is_installed (entry)) + { + self->removable++; + state_flags |= ENTRY_REMOVABLE; + if (!bz_entry_is_holding (entry)) { - self->removable++; - if (!bz_entry_is_holding (entry)) - { - self->removable_available++; - g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE_AND_AVAILABLE]); - } - g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE]); + self->removable_available++; + state_flags |= ENTRY_REMOVABLE_AVAILABLE; + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE_AND_AVAILABLE]); } - else + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE]); + } + else + { + if (bz_entry_is_reinstallable (entry)) { - gboolean is_installed_ref = FALSE; - - if (BZ_IS_FLATPAK_ENTRY (entry)) - is_installed_ref = bz_flatpak_entry_is_installed_ref (BZ_FLATPAK_ENTRY (entry)); - - if (!is_installed_ref) + self->installable++; + state_flags |= ENTRY_INSTALLABLE; + if (!bz_entry_is_holding (entry)) { - self->installable++; - if (!bz_entry_is_holding (entry)) - { - self->installable_available++; - g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE_AND_AVAILABLE]); - } - g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE]); + self->installable_available++; + state_flags |= ENTRY_INSTALLABLE_AVAILABLE; + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE_AND_AVAILABLE]); } + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE]); } } + if (existing != G_MAXUINT) + g_array_index (self->state_flags, gint32, existing) = state_flags; + else + g_array_append_val (self->state_flags, state_flags); - if (!is_addon && is_searchable && !self->searchable) + if (!is_addon && is_searchable) self->searchable = TRUE; } @@ -1206,38 +1244,56 @@ installed_changed (BzEntryGroup *self, BzEntry *entry) { g_autoptr (GMutexLocker) locker = NULL; - gboolean is_installed_ref = FALSE; + gboolean reinstallable = FALSE; const char *unique_id = NULL; const char *version = NULL; guint index = 0; + gint32 state_flags = 0; locker = g_mutex_locker_new (&self->mutex); - if (BZ_IS_FLATPAK_ENTRY (entry)) - is_installed_ref = bz_flatpak_entry_is_installed_ref (BZ_FLATPAK_ENTRY (entry)); - - unique_id = bz_entry_get_unique_id (entry); - version = bz_entry_get_installed_version (entry); - index = gtk_string_list_find (self->unique_ids, unique_id); - - if (index != G_MAXUINT) - { - gtk_string_list_splice (self->installed_versions, index, 1, - (const char *const[]){ - version != NULL ? version : "", - NULL }); - } + reinstallable = bz_entry_is_reinstallable (entry); + unique_id = bz_entry_get_unique_id (entry); + version = bz_entry_get_installed_version (entry); + index = gtk_string_list_find (self->unique_ids, unique_id); + if (index == G_MAXUINT) + return; + state_flags = g_array_index (self->state_flags, gint32, index); + gtk_string_list_splice (self->installed_versions, index, 1, + (const char *const[]){ + version != NULL ? version : "", + NULL }); g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLED_VERSIONS]); if (bz_entry_is_installed (entry)) { - self->installable--; - self->removable++; + if (state_flags & ENTRY_INSTALLABLE) + { + self->installable--; + state_flags &= ~ENTRY_INSTALLABLE; + } + + if (!(state_flags & ENTRY_REMOVABLE)) + { + self->removable++; + state_flags |= ENTRY_REMOVABLE; + } + if (!bz_entry_is_holding (entry)) { - self->installable_available--; - self->removable_available++; + if (state_flags & ENTRY_INSTALLABLE_AVAILABLE) + { + self->installable_available--; + state_flags &= ~ENTRY_INSTALLABLE_AVAILABLE; + } + + if (!(state_flags & ENTRY_REMOVABLE_AVAILABLE)) + { + self->removable_available++; + state_flags |= ENTRY_REMOVABLE_AVAILABLE; + } + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE_AND_AVAILABLE]); g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE_AND_AVAILABLE]); } @@ -1246,25 +1302,48 @@ installed_changed (BzEntryGroup *self, } else { - self->removable--; - if (!is_installed_ref) - self->installable++; + if (state_flags & ENTRY_REMOVABLE) + { + self->removable--; + state_flags &= ~ENTRY_REMOVABLE; + } + + if (reinstallable) + { + if (!(state_flags & ENTRY_INSTALLABLE)) + { + self->installable++; + state_flags |= ENTRY_INSTALLABLE; + } + } if (!bz_entry_is_holding (entry)) { - self->removable_available--; - if (!is_installed_ref) - self->installable_available++; + if (state_flags & ENTRY_REMOVABLE_AVAILABLE) + { + self->removable_available--; + state_flags &= ~ENTRY_REMOVABLE_AVAILABLE; + } + + if (reinstallable) + { + if (!(state_flags & ENTRY_INSTALLABLE_AVAILABLE)) + { + self->installable_available++; + state_flags |= ENTRY_INSTALLABLE_AVAILABLE; + } + } g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE_AND_AVAILABLE]); - if (!is_installed_ref) + if (reinstallable) g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE_AND_AVAILABLE]); } g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE]); - if (!is_installed_ref) + if (reinstallable) g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE]); } + g_array_index (self->state_flags, gint32, index) = state_flags; dex_clear (&self->user_data_size_future); self->user_data_size = 0; @@ -1277,23 +1356,60 @@ holding_changed (BzEntryGroup *self, BzEntry *entry) { g_autoptr (GMutexLocker) locker = NULL; + gboolean reinstallable = FALSE; + const char *unique_id = NULL; + guint index = 0; + gint32 state_flags = 0; locker = g_mutex_locker_new (&self->mutex); + reinstallable = bz_entry_is_reinstallable (entry); + + unique_id = bz_entry_get_unique_id (entry); + index = gtk_string_list_find (self->unique_ids, unique_id); + if (index == G_MAXUINT) + return; + state_flags = g_array_index (self->state_flags, gint32, index); + if (bz_entry_is_holding (entry)) { if (bz_entry_is_installed (entry)) - self->removable_available--; + { + if (state_flags & ENTRY_REMOVABLE_AVAILABLE) + { + self->removable_available--; + state_flags &= ~ENTRY_REMOVABLE_AVAILABLE; + } + } else - self->installable_available--; + { + if (state_flags & ENTRY_INSTALLABLE_AVAILABLE) + { + self->installable_available--; + state_flags &= ~ENTRY_INSTALLABLE_AVAILABLE; + } + } } else { if (bz_entry_is_installed (entry)) - self->removable_available++; - else - self->installable_available++; + { + if (!(state_flags & ENTRY_REMOVABLE_AVAILABLE)) + { + self->removable_available++; + state_flags |= ENTRY_REMOVABLE_AVAILABLE; + } + } + else if (reinstallable) + { + if (!(state_flags & ENTRY_INSTALLABLE_AVAILABLE)) + { + self->installable_available++; + state_flags |= ENTRY_INSTALLABLE_AVAILABLE; + } + } } + g_array_index (self->state_flags, gint32, index) = state_flags; g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REMOVABLE_AND_AVAILABLE]); g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INSTALLABLE_AND_AVAILABLE]); diff --git a/src/bz-entry.c b/src/bz-entry.c index c389c03c..2ec4b7b6 100644 --- a/src/bz-entry.c +++ b/src/bz-entry.c @@ -74,6 +74,7 @@ typedef struct gint hold; gboolean installed; char *installed_version; + gboolean reinstallable; gboolean searchable; guint kinds; @@ -141,6 +142,7 @@ enum PROP_HOLDING, PROP_INSTALLED, PROP_INSTALLED_VERSION, + PROP_REINSTALLABLE, PROP_SEARCHABLE, PROP_KINDS, PROP_ADDONS, @@ -273,6 +275,9 @@ bz_entry_get_property (GObject *object, case PROP_INSTALLED_VERSION: g_value_set_string (value, priv->installed_version); break; + case PROP_REINSTALLABLE: + g_value_set_boolean (value, priv->reinstallable); + break; case PROP_SEARCHABLE: g_value_set_boolean (value, priv->searchable); break; @@ -459,6 +464,9 @@ bz_entry_set_property (GObject *object, g_clear_pointer (&priv->installed_version, g_free); priv->installed_version = g_value_dup_string (value); break; + case PROP_REINSTALLABLE: + priv->reinstallable = g_value_get_boolean (value); + break; case PROP_SEARCHABLE: priv->searchable = g_value_get_boolean (value); break; @@ -693,6 +701,12 @@ bz_entry_class_init (BzEntryClass *klass) NULL, NULL, NULL, G_PARAM_READWRITE); + props[PROP_REINSTALLABLE] = + g_param_spec_boolean ( + "reinstallable", + NULL, NULL, TRUE, + G_PARAM_READWRITE); + props[PROP_SEARCHABLE] = g_param_spec_boolean ( "searchable", @@ -1050,6 +1064,7 @@ bz_entry_init (BzEntry *self) BzEntryPrivate *priv = bz_entry_get_instance_private (self); priv->hold = 0; + priv->reinstallable = TRUE; priv->searchable = TRUE; priv->favorites_count = -1; } @@ -1065,6 +1080,7 @@ bz_entry_real_serialize (BzSerializable *serializable, if (priv->installed_version != NULL) g_variant_builder_add (builder, "{sv}", "installed-version", g_variant_new_string (priv->installed_version)); g_variant_builder_add (builder, "{sv}", "kinds", g_variant_new_uint32 (priv->kinds)); + g_variant_builder_add (builder, "{sv}", "reinstallable", g_variant_new_boolean (priv->reinstallable)); g_variant_builder_add (builder, "{sv}", "searchable", g_variant_new_boolean (priv->searchable)); if (priv->addons != NULL) { @@ -1412,6 +1428,8 @@ bz_entry_real_deserialize (BzSerializable *serializable, priv->installed_version = g_variant_dup_string (value, NULL); else if (g_strcmp0 (key, "kinds") == 0) priv->kinds = g_variant_get_uint32 (value); + else if (g_strcmp0 (key, "reinstallable") == 0) + priv->reinstallable = g_variant_get_boolean (value); else if (g_strcmp0 (key, "searchable") == 0) priv->searchable = g_variant_get_boolean (value); else if (g_strcmp0 (key, "addons") == 0) @@ -1808,6 +1826,17 @@ bz_entry_is_of_kinds (BzEntry *self, return (priv->kinds & kinds) == kinds; } +gboolean +bz_entry_is_reinstallable (BzEntry *self) +{ + BzEntryPrivate *priv = NULL; + + g_return_val_if_fail (BZ_IS_ENTRY (self), TRUE); + priv = bz_entry_get_instance_private (self); + + return priv->reinstallable; +} + gboolean bz_entry_is_searchable (BzEntry *self) { diff --git a/src/bz-entry.h b/src/bz-entry.h index 93ab94f8..5d250750 100644 --- a/src/bz-entry.h +++ b/src/bz-entry.h @@ -24,8 +24,8 @@ #include #include -#include "bz-repository.h" #include "bz-category-flags.h" +#include "bz-repository.h" G_BEGIN_DECLS @@ -97,6 +97,9 @@ void bz_entry_set_installed (BzEntry *self, gboolean installed); +gboolean +bz_entry_is_reinstallable (BzEntry *self); + gboolean bz_entry_is_searchable (BzEntry *self); diff --git a/src/bz-flatpak-entry.c b/src/bz-flatpak-entry.c index f86bd57c..e36b3f5e 100644 --- a/src/bz-flatpak-entry.c +++ b/src/bz-flatpak-entry.c @@ -53,6 +53,7 @@ struct _BzFlatpakEntry char *application_command; char *runtime_name; char *addon_extension_of_ref; + char *bundle_path; BzResult *runtime_result; FlatpakRef *ref; @@ -74,6 +75,7 @@ enum PROP_USER, PROP_FLATPAK_NAME, PROP_IS_BUNDLE, + PROP_BUNDLE_PATH, PROP_FLATPAK_ID, PROP_FLATPAK_VERSION, PROP_APPLICATION_NAME, @@ -132,6 +134,9 @@ bz_flatpak_entry_get_property (GObject *object, case PROP_IS_BUNDLE: g_value_set_boolean (value, self->is_bundle); break; + case PROP_BUNDLE_PATH: + g_value_set_string (value, self->bundle_path); + break; case PROP_APPLICATION_RUNTIME: g_value_set_string (value, self->application_runtime); break; @@ -172,6 +177,7 @@ bz_flatpak_entry_set_property (GObject *object, case PROP_APPLICATION_COMMAND: case PROP_RUNTIME_NAME: case PROP_ADDON_OF_REF: + case PROP_BUNDLE_PATH: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -224,6 +230,12 @@ bz_flatpak_entry_class_init (BzFlatpakEntryClass *klass) FALSE, G_PARAM_READABLE); + props[PROP_BUNDLE_PATH] = + g_param_spec_string ( + "bundle-path", + NULL, NULL, NULL, + G_PARAM_READABLE); + props[PROP_APPLICATION_RUNTIME] = g_param_spec_string ( "application-runtime", @@ -270,7 +282,10 @@ bz_flatpak_entry_real_serialize (BzSerializable *serializable, BzFlatpakEntry *self = BZ_FLATPAK_ENTRY (serializable); g_variant_builder_add (builder, "{sv}", "user", g_variant_new_boolean (self->user)); + g_variant_builder_add (builder, "{sv}", "is-bundle", g_variant_new_boolean (self->is_bundle)); g_variant_builder_add (builder, "{sv}", "is-installed-ref", g_variant_new_boolean (self->is_installed_ref)); + if (self->bundle_path != NULL) + g_variant_builder_add (builder, "{sv}", "bundle-path", g_variant_new_string (self->bundle_path)); if (self->flatpak_name != NULL) g_variant_builder_add (builder, "{sv}", "flatpak-name", g_variant_new_string (self->flatpak_name)); if (self->flatpak_id != NULL) @@ -312,8 +327,12 @@ bz_flatpak_entry_real_deserialize (BzSerializable *serializable, if (g_strcmp0 (key, "user") == 0) self->user = g_variant_get_boolean (value); + else if (g_strcmp0 (key, "is-bundle") == 0) + self->is_bundle = g_variant_get_boolean (value); else if (g_strcmp0 (key, "is-installed-ref") == 0) self->is_installed_ref = g_variant_get_boolean (value); + else if (g_strcmp0 (key, "bundle-path") == 0) + self->bundle_path = g_variant_dup_string (value, NULL); else if (g_strcmp0 (key, "flatpak-name") == 0) self->flatpak_name = g_variant_dup_string (value, NULL); else if (g_strcmp0 (key, "flatpak-id") == 0) @@ -401,6 +420,7 @@ bz_flatpak_entry_new_for_ref (FlatpakRef *ref, g_autoptr (GdkPaintable) icon_paintable = NULL; g_autoptr (BzAppPermissions) permissions = NULL; gboolean searchable = FALSE; + gboolean reinstallable = FALSE; g_return_val_if_fail (FLATPAK_IS_REF (ref), NULL); g_return_val_if_fail (FLATPAK_IS_REMOTE_REF (ref) || FLATPAK_IS_BUNDLE_REF (ref) || FLATPAK_IS_INSTALLED_REF (ref), NULL); @@ -412,6 +432,15 @@ bz_flatpak_entry_new_for_ref (FlatpakRef *ref, self->is_installed_ref = FLATPAK_IS_INSTALLED_REF (ref); self->ref = g_object_ref (ref); + if (FLATPAK_IS_BUNDLE_REF (ref)) + { + GFile *file = NULL; + + file = flatpak_bundle_ref_get_file (FLATPAK_BUNDLE_REF (ref)); + if (file != NULL) + self->bundle_path = g_file_get_path (file); + } + key_file = g_key_file_new (); if (FLATPAK_IS_REMOTE_REF (ref)) bytes = flatpak_remote_ref_get_metadata (FLATPAK_REMOTE_REF (ref)); @@ -613,7 +642,8 @@ bz_flatpak_entry_new_for_ref (FlatpakRef *ref, if (permissions == NULL) return NULL; - searchable = !FLATPAK_IS_INSTALLED_REF (ref); + searchable = !FLATPAK_IS_INSTALLED_REF (ref); + reinstallable = !FLATPAK_IS_INSTALLED_REF (ref); g_object_set ( self, @@ -629,6 +659,7 @@ bz_flatpak_entry_new_for_ref (FlatpakRef *ref, "icon-paintable", icon_paintable, "permissions", permissions, "searchable", searchable, + "reinstallable", reinstallable, NULL); return g_steal_pointer (&self); @@ -642,7 +673,8 @@ bz_flatpak_ref_parts_format_unique (const char *origin, return g_strdup_printf ( "FLATPAK-%s::%s::%s", user ? "USER" : "SYSTEM", - origin, fmt); + origin != NULL ? origin : "bundle", + fmt); } char * @@ -656,8 +688,6 @@ bz_flatpak_ref_format_unique (FlatpakRef *ref, if (FLATPAK_IS_REMOTE_REF (ref)) origin = flatpak_remote_ref_get_remote_name (FLATPAK_REMOTE_REF (ref)); - else if (FLATPAK_IS_BUNDLE_REF (ref)) - origin = flatpak_bundle_ref_get_origin (FLATPAK_BUNDLE_REF (ref)); else if (FLATPAK_IS_INSTALLED_REF (ref)) origin = flatpak_installed_ref_get_origin (FLATPAK_INSTALLED_REF (ref)); @@ -759,6 +789,13 @@ bz_flatpak_entry_is_installed_ref (BzFlatpakEntry *self) return self->is_installed_ref; } +const char * +bz_flatpak_entry_get_bundle_path (BzFlatpakEntry *self) +{ + g_return_val_if_fail (BZ_IS_FLATPAK_ENTRY (self), NULL); + return self->bundle_path; +} + gboolean bz_flatpak_entry_launch (BzFlatpakEntry *self, BzFlatpakInstance *flatpak, @@ -814,6 +851,7 @@ clear_entry (BzFlatpakEntry *self) g_clear_pointer (&self->application_command, g_free); g_clear_pointer (&self->runtime_name, g_free); g_clear_pointer (&self->addon_extension_of_ref, g_free); + g_clear_pointer (&self->bundle_path, g_free); g_clear_object (&self->runtime_result); } diff --git a/src/bz-flatpak-entry.h b/src/bz-flatpak-entry.h index 5c85e1c2..41aacd48 100644 --- a/src/bz-flatpak-entry.h +++ b/src/bz-flatpak-entry.h @@ -63,6 +63,9 @@ bz_flatpak_entry_is_bundle (BzFlatpakEntry *self); gboolean bz_flatpak_entry_is_installed_ref (BzFlatpakEntry *self); +const char * +bz_flatpak_entry_get_bundle_path (BzFlatpakEntry *self); + const char * bz_flatpak_entry_get_addon_extension_of_ref (BzFlatpakEntry *self); diff --git a/src/bz-flatpak-instance.c b/src/bz-flatpak-instance.c index d4fc7fb8..353bb820 100644 --- a/src/bz-flatpak-instance.c +++ b/src/bz-flatpak-instance.c @@ -169,11 +169,19 @@ BZ_DEFINE_DATA ( static DexFuture * retrieve_refs_for_remote_fiber (RetrieveRefsForRemoteData *data); -static void -gather_refs_update_progress (const char *status, - guint progress, - gboolean estimating, - GatherRefsData *data); +static DexFuture * +retrieve_refs_for_enumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote); + +static DexFuture * +retrieve_refs_for_noenumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote); BZ_DEFINE_DATA ( transaction, @@ -256,6 +264,20 @@ static void transaction_progress_changed (FlatpakTransactionProgress *object, TransactionOperationData *data); +BZ_DEFINE_DATA ( + transaction_operation_done, + TransactionOperationDone, + { + TransactionData *parent; + FlatpakTransaction *transaction; + FlatpakTransactionOperation *operation; + }, + BZ_RELEASE_DATA (parent, transaction_data_unref); + BZ_RELEASE_DATA (transaction, g_object_unref); + BZ_RELEASE_DATA (operation, g_object_unref)); +static DexFuture * +transaction_operation_done_fiber (TransactionOperationDoneData *data); + static void installation_event (BzFlatpakInstance *self, GFile *file, @@ -949,11 +971,15 @@ ensure_flathub_fiber (EnsureFlathubData *data) static DexFuture * load_local_ref_fiber (LoadLocalRefData *data) { - GFile *file = data->file; - gboolean result = FALSE; - g_autoptr (GError) local_error = NULL; - g_autofree char *uri = NULL; - g_autofree char *path = NULL; + g_autoptr (BzFlatpakInstance) self = NULL; + GFile *file = data->file; + GCancellable *cancellable = data->cancellable; + gboolean result = FALSE; + g_autoptr (GError) local_error = NULL; + g_autofree char *uri = NULL; + g_autofree char *path = NULL; + + bz_weak_get_or_return_reject (self, data->self); uri = g_file_get_uri (file); path = g_file_get_path (file); @@ -1017,16 +1043,30 @@ load_local_ref_fiber (LoadLocalRefData *data) "Failed to load locate \"Name\" key in flatpakref '%s': %s", uri, local_error->message); + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_PRESENT_ID); + bz_backend_notification_set_generic_id (notif, name); + + send_notif_all (self, notif, TRUE); + } + return dex_future_new_take_string (g_steal_pointer (&name)); } else /* This is a bundle ref */ { - g_autoptr (FlatpakBundleRef) bref = NULL; - g_autoptr (BzFlatpakEntry) entry = NULL; - g_autoptr (GBytes) appstream_gz = NULL; - g_autoptr (GBytes) appstream = NULL; - g_autoptr (AsComponent) component = NULL; + g_autoptr (FlatpakBundleRef) bref = NULL; + const char *name = NULL; + const char *origin = NULL; + FlatpakInstallation *add_to_installation = NULL; + g_autoptr (FlatpakRemote) remote = NULL; + g_autoptr (BzFlatpakEntry) entry = NULL; + g_autoptr (GBytes) appstream_gz = NULL; + g_autoptr (GBytes) appstream = NULL; + g_autoptr (AsComponent) component = NULL; if (path == NULL) return dex_future_new_reject ( @@ -1044,6 +1084,126 @@ load_local_ref_fiber (LoadLocalRefData *data) path, local_error->message); + name = flatpak_ref_get_name (FLATPAK_REF (bref)); + origin = flatpak_bundle_ref_get_origin (bref); + + if (self->system != NULL) + add_to_installation = self->system; + else if (self->user != NULL) + add_to_installation = self->user; + + /* First check if we already should have the origin remote + installed */ + if (self->system != NULL) + { + g_autoptr (GPtrArray) remotes = NULL; + + remotes = flatpak_installation_list_remotes ( + self->system, NULL, NULL); + if (remotes != NULL) + { + for (guint i = 0; i < remotes->len; i++) + { + FlatpakRemote *existing = NULL; + const char *url = NULL; + + existing = g_ptr_array_index (remotes, i); + url = flatpak_remote_get_url (existing); + + if (url != NULL && + g_strcmp0 (url, origin) == 0) + { + remote = g_object_ref (existing); + add_to_installation = NULL; + break; + } + } + } + } + if (self->user != NULL) + { + g_autoptr (GPtrArray) remotes = NULL; + + remotes = flatpak_installation_list_remotes ( + self->user, NULL, NULL); + if (remotes != NULL) + { + for (guint i = 0; i < remotes->len; i++) + { + FlatpakRemote *existing = NULL; + const char *url = NULL; + + existing = g_ptr_array_index (remotes, i); + url = flatpak_remote_get_url (existing); + + if (url != NULL && + g_strcmp0 (url, origin) == 0) + { + remote = g_object_ref (existing); + add_to_installation = NULL; + break; + } + } + } + } + + if (add_to_installation != NULL) + { + g_autoptr (FlatpakRemote) config_remote = NULL; + g_autofree char *remote_name = NULL; + + /* Configure and sync the new remote */ + remote_name = g_strdup_printf ("%s-bazaar-origin", name); + + config_remote = flatpak_remote_new (remote_name); + flatpak_remote_set_url (config_remote, origin); + flatpak_remote_set_disabled (config_remote, FALSE); + flatpak_remote_set_noenumerate (config_remote, FALSE); + flatpak_remote_set_gpg_verify (config_remote, TRUE); + + result = flatpak_installation_add_remote ( + add_to_installation, config_remote, FALSE, cancellable, NULL); + if (result) + { + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES); + + send_notif_all (self, notif, TRUE); + } + + remote = flatpak_installation_get_remote_by_name ( + add_to_installation, remote_name, cancellable, NULL); + if (remote != NULL) + { + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START); + bz_backend_notification_set_remote_name (notif, remote_name); + + send_notif_all (self, notif, TRUE); + } + result = dex_await ( + retrieve_refs_for_enumerable_remote ( + self, cancellable, remote_name, add_to_installation, remote), + NULL); + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH); + bz_backend_notification_set_remote_name (notif, remote_name); + + send_notif_all (self, notif, TRUE); + } + } + } + } + appstream_gz = flatpak_bundle_ref_get_appstream (bref); if (appstream_gz != NULL) { @@ -1090,7 +1250,7 @@ load_local_ref_fiber (LoadLocalRefData *data) entry = bz_flatpak_entry_new_for_ref ( FLATPAK_REF (bref), - NULL, + remote, FALSE, component, NULL, @@ -1247,22 +1407,13 @@ retrieve_remote_refs_fiber (GatherRefsData *data) "%s", error_string->str); } -static void -gather_refs_update_progress (const char *status, - guint progress, - gboolean estimating, - GatherRefsData *data) -{ -} - static DexFuture * -retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, - const char *remote_name, - FlatpakInstallation *installation, - FlatpakRemote *remote) +retrieve_refs_for_enumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote) { - g_autoptr (BzFlatpakInstance) self = NULL; - GCancellable *cancellable = data->parent->cancellable; g_autoptr (GError) local_error = NULL; gboolean result = FALSE; g_autoptr (GFile) appstream_dir = NULL; @@ -1276,8 +1427,6 @@ retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, g_autoptr (GHashTable) component_hash = NULL; g_autoptr (GPtrArray) refs = NULL; - bz_weak_get_or_return_reject (self, data->parent->self); - g_debug ("Remote '%s' is enumerable, listing all remote refs", remote_name); result = flatpak_installation_update_remote_sync ( @@ -1296,10 +1445,7 @@ retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, result = flatpak_installation_update_appstream_full_sync ( installation, remote_name, - NULL, - (FlatpakProgressCallback) gather_refs_update_progress, - data, - NULL, + NULL, NULL, NULL, NULL, cancellable, &local_error); if (!result) @@ -1479,19 +1625,16 @@ retrieve_refs_for_enumerable_remote (RetrieveRefsForRemoteData *data, } static DexFuture * -retrieve_refs_for_noenumerable_remote (RetrieveRefsForRemoteData *data, - const char *remote_name, - FlatpakInstallation *installation, - FlatpakRemote *remote) +retrieve_refs_for_noenumerable_remote (BzFlatpakInstance *self, + GCancellable *cancellable, + const char *remote_name, + FlatpakInstallation *installation, + FlatpakRemote *remote) { - g_autoptr (BzFlatpakInstance) self = NULL; - GCancellable *cancellable = data->parent->cancellable; g_autoptr (GError) local_error = NULL; g_autoptr (GPtrArray) installed_apps = NULL; guint matched = 0; - bz_weak_get_or_return_reject (self, data->parent->self); - installed_apps = flatpak_installation_list_installed_refs_by_kind ( installation, FLATPAK_REF_KIND_APP, @@ -1628,10 +1771,12 @@ retrieve_refs_for_remote_fiber (RetrieveRefsForRemoteData *data) if (is_noenumerate) #endif ret = retrieve_refs_for_noenumerable_remote ( - data, remote_name, installation, remote); + self, data->parent->cancellable, + remote_name, installation, remote); else ret = retrieve_refs_for_enumerable_remote ( - data, remote_name, installation, remote); + self, data->parent->cancellable, + remote_name, installation, remote); { g_autoptr (BzBackendNotification) notif = NULL; @@ -1908,15 +2053,17 @@ transaction_fiber (TransactionData *data) for (guint i = 0; i < installations->len; i++) { BzFlatpakEntry *entry = NULL; + const char *bundle_path = NULL; FlatpakRef *ref = NULL; gboolean is_user = FALSE; g_autofree char *ref_fmt = NULL; g_autoptr (FlatpakTransaction) transaction = NULL; - entry = g_ptr_array_index (installations, i); - ref = bz_flatpak_entry_get_ref (entry); - is_user = bz_flatpak_entry_is_user (BZ_FLATPAK_ENTRY (entry)); - ref_fmt = flatpak_ref_format_ref (ref); + entry = g_ptr_array_index (installations, i); + bundle_path = bz_flatpak_entry_get_bundle_path (entry); + ref = bz_flatpak_entry_get_ref (entry); + is_user = bz_flatpak_entry_is_user (BZ_FLATPAK_ENTRY (entry)); + ref_fmt = flatpak_ref_format_ref (ref); if ((is_user && self->user == NULL) || (!is_user && self->system == NULL)) @@ -1945,12 +2092,25 @@ transaction_fiber (TransactionData *data) local_error->message); } - result = flatpak_transaction_add_install ( - transaction, - bz_entry_get_remote_repo_name (BZ_ENTRY (entry)), - ref_fmt, - NULL, - &local_error); + if (bundle_path != NULL) + /* Prioritize bundle installation */ + { + g_autoptr (GFile) file = NULL; + + file = g_file_new_for_path (bundle_path); + result = flatpak_transaction_add_install_bundle ( + transaction, + file, + NULL, + &local_error); + } + else + result = flatpak_transaction_add_install ( + transaction, + bz_entry_get_remote_repo_name (BZ_ENTRY (entry)), + ref_fmt, + NULL, + &local_error); if (!result) { dex_channel_close_send (channel); @@ -2383,24 +2543,15 @@ transaction_operation_done (FlatpakTransaction *transaction, gint result, TransactionData *data) { - g_autoptr (BzFlatpakInstance) self = NULL; - g_autoptr (BzBackendTransactionOpPayload) payload = NULL; - FlatpakTransactionOperationType op_type = 0; - BzBackendNotificationKind notif_kind = 0; - const char *origin = NULL; - const char *ref = NULL; - gboolean is_user = FALSE; - g_autofree char *unique_id = NULL; - g_autoptr (BzBackendNotification) notif = NULL; - const char *version = NULL; - FlatpakInstallation *installation = NULL; - g_autoptr (FlatpakInstalledRef) iref = NULL; - g_autoptr (GError) local_error = NULL; - g_autoptr (FlatpakRef) parsed_ref = NULL; + g_autoptr (GMutexLocker) locker = NULL; + g_autoptr (BzFlatpakInstance) self = NULL; + g_autoptr (BzBackendTransactionOpPayload) payload = NULL; + g_autoptr (TransactionOperationDoneData) future_data = NULL; + g_autoptr (DexFuture) future = NULL; bz_weak_get_or_return (self, data->self); + locker = g_mutex_locker_new (&data->mutex); - g_mutex_lock (&data->mutex); g_hash_table_replace ( data->op_to_progress_hash, g_object_ref (operation), @@ -2413,74 +2564,23 @@ transaction_operation_done (FlatpakTransaction *transaction, dex_channel_send ( data->channel, dex_future_new_for_object (payload))); - g_mutex_unlock (&data->mutex); if (result == FLATPAK_TRANSACTION_RESULT_NO_CHANGE) return; - op_type = flatpak_transaction_operation_get_operation_type (operation); - switch (op_type) - { - case FLATPAK_TRANSACTION_OPERATION_INSTALL: - case FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE: - notif_kind = BZ_BACKEND_NOTIFICATION_KIND_INSTALL_DONE; - break; - case FLATPAK_TRANSACTION_OPERATION_UPDATE: - notif_kind = BZ_BACKEND_NOTIFICATION_KIND_UPDATE_DONE; - break; - case FLATPAK_TRANSACTION_OPERATION_UNINSTALL: - notif_kind = BZ_BACKEND_NOTIFICATION_KIND_REMOVE_DONE; - break; - case FLATPAK_TRANSACTION_OPERATION_LAST_TYPE: - default: - g_assert_not_reached (); - } - - origin = flatpak_transaction_operation_get_remote (operation); - ref = flatpak_transaction_operation_get_ref (operation); - is_user = flatpak_transaction_get_installation (transaction) == self->user_interactive; - unique_id = bz_flatpak_ref_parts_format_unique (origin, ref, is_user); - - if (notif_kind == BZ_BACKEND_NOTIFICATION_KIND_INSTALL_DONE || - notif_kind == BZ_BACKEND_NOTIFICATION_KIND_UPDATE_DONE) - { - installation = flatpak_transaction_get_installation (transaction); - - parsed_ref = flatpak_ref_parse (ref, &local_error); - if (parsed_ref != NULL) - { - iref = flatpak_installation_get_installed_ref ( - installation, - flatpak_ref_get_kind (parsed_ref), - flatpak_ref_get_name (parsed_ref), - flatpak_ref_get_arch (parsed_ref), - flatpak_ref_get_branch (parsed_ref), - NULL, - &local_error); - - if (iref != NULL) - version = flatpak_installed_ref_get_appdata_version (iref); - else if (local_error != NULL) - { - g_warning ("Failed to get installed ref for version: %s", local_error->message); - g_clear_error (&local_error); - } - } - else if (local_error != NULL) - { - g_warning ("Failed to parse ref for version: %s", local_error->message); - g_clear_error (&local_error); - } - } - - notif = bz_backend_notification_new (); - bz_backend_notification_set_kind (notif, notif_kind); - bz_backend_notification_set_unique_id (notif, unique_id); + future_data = transaction_operation_done_data_new (); + future_data->parent = transaction_data_ref (data); + future_data->transaction = g_object_ref (transaction); + future_data->operation = g_object_ref (operation); - if (version != NULL && *version != '\0') - bz_backend_notification_set_version (notif, version); + future = dex_scheduler_spawn ( + self->scheduler, + bz_get_dex_stack_size (), + (DexFiberFunc) transaction_operation_done_fiber, + transaction_operation_done_data_ref (future_data), + transaction_operation_done_data_unref); - send_notif_all (self, notif, TRUE); + g_ptr_array_add (data->send_futures, g_steal_pointer (&future)); } static gboolean @@ -2632,6 +2732,140 @@ transaction_progress_changed (FlatpakTransactionProgress *progress, g_mutex_unlock (&parent->mutex); } +static DexFuture * +transaction_operation_done_fiber (TransactionOperationDoneData *data) +{ + g_autoptr (BzFlatpakInstance) self = NULL; + FlatpakTransaction *transaction = data->transaction; + FlatpakTransactionOperation *operation = data->operation; + g_autoptr (GError) local_error = NULL; + FlatpakTransactionOperationType op_type = 0; + BzBackendNotificationKind notif_kind = 0; + const char *origin = NULL; + const char *ref = NULL; + gboolean is_user = FALSE; + g_autofree char *unique_id = NULL; + const char *version = NULL; + FlatpakInstallation *installation = NULL; + g_autoptr (FlatpakInstalledRef) iref = NULL; + g_autoptr (FlatpakRef) parsed_ref = NULL; + + bz_weak_get_or_return_reject (self, data->parent->self); + + op_type = flatpak_transaction_operation_get_operation_type (operation); + switch (op_type) + { + case FLATPAK_TRANSACTION_OPERATION_INSTALL: + case FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE: + notif_kind = BZ_BACKEND_NOTIFICATION_KIND_INSTALL_DONE; + break; + case FLATPAK_TRANSACTION_OPERATION_UPDATE: + notif_kind = BZ_BACKEND_NOTIFICATION_KIND_UPDATE_DONE; + break; + case FLATPAK_TRANSACTION_OPERATION_UNINSTALL: + notif_kind = BZ_BACKEND_NOTIFICATION_KIND_REMOVE_DONE; + break; + case FLATPAK_TRANSACTION_OPERATION_LAST_TYPE: + default: + g_assert_not_reached (); + } + + installation = flatpak_transaction_get_installation (transaction); + + origin = flatpak_transaction_operation_get_remote (operation); + ref = flatpak_transaction_operation_get_ref (operation); + is_user = installation == self->user_interactive; + + unique_id = bz_flatpak_ref_parts_format_unique (origin, ref, is_user); + + if (op_type == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE) + { + g_autoptr (FlatpakRemote) remote = NULL; + + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_INVALIDATE_REMOTES); + + send_notif_all (self, notif, TRUE); + } + + remote = flatpak_installation_get_remote_by_name ( + installation, origin, NULL, NULL); + if (remote != NULL) + { + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_START); + bz_backend_notification_set_remote_name (notif, origin); + + send_notif_all (self, notif, TRUE); + } + dex_await ( + retrieve_refs_for_noenumerable_remote ( + self, NULL, origin, installation, remote), + NULL); + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, BZ_BACKEND_NOTIFICATION_KIND_REMOTE_SYNC_FINISH); + bz_backend_notification_set_remote_name (notif, origin); + + send_notif_all (self, notif, TRUE); + } + } + } + + if (notif_kind == BZ_BACKEND_NOTIFICATION_KIND_INSTALL_DONE || + notif_kind == BZ_BACKEND_NOTIFICATION_KIND_UPDATE_DONE) + { + parsed_ref = flatpak_ref_parse (ref, &local_error); + if (parsed_ref != NULL) + { + iref = flatpak_installation_get_installed_ref ( + installation, + flatpak_ref_get_kind (parsed_ref), + flatpak_ref_get_name (parsed_ref), + flatpak_ref_get_arch (parsed_ref), + flatpak_ref_get_branch (parsed_ref), + NULL, + &local_error); + + if (iref != NULL) + version = flatpak_installed_ref_get_appdata_version (iref); + else if (local_error != NULL) + { + g_warning ("Failed to get installed ref for version: %s", local_error->message); + g_clear_error (&local_error); + } + } + else if (local_error != NULL) + { + g_warning ("Failed to parse ref for version: %s", local_error->message); + g_clear_error (&local_error); + } + } + + { + g_autoptr (BzBackendNotification) notif = NULL; + + notif = bz_backend_notification_new (); + bz_backend_notification_set_kind (notif, notif_kind); + bz_backend_notification_set_unique_id (notif, unique_id); + + if (version != NULL && *version != '\0') + bz_backend_notification_set_version (notif, version); + + send_notif_all (self, notif, TRUE); + } + + return dex_future_new_true (); +} + static void installation_event (BzFlatpakInstance *self, GFile *file, diff --git a/src/bz-full-view.blp b/src/bz-full-view.blp index b2b3ce86..8ed3b044 100644 --- a/src/bz-full-view.blp +++ b/src/bz-full-view.blp @@ -95,11 +95,11 @@ template $BzFullView: Adw.Bin { margin-bottom: 15; spacing: 20; - Adw.Banner { - title: _("Installing .flatpak bundles is not yet supported"); - visible: bind template.ui-entry as <$BzResult>.object as <$BzFlatpakEntry>.is-bundle as ; - revealed: true; - } + // Adw.Banner { + // title: _("Installing .flatpak bundles is not yet supported"); + // visible: bind template.ui-entry as <$BzResult>.object as <$BzFlatpakEntry>.is-bundle as ; + // revealed: true; + // } Adw.Banner { title: _("This is a local preview, some details may differ from the published listing"); diff --git a/src/bz-install-controls.c b/src/bz-install-controls.c index 64451abc..e0c63e6b 100644 --- a/src/bz-install-controls.c +++ b/src/bz-install-controls.c @@ -40,6 +40,8 @@ struct _BzInstallControls gboolean wide; BzTransactionEntryTracker *tracker; + GListModel *all_trackers; + GtkWidget *install_button; char *install_btn_class; char *pride_class; @@ -349,6 +351,12 @@ bz_install_controls_dispose (GObject *object) pride_flag_changed, self); + if (self->all_trackers != NULL) + g_signal_handlers_disconnect_by_func ( + self->all_trackers, + on_all_trackers_changed, + self); + g_clear_object (&self->group); g_clear_object (&self->state); g_clear_object (&self->settings); @@ -356,6 +364,7 @@ bz_install_controls_dispose (GObject *object) g_clear_object (&self->install_button); g_clear_pointer (&self->install_btn_class, g_free); g_clear_pointer (&self->pride_class, g_free); + g_clear_object (&self->all_trackers); G_OBJECT_CLASS (bz_install_controls_parent_class)->dispose (object); } @@ -609,30 +618,23 @@ bz_install_controls_set_state (BzInstallControls *self, { g_return_if_fail (BZ_IS_INSTALL_CONTROLS (self)); - if (self->state != NULL) + if (self->all_trackers != NULL) { - BzTransactionManager *old_mgr = NULL; - g_autoptr (GListModel) all = NULL; - - old_mgr = bz_state_info_get_transaction_manager (self->state); - if (old_mgr != NULL) - g_object_get (old_mgr, "all-trackers", &all, NULL); - if (all != NULL) - g_signal_handlers_disconnect_by_func (all, on_all_trackers_changed, self); + g_signal_handlers_disconnect_by_func (self->all_trackers, on_all_trackers_changed, self); + g_clear_object (&self->all_trackers); } g_set_object (&self->state, state); if (state != NULL) { - BzTransactionManager *mgr = NULL; - g_autoptr (GListModel) all = NULL; + BzTransactionManager *mgr = NULL; mgr = bz_state_info_get_transaction_manager (state); if (mgr != NULL) - g_object_get (mgr, "all-trackers", &all, NULL); - if (all != NULL) - g_signal_connect (all, "items-changed", + g_object_get (mgr, "all-trackers", &self->all_trackers, NULL); + if (self->all_trackers != NULL) + g_signal_connect (self->all_trackers, "items-changed", G_CALLBACK (on_all_trackers_changed), self); } diff --git a/src/bz-transaction-dialog.c b/src/bz-transaction-dialog.c index b645d813..0ecb8be0 100644 --- a/src/bz-transaction-dialog.c +++ b/src/bz-transaction-dialog.c @@ -42,8 +42,7 @@ should_skip_entry (BzEntry *entry, if (bz_entry_is_holding (entry)) return TRUE; - if (!remove && BZ_IS_FLATPAK_ENTRY (entry) && - bz_flatpak_entry_is_installed_ref (BZ_FLATPAK_ENTRY (entry))) + if (!remove && !bz_entry_is_reinstallable (entry)) return TRUE; is_installed = bz_entry_is_installed (entry); @@ -337,8 +336,7 @@ show_dialog_fiber (ShowDialogData *data) g_autoptr (BzEntry) entry = NULL; entry = g_list_model_get_item (G_LIST_MODEL (store), i - 1); - if (BZ_IS_FLATPAK_ENTRY (entry) && - bz_flatpak_entry_is_installed_ref (BZ_FLATPAK_ENTRY (entry)) && + if (!bz_entry_is_reinstallable (entry) && (!data->remove || !bz_entry_is_installed (entry))) g_list_store_remove (store, i - 1); } diff --git a/src/meson.build b/src/meson.build index 9671be6d..37f01d47 100644 --- a/src/meson.build +++ b/src/meson.build @@ -67,12 +67,13 @@ bz_sources = files( 'bz-async-texture.c', 'bz-auth-state.c', 'bz-backend.c', + 'bz-bundle-install-dialog.c', 'bz-category-flags.c', 'bz-category-tile.c', 'bz-content-provider.c', 'bz-context-row.c', - 'bz-context-tile.c', 'bz-context-tile-callbacks.c', + 'bz-context-tile.c', 'bz-curated-app-tile.c', 'bz-curated-view.c', 'bz-data-graph.c', @@ -137,8 +138,8 @@ bz_sources = files( 'bz-screenshots-carousel.c', 'bz-search-engine.c', 'bz-search-filter-popover.c', - 'bz-search-pill-list.c', 'bz-search-page.c', + 'bz-search-pill-list.c', 'bz-section-view.c', 'bz-serializable.c', 'bz-share-list.c', @@ -262,19 +263,20 @@ blueprints = custom_target('blueprints', input: files( 'bz-addon-tile.blp', 'bz-addons-dialog.blp', - 'bz-donations-dialog.blp', 'bz-age-rating-dialog.blp', 'bz-all-apps-page.blp', 'bz-app-size-dialog.blp', 'bz-app-tile.blp', 'bz-apps-page.blp', 'bz-appstream-description-render.blp', + 'bz-bundle-install-dialog.blp', 'bz-category-tile.blp', 'bz-context-tile.blp', 'bz-curated-app-tile.blp', 'bz-curated-view.blp', 'bz-decorated-screenshot.blp', 'bz-developer-badge.blp', + 'bz-donations-dialog.blp', 'bz-entry-inspector.blp', 'bz-entry-selection-row.blp', 'bz-error-dialog.blp', @@ -302,8 +304,8 @@ blueprints = custom_target('blueprints', 'bz-safety-dialog.blp', 'bz-screenshot-page.blp', 'bz-screenshots-carousel.blp', - 'bz-search-page.blp', 'bz-search-filter-popover.blp', + 'bz-search-page.blp', 'bz-section-view.blp', 'bz-stats-dialog.blp', 'bz-transaction-list-dialog.blp',