debdiff *.dsc | filterdiff -p1 -x'po/*.po' -x'debian/patches/*.patch' >| gnome-software_48.3-2.diff

diff -Nru gnome-software-48.2/data/metainfo/org.gnome.Software.metainfo.xml.in gnome-software-48.3/data/metainfo/org.gnome.Software.metainfo.xml.in
--- gnome-software-48.2/data/metainfo/org.gnome.Software.metainfo.xml.in	2025-06-02 10:33:10.000000000 +0100
+++ gnome-software-48.3/data/metainfo/org.gnome.Software.metainfo.xml.in	2025-06-27 07:43:58.000000000 +0100
@@ -66,6 +66,19 @@
    Validate with `appstreamcli validate *.metainfo.xml`
   -->
   <releases>
+    <release date="2025-06-27" version="48.3" type="stable">
+      <description>
+        <p>This is a stable release with the following change:</p>
+        <ul>
+          <li>Fix crash on application shutdown</li>
+        </ul>
+        <p>This release also updates translation:</p>
+        <ul>
+          <li>Chinese (Taiwan) (Cheng-Chia Tseng)</li>
+        </ul>
+      </description>
+    </release>
+
     <release date="2025-06-02" version="48.2" type="stable">
       <description>
         <p>This is a stable release with the following changes:</p>
diff -Nru gnome-software-48.2/debian/changelog gnome-software-48.3/debian/changelog
--- gnome-software-48.2/debian/changelog	2025-06-06 20:47:49.000000000 +0100
+++ gnome-software-48.3/debian/changelog	2025-07-17 13:08:06.000000000 +0100
@@ -1,3 +1,49 @@
+gnome-software (48.3-2) unstable; urgency=medium
+
+  * Team upload
+  * d/patches: Update from upstream gnome-48 branch, up to commit
+    48.3-6-g10fb1a6bb
+    - d/p/update-monitor-improve-logic-for-deciding-when-to-show-no.patch:
+      Reduce frequency of update notifications
+      (gnome-software#2856 upstream)
+      + Don't notify users about out-of-date apt metadata until it's at
+        least 7 days old.
+      + Don't nag users about software update actions more than once per
+        day, with the frequency reducing according to the importance of
+        the action we are suggesting.
+      + Don't notify users about out-of-date apt metadata if we would not
+        be able to download it right now in any case. In particular this
+        prevents an annoying notification when resuming from suspend,
+        during which time we can expect that networking has not yet come
+        back up.
+    - d/p/gs-update-monitor-Do-not-show-Updates-are-Out-of-Date-whe.patch:
+      Don't notify users about out-of-date apt metadata if updates are
+      being prevented by configuration
+      (gnome-software#2815 upstream)
+    - Translation updates
+  * d/rules: Don't display the sample list of "Editor's Choice" apps.
+    Upstream no longer recommends using this list and does not maintain
+    it, and we don't want to keep recommending apps that are dead upstream
+    such as gnome-dictionary and gnome-photos. Ideally we should provide
+    our own curated list for forky, but until we can do that, a minimal
+    fix is to not display it. (Helps: #1100084)
+
+ -- Simon McVittie <smcv@debian.org>  Thu, 17 Jul 2025 13:08:06 +0100
+
+gnome-software (48.3-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream bugfix release
+    - Fix a crash on exit while a "toast" notification is shown
+      (gnome-software#2853 upstream)
+    - Explicitly disconnect signal handlers on dispose, to guard against
+      possible use-after-free crashes during exit
+      (related to gnome-software#2853 upstream)
+    - Translation updates
+  * d/control: Remove Gunnar Hjalmarsson from Uploaders (Closes: #1069562)
+
+ -- Simon McVittie <smcv@debian.org>  Mon, 14 Jul 2025 18:02:25 +0100
+
 gnome-software (48.2-1) unstable; urgency=medium
 
   * New upstream bugfix release
diff -Nru gnome-software-48.2/debian/control gnome-software-48.3/debian/control
--- gnome-software-48.2/debian/control	2025-06-06 20:47:49.000000000 +0100
+++ gnome-software-48.3/debian/control	2025-07-17 13:08:06.000000000 +0100
@@ -3,7 +3,6 @@
 Priority: optional
 Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
 Uploaders: Amin Bandali <bandali@ubuntu.com>,
-           Gunnar Hjalmarsson <gunnarhj@debian.org>,
            Jeremy Bícha <jbicha@ubuntu.com>,
            Laurent Bigonville <bigon@debian.org>,
            Matthias Klumpp <mak@debian.org>
diff -Nru gnome-software-48.2/debian/patches/series gnome-software-48.3/debian/patches/series
--- gnome-software-48.2/debian/patches/series	2025-06-06 20:47:49.000000000 +0100
+++ gnome-software-48.3/debian/patches/series	2025-07-17 13:08:06.000000000 +0100
@@ -0,0 +1,4 @@
+Update-Russian-translation.patch
+gs-update-monitor-Do-not-show-Updates-are-Out-of-Date-whe.patch
+update-monitor-improve-logic-for-deciding-when-to-show-no.patch
+Update-Chinese-Taiwan-translation.patch
diff -Nru gnome-software-48.2/debian/rules gnome-software-48.3/debian/rules
--- gnome-software-48.2/debian/rules	2025-06-06 20:47:49.000000000 +0100
+++ gnome-software-48.3/debian/rules	2025-07-17 13:08:06.000000000 +0100
@@ -12,7 +12,6 @@
 	-Dpackagekit_autoremove=true \
 	-Drpm_ostree=false \
 	-Dapt=true \
-	-Dhardcoded_curated=true \
 	-Ddefault_featured_apps=true
 
 # these are conditionally re-enabled later
diff -Nru gnome-software-48.2/meson.build gnome-software-48.3/meson.build
--- gnome-software-48.2/meson.build	2025-06-02 10:33:10.000000000 +0100
+++ gnome-software-48.3/meson.build	2025-06-27 07:43:58.000000000 +0100
@@ -1,5 +1,5 @@
 project('gnome-software', 'c',
-  version : '48.2',
+  version : '48.3',
   license : 'GPL-2.0-or-later',
   default_options : ['warning_level=2', 'c_std=c11'],
   meson_version : '>=1.0.1'
diff -Nru gnome-software-48.2/NEWS gnome-software-48.3/NEWS
--- gnome-software-48.2/NEWS	2025-06-02 10:33:10.000000000 +0100
+++ gnome-software-48.3/NEWS	2025-06-27 07:43:58.000000000 +0100
@@ -1,3 +1,13 @@
+Version 48.3
+~~~~~~~~~~~~
+Released: 2025-06-27
+
+This is a stable release with the following change:
+ * Fix crash on application shutdown
+
+This release also updates translation:
+ * Chinese (Taiwan) (Cheng-Chia Tseng)
+
 Version 48.2
 ~~~~~~~~~~~~
 Released: 2025-06-02
diff -Nru gnome-software-48.2/src/gs-shell.c gnome-software-48.3/src/gs-shell.c
--- gnome-software-48.2/src/gs-shell.c	2025-06-02 10:33:10.000000000 +0100
+++ gnome-software-48.3/src/gs-shell.c	2025-06-27 07:43:58.000000000 +0100
@@ -66,8 +66,17 @@
 	AdwApplicationWindow	 parent_object;
 
 	GSettings		*settings;
+	gulong			 settings_changed_download_updates_id;
+
 	GCancellable		*cancellable;
+
 	GsPluginLoader		*plugin_loader;
+	gulong			 plugin_loader_reload_id;
+	gulong			 plugin_loader_notify_events_id;
+	gulong			 plugin_loader_notify_network_metered_id;
+	gulong			 plugin_loader_basic_auth_start_id;
+	gulong			 plugin_loader_ask_untrusted_id;
+
 	GtkWidget		*header_start_widget;
 	GtkWidget		*header_end_widget;
 	GtkWidget		*sub_header_end_widget;
@@ -106,6 +115,7 @@
 	guint			 allocation_changed_cb_id;
 
 	GsPage			*pages[GS_SHELL_MODE_LAST];
+	gulong			 overview_page_refreshed_id;
 };
 
 G_DEFINE_TYPE (GsShell, gs_shell, ADW_TYPE_APPLICATION_WINDOW)
@@ -704,6 +714,13 @@
 	guint i;
 	g_autoptr(GPtrArray) events = NULL;
 
+	/* If a toast is showing when the GsShell is disposed, libadwaita will
+	 * explicitly dismiss the toast. Unfortunately this happens after
+	 * chaining up from GsShell.dispose(), so the plugin loader has already
+	 * been cleared. */
+	if (shell->plugin_loader == NULL)
+		return;
+
 	/* mark any events currently showing as invalid */
 	events = gs_plugin_loader_get_events (shell->plugin_loader);
 	for (i = 0; i < events->len; i++) {
@@ -831,11 +848,12 @@
 {
 	GsShell *shell = data;
 
-	g_signal_handlers_disconnect_by_func (overview_page, overview_page_refresh_done, data);
+	g_clear_signal_handler (&shell->overview_page_refreshed_id, overview_page);
 
 	/* now that we're finished with the loading page, connect the reload signal handler */
-	g_signal_connect (shell->plugin_loader, "reload",
-	                  G_CALLBACK (gs_shell_reload_cb), shell);
+	shell->plugin_loader_reload_id =
+		g_signal_connect (shell->plugin_loader, "reload",
+			          G_CALLBACK (gs_shell_reload_cb), shell);
 
 	/* schedule to change the mode in an idle callback, since it can take a
 	 * while and this callback handler is typically called at the end of a
@@ -858,15 +876,17 @@
 	/* if the "loaded" signal handler didn't change the mode, kick off async
 	 * overview page refresh, and switch to the page once done */
 	if (gs_shell_get_mode (shell) == GS_SHELL_MODE_LOADING || been_overview) {
-		g_signal_connect (shell->pages[GS_SHELL_MODE_OVERVIEW], "refreshed",
-		                  G_CALLBACK (overview_page_refresh_done), shell);
+		shell->overview_page_refreshed_id =
+			g_signal_connect (shell->pages[GS_SHELL_MODE_OVERVIEW], "refreshed",
+				          G_CALLBACK (overview_page_refresh_done), shell);
 		gs_page_reload (GS_PAGE (shell->pages[GS_SHELL_MODE_OVERVIEW]));
 		return;
 	}
 
 	/* now that we're finished with the loading page, connect the reload signal handler */
-	g_signal_connect (shell->plugin_loader, "reload",
-	                  G_CALLBACK (gs_shell_reload_cb), shell);
+	shell->plugin_loader_reload_id =
+		g_signal_connect (shell->plugin_loader, "reload",
+			          G_CALLBACK (gs_shell_reload_cb), shell);
 }
 
 static gboolean
@@ -2192,8 +2212,9 @@
 	gs_shell_setup_pages (shell);
 
 	/* set up the metered data info bar and mogwai */
-	g_signal_connect (shell->settings, "changed::download-updates",
-			  (GCallback) gs_shell_download_updates_changed_cb, shell);
+	shell->settings_changed_download_updates_id =
+		g_signal_connect (shell->settings, "changed::download-updates",
+				  (GCallback) gs_shell_download_updates_changed_cb, shell);
 
 	odrs_provider = gs_plugin_loader_get_odrs_provider (shell->plugin_loader);
 	gs_details_page_set_odrs_provider (GS_DETAILS_PAGE (shell->pages[GS_SHELL_MODE_DETAILS]), odrs_provider);
@@ -2459,6 +2480,8 @@
 {
 	GsShell *shell = GS_SHELL (object);
 
+	g_clear_signal_handler (&shell->overview_page_refreshed_id, shell->pages[GS_SHELL_MODE_OVERVIEW]);
+
 	g_clear_object (&shell->sub_page_header_title_binding);
 
 	if (shell->back_entry_stack != NULL) {
@@ -2466,11 +2489,20 @@
 		shell->back_entry_stack = NULL;
 	}
 	g_clear_object (&shell->cancellable);
+
+	g_clear_signal_handler (&shell->plugin_loader_reload_id, shell->plugin_loader);
+	g_clear_signal_handler (&shell->plugin_loader_notify_events_id, shell->plugin_loader);
+	g_clear_signal_handler (&shell->plugin_loader_notify_network_metered_id, shell->plugin_loader);
+	g_clear_signal_handler (&shell->plugin_loader_basic_auth_start_id, shell->plugin_loader);
+	g_clear_signal_handler (&shell->plugin_loader_ask_untrusted_id, shell->plugin_loader);
 	g_clear_object (&shell->plugin_loader);
+
 	g_clear_object (&shell->header_start_widget);
 	g_clear_object (&shell->header_end_widget);
 	g_clear_object (&shell->sub_header_end_widget);
 	g_clear_object (&shell->page);
+
+	g_clear_signal_handler (&shell->settings_changed_download_updates_id, shell->settings);
 	g_clear_object (&shell->settings);
 
 #ifdef HAVE_MOGWAI
diff -Nru gnome-software-48.2/src/gs-update-monitor.c gnome-software-48.3/src/gs-update-monitor.c
--- gnome-software-48.2/src/gs-update-monitor.c	2025-06-02 10:33:10.000000000 +0100
+++ gnome-software-48.3/src/gs-update-monitor.c	2025-07-17 14:35:01.000000000 +0100
@@ -200,26 +200,38 @@
 {
 	gboolean has_important = FALSE, all_downloaded = FALSE, any_downloaded = FALSE;
 	gboolean should_download, res = FALSE;
-	gint64 timestamp_days;
+	gint64 notification_timestamp_days;
 
-	if (!get_timestamp_difference_days (monitor, "update-notification-timestamp", &timestamp_days)) {
+	if (!get_timestamp_difference_days (monitor, "update-notification-timestamp", &notification_timestamp_days)) {
 		/* Large-enough number to succeed for the initial test */
-		timestamp_days = 365;
+		notification_timestamp_days = 365;
 	}
 
-	should_download = should_download_updates (monitor);
+	/* can_download reflects whether GNOME Software should *automatically*
+	 * download updates, not whether the user is able to do so. E.g. it is
+	 * false if power saving mode is enabled.
+	 *
+	 * Beware it is expected to be spuriously false, e.g. immediately after
+	 * resuming from suspend when no network is available for a second or
+	 * two. This is OK because it should not be spuriously false for
+	 * long periods of time.
+	 */
+	should_download = should_download_updates (monitor) && can_download;
+
 	if (apps != NULL)
 		check_updates_kind (apps, &has_important, &all_downloaded, &any_downloaded);
 
 	if (apps == NULL || !gs_app_list_length (apps)) {
-		/* Notify only when the download is disabled, or cannot download, and it's the 4th day or it's more than 7 days */
-		if ((!should_download || !can_download) && (timestamp_days >= 7 || timestamp_days == 4)) {
+		if (!should_download &&
+		    gs_plugin_loader_get_allow_updates (monitor->plugin_loader) &&
+		    notification_timestamp_days >= 1 &&
+		    check_if_timestamp_more_than_days_ago (monitor, "check-timestamp", 7)) {
 			*out_title = _("Updates Are Out of Date");
 			*out_body = _("Please check for available updates");
 			res = TRUE;
 		}
 	} else if (has_important) {
-		if (timestamp_days >= 1) {
+		if (notification_timestamp_days >= 1) {
 			if (all_downloaded) {
 				*out_title = _("Critical Updates Ready to Install");
 				*out_body = _("Install critical updates as soon as possible");
@@ -231,14 +243,13 @@
 			}
 		}
 	} else if (all_downloaded) {
-		if (timestamp_days >= 3) {
+		if (notification_timestamp_days >= 3) {
 			*out_title = _("Updates Ready to Install");
 			*out_body = _("Software updates are ready and waiting");
 			res = TRUE;
 		}
-	/* To not hide downloaded updates for 14 days when new updates were discovered meanwhile.
-	   Never show "Available to Download" when it's supposed to download the updates. */
-	} else if (!should_download && timestamp_days >= 14) {
+	} else if (!should_download && notification_timestamp_days >= 3 &&
+		   check_if_timestamp_more_than_days_ago (monitor, "install-timestamp", 14)) {
 		*out_title = _("Updates Available to Download");
 		*out_body = _("Software updates can be downloaded");
 		res = TRUE;
@@ -246,7 +257,7 @@
 
 	g_debug ("%s: last_test_days:%" G_GINT64_FORMAT " n-apps:%u should_download:%d can_download:%d has_important:%d "
 		"all_downloaded:%d any_downloaded:%d res:%d%s%s%s%s", G_STRFUNC,
-		timestamp_days, apps == NULL ? 0 : gs_app_list_length (apps), should_download, can_download, has_important,
+		notification_timestamp_days, apps == NULL ? 0 : gs_app_list_length (apps), should_download, can_download, has_important,
 		all_downloaded, any_downloaded, res,
 		res ? " reason:" : "",
 		res ? *out_title : "",
