*
* 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
*/
#include "config.h"
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "bz-async-texture.h"
#include "bz-entry.h"
#include "bz-transaction-entry-tracker.h"
#include "env.h"
#include "transaction-notification.h"
#include "util.h"
BZ_DEFINE_DATA (
notify_finished,
NotifyFinished,
{
BzTransaction *transaction;
gboolean success;
},
BZ_RELEASE_DATA (transaction, g_object_unref))
static DexFuture *
notify_finished_release_hold (DexFuture *future,
GApplication *app);
static DexFuture *
notify_finished_fiber (NotifyFinishedData *data);
void
bz_transaction_notify_finished (BzTransaction *transaction,
gboolean success)
{
g_autoptr (NotifyFinishedData) data = NULL;
GApplication *app = NULL;
g_return_if_fail (BZ_IS_TRANSACTION (transaction));
app = g_application_get_default ();
g_application_hold (app);
data = notify_finished_data_new ();
data->transaction = g_object_ref (transaction);
data->success = success;
dex_future_disown (dex_future_finally (
dex_scheduler_spawn (
dex_scheduler_get_default (),
bz_get_dex_stack_size (),
(DexFiberFunc) notify_finished_fiber,
notify_finished_data_ref (data),
notify_finished_data_unref),
(DexFutureCallback) notify_finished_release_hold,
app, NULL));
}
static DexFuture *
notify_finished_release_hold (DexFuture *future,
GApplication *app)
{
g_application_release (app);
return dex_ref (future);
}
static DexFuture *
notify_finished_fiber (NotifyFinishedData *data)
{
BzTransaction *transaction = data->transaction;
GApplication *app = NULL;
GList *windows = NULL;
GListModel *trackers = NULL;
guint n_trackers = 0;
if (!data->success)
return dex_future_new_true ();
app = g_application_get_default ();
windows = gtk_application_get_windows (GTK_APPLICATION (app));
if (windows != NULL)
return dex_future_new_true ();
trackers = bz_transaction_get_trackers (transaction);
if (trackers != NULL)
n_trackers = g_list_model_get_n_items (trackers);
for (guint i = 0; i < n_trackers; i++)
{
g_autoptr (BzTransactionEntryTracker) tracker = NULL;
BzTransactionEntryKind kind = 0;
BzEntry *entry = NULL;
const char *title = NULL;
g_autofree char *summary = NULL;
g_autofree char *notif_id = NULL;
g_autoptr (GNotification) notification = NULL;
g_autoptr (GIcon) icon = NULL;
GdkPaintable *paintable = NULL;
tracker = g_list_model_get_item (trackers, i);
kind = bz_transaction_entry_tracker_get_kind (tracker);
if (kind != BZ_TRANSACTION_ENTRY_KIND_INSTALL)
continue;
entry = bz_transaction_entry_tracker_get_entry (tracker);
if (entry == NULL || !bz_entry_is_of_kinds (entry, BZ_ENTRY_KIND_APPLICATION))
continue;
title = bz_entry_get_title (entry);
if (title == NULL)
title = bz_entry_get_id (entry);
if (title == NULL)
continue;
summary = g_strdup_printf (_ ("%s Installed"), title);
notification = g_notification_new (summary);
g_notification_set_body (notification, _ ("The app is ready to be used"));
paintable = bz_entry_get_icon_paintable (entry);
if (paintable != NULL && BZ_IS_ASYNC_TEXTURE (paintable))
icon = (GIcon *) dex_await_object (
bz_async_texture_dup_icon_future (BZ_ASYNC_TEXTURE (paintable)),
NULL);
if (icon != NULL)
g_notification_set_icon (notification, icon);
g_notification_set_priority (notification, G_NOTIFICATION_PRIORITY_NORMAL);
notif_id = g_strdup_printf ("app-installed-%s", bz_entry_get_unique_id (entry));
g_application_send_notification (app, notif_id, notification);
}
return dex_future_new_true ();
}