*
* 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 "bz-entry-group.h"
#include "bz-entry.h"
#include "bz-releases-list.h"
#include "bz-result.h"
#include "bz-update-history-data-point.h"
#include "bz-update-history-dialog.h"
#include "env.h"
#include "template-callbacks.h"
#include "util.h"
struct _BzUpdateHistoryDialog
{
AdwDialog parent_instance;
AdwNavigationView *nav_view;
GtkListView *list_view;
AdwNavigationPage *releases_page;
GtkStack *releases_stack;
AdwBin *releases_bin;
};
G_DEFINE_FINAL_TYPE (BzUpdateHistoryDialog, bz_update_history_dialog, ADW_TYPE_DIALOG)
static DexFuture *
load_releases_fiber (BzWeakRef *wr,
BzEntryGroup *group,
GtkStringList *installed);
static char *
format_version_change (gpointer object,
const char *old_version,
const char *new_version)
{
if (new_version == NULL)
return g_strdup ("");
if (old_version != NULL && g_strcmp0 (old_version, new_version) != 0)
return g_strdup_printf ("%s → %s", old_version, new_version);
return g_strdup (new_version);
}
static void
row_activated_cb (BzUpdateHistoryDialog *self,
guint position,
GtkListView *list_view)
{
g_autoptr (BzUpdateHistoryDataPoint) point = NULL;
g_autoptr (BzWeakRef) wr = NULL;
g_autoptr (GtkStringList) installed = NULL;
GtkSelectionModel *model = NULL;
BzEntryGroup *group = NULL;
const char *new_version = NULL;
g_return_if_fail (BZ_IS_UPDATE_HISTORY_DIALOG (self));
model = gtk_list_view_get_model (self->list_view);
if (model == NULL)
return;
point = g_list_model_get_item (G_LIST_MODEL (model), position);
if (point == NULL)
return;
group = bz_update_history_data_point_get_group (point);
new_version = bz_update_history_data_point_get_new_version (point);
if (group != NULL)
{
const char *title = NULL;
title = bz_entry_group_get_title (group);
adw_navigation_page_set_title (self->releases_page,
title != NULL ? title : _ ("Release Notes"));
}
adw_bin_set_child (self->releases_bin, NULL);
gtk_stack_set_visible_child_name (self->releases_stack, "empty");
adw_navigation_view_push_by_tag (self->nav_view, "releases");
if (group == NULL)
return;
wr = bz_weak_ref_new (self);
installed = gtk_string_list_new (NULL);
if (new_version != NULL)
gtk_string_list_append (installed, new_version);
dex_future_disown (dex_scheduler_spawnv (
dex_scheduler_get_default (),
bz_get_dex_stack_size (),
G_CALLBACK (load_releases_fiber),
3,
BZ_TYPE_WEAK_REF, wr,
BZ_TYPE_ENTRY_GROUP, group,
GTK_TYPE_STRING_LIST, installed));
}
static void
bz_update_history_dialog_class_init (BzUpdateHistoryDialogClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
g_type_ensure (BZ_TYPE_UPDATE_HISTORY_DATA_POINT);
g_type_ensure (BZ_TYPE_ENTRY_GROUP);
g_type_ensure (BZ_TYPE_ENTRY);
g_type_ensure (BZ_TYPE_RESULT);
gtk_widget_class_set_template_from_resource (widget_class,
"/io/github/kolunmi/Bazaar/bz-update-history-dialog.ui");
bz_widget_class_bind_all_util_callbacks (widget_class);
gtk_widget_class_bind_template_child (widget_class, BzUpdateHistoryDialog, nav_view);
gtk_widget_class_bind_template_child (widget_class, BzUpdateHistoryDialog, list_view);
gtk_widget_class_bind_template_child (widget_class, BzUpdateHistoryDialog, releases_page);
gtk_widget_class_bind_template_child (widget_class, BzUpdateHistoryDialog, releases_stack);
gtk_widget_class_bind_template_child (widget_class, BzUpdateHistoryDialog, releases_bin);
gtk_widget_class_bind_template_callback (widget_class, format_version_change);
gtk_widget_class_bind_template_callback (widget_class, row_activated_cb);
}
static void
bz_update_history_dialog_init (BzUpdateHistoryDialog *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
AdwDialog *
bz_update_history_dialog_new (GListModel *model)
{
BzUpdateHistoryDialog *self = NULL;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
self = g_object_new (BZ_TYPE_UPDATE_HISTORY_DIALOG, NULL);
if (model != NULL)
{
g_autoptr (GtkNoSelection) selection = NULL;
selection = gtk_no_selection_new (g_object_ref (model));
gtk_list_view_set_model (self->list_view, GTK_SELECTION_MODEL (selection));
}
return ADW_DIALOG (self);
}
static DexFuture *
load_releases_fiber (BzWeakRef *wr,
BzEntryGroup *group,
GtkStringList *installed)
{
g_autoptr (BzUpdateHistoryDialog) self = NULL;
g_autoptr (GError) local_error = NULL;
g_autoptr (BzResult) result = NULL;
g_autoptr (BzEntry) entry = NULL;
g_autoptr (GListModel) history = NULL;
GtkWidget *list = NULL;
guint n_items = 0;
bz_weak_get_or_return_reject (self, &wr->ref);
result = bz_entry_group_dup_ui_entry (group);
if (result == NULL)
return dex_future_new_false ();
entry = dex_await_object (bz_result_dup_future (result), &local_error);
if (entry == NULL)
return dex_future_new_for_error (g_steal_pointer (&local_error));
g_object_get (entry, "version-history", &history, NULL);
if (history != NULL)
n_items = g_list_model_get_n_items (history);
if (n_items > 0)
{
list = bz_releases_list_new (history, G_LIST_MODEL (installed));
adw_bin_set_child (self->releases_bin, list);
}
gtk_stack_set_visible_child_name (self->releases_stack,
n_items > 0 ? "content" : "empty");
return dex_future_new_true ();
}