# HG changeset patch
# Node ID f6dde943ec3494877ee8e6795a82ce228b7bca05
# Parent  818220289e6707dc41849145c12a60bfb96aabe7
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -49,6 +49,7 @@
 	'purplenotification.c',
 	'purplenotificationaddcontact.c',
 	'purplenotificationauthorizationrequest.c',
+	'purplenotificationconnectionerror.c',
 	'purplenotificationlink.c',
 	'purplenotificationmanager.c',
 	'purpleoptions.c',
@@ -143,6 +144,7 @@
 	'purplenotification.h',
 	'purplenotificationaddcontact.h',
 	'purplenotificationauthorizationrequest.h',
+	'purplenotificationconnectionerror.h',
 	'purplenotificationlink.h',
 	'purplenotificationmanager.h',
 	'purpleoptions.h',
diff --git a/libpurple/purplenotification.c b/libpurple/purplenotification.c
--- a/libpurple/purplenotification.c
+++ b/libpurple/purplenotification.c
@@ -29,6 +29,7 @@
 #include "purpleenums.h"
 #include "purplenotificationaddcontact.h"
 #include "purplenotificationauthorizationrequest.h"
+#include "purplenotificationconnectionerror.h"
 #include "util.h"
 
 typedef struct {
@@ -540,43 +541,9 @@
 purple_notification_new_from_connection_error(PurpleAccount *account,
                                               PurpleConnectionErrorInfo *info)
 {
-	PurpleConnectionErrorInfo *copy = NULL;
-	PurpleNotification *notification = NULL;
-	PurpleProtocol *protocol = NULL;
-	char *title = NULL;
-	const char *username = NULL;
-
 	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
-	g_return_val_if_fail(info != NULL, NULL);
-
-	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-	copy = purple_connection_error_info_copy(info);
-	notification = purple_notification_new(PURPLE_NOTIFICATION_TYPE_CONNECTION_ERROR,
-	                                       account, copy,
-	                                       (GDestroyNotify)purple_connection_error_info_free);
-	G_GNUC_END_IGNORE_DEPRECATIONS
 
-	/* Set the title of the notification. */
-	username = purple_account_get_username(account);
-	if(purple_account_get_enabled(account)) {
-		title = g_strdup_printf(_("%s disconnected"), username);
-	} else {
-		title = g_strdup_printf(_("%s disabled"), username);
-	}
-	purple_notification_set_title(notification, title);
-	g_free(title);
-
-	/* Add the protocol's icon as the notification's icon. */
-	protocol = purple_account_get_protocol(account);
-	if(PURPLE_IS_PROTOCOL(protocol)) {
-		const char *icon_name = purple_protocol_get_icon_name(protocol);
-
-		if(icon_name != NULL) {
-			purple_notification_set_icon_name(notification, icon_name);
-		}
-	}
-
-	return notification;
+	return purple_notification_connection_error_new(NULL, account, info);
 }
 
 const char *
diff --git a/libpurple/purplenotification.h b/libpurple/purplenotification.h
--- a/libpurple/purplenotification.h
+++ b/libpurple/purplenotification.h
@@ -181,7 +181,7 @@
  *
  * Since: 3.0
  */
-PURPLE_AVAILABLE_IN_3_0
+PURPLE_DEPRECATED_FOR(purple_notification_connection_error_new)
 PurpleNotification *purple_notification_new_from_connection_error(PurpleAccount *account, PurpleConnectionErrorInfo *info);
 
 /**
diff --git a/libpurple/purplenotificationconnectionerror.c b/libpurple/purplenotificationconnectionerror.c
new file mode 100644
--- /dev/null
+++ b/libpurple/purplenotificationconnectionerror.c
@@ -0,0 +1,251 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This library 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 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library 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 library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "purplenotificationconnectionerror.h"
+
+#include "purpleconnectionerrorinfo.h"
+
+struct _PurpleNotificationConnectionError {
+	PurpleNotification parent;
+
+	PurpleConnectionErrorInfo *info;
+};
+
+enum {
+	PROP_0,
+	PROP_ERROR_INFO,
+	N_PROPERTIES,
+	/* Overrides */
+	PROP_ACCOUNT,
+};
+static GParamSpec *properties[N_PROPERTIES] = {NULL, };
+
+static void
+purple_notification_connection_error_error_notify_cb(GObject *self,
+                                                     GParamSpec *pspec,
+                                                     gpointer data);
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+purple_notification_connection_error_update(PurpleNotification *notification) {
+	PurpleAccount *account = NULL;
+	const PurpleConnectionErrorInfo *error = NULL;
+	PurpleProtocol *protocol = NULL;
+	GObject *obj = G_OBJECT(notification);
+	char *title = NULL;
+	const char *icon_name = "network-error-symbolic";
+	const char *subtitle = NULL;
+
+	account = purple_notification_get_account(notification);
+	error = purple_account_get_error(account);
+
+	protocol = purple_account_get_protocol(account);
+	if(PURPLE_IS_PROTOCOL(protocol)) {
+		icon_name = purple_protocol_get_icon_name(protocol);
+	}
+
+	if(error == NULL) {
+		title = g_strdup_printf(_("%s has no connection error"),
+		                        purple_account_get_username(account));
+	} else {
+		title = g_strdup_printf(_("%s disconnected"),
+		                      purple_account_get_username(account));
+		subtitle = error->description;
+	}
+
+	g_object_freeze_notify(obj);
+	purple_notification_set_icon_name(notification, icon_name);
+	purple_notification_set_subtitle(notification, subtitle);
+	purple_notification_set_title(notification, title);
+	g_object_thaw_notify(obj);
+
+	g_clear_pointer(&title, g_free);
+}
+
+static void
+purple_notification_connection_error_set_error_info(PurpleNotificationConnectionError *error,
+                                                    PurpleConnectionErrorInfo *info)
+{
+	if(error->info == info) {
+		return;
+	}
+
+	g_clear_pointer(&error->info, purple_connection_error_info_free);
+	if(info != NULL) {
+		error->info = purple_connection_error_info_copy(info);
+	}
+
+	g_object_notify_by_pspec(G_OBJECT(error), properties[PROP_ERROR_INFO]);
+}
+
+static void
+purple_notification_connection_error_set_account(PurpleNotificationConnectionError *error,
+                                                 PurpleAccount *account)
+{
+	PurpleAccount *old = NULL;
+	PurpleNotification *notification = PURPLE_NOTIFICATION(error);
+
+	old = purple_notification_get_account(notification);
+	if(old == account) {
+		return;
+	}
+
+	if(PURPLE_IS_ACCOUNT(old)) {
+		g_signal_handlers_disconnect_by_func(old,
+		                                     purple_notification_connection_error_error_notify_cb,
+		                                     notification);
+	}
+
+	purple_notification_set_account(notification, account);
+
+	g_signal_connect_object(account, "notify::error",
+	                        G_CALLBACK(purple_notification_connection_error_error_notify_cb),
+	                        notification, G_CONNECT_DEFAULT);
+
+	purple_notification_connection_error_update(notification);
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_notification_connection_error_error_notify_cb(G_GNUC_UNUSED GObject *self,
+                                                     G_GNUC_UNUSED GParamSpec *pspec,
+                                                     gpointer data)
+{
+	purple_notification_connection_error_update(data);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_FINAL_TYPE(PurpleNotificationConnectionError,
+                    purple_notification_connection_error,
+                    PURPLE_TYPE_NOTIFICATION)
+
+static void
+purple_notification_connection_error_finalize(GObject *obj) {
+	PurpleNotificationConnectionError *error = NULL;
+
+	error = PURPLE_NOTIFICATION_CONNECTION_ERROR(obj);
+	g_clear_pointer(&error->info, purple_connection_error_info_free);
+
+	G_OBJECT_CLASS(purple_notification_connection_error_parent_class)->finalize(obj);
+}
+
+static void
+purple_notification_connection_error_get_property(GObject *obj, guint param_id,
+                                                  GValue *value,
+                                                  GParamSpec *pspec)
+{
+	PurpleNotification *notification = PURPLE_NOTIFICATION(obj);
+
+	switch(param_id) {
+	/* Overrides */
+	case PROP_ACCOUNT:
+		g_value_set_object(value,
+		                   purple_notification_get_account(notification));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+		break;
+	}
+}
+
+static void
+purple_notification_connection_error_set_property(GObject *obj, guint param_id,
+                                                  const GValue *value,
+                                                  GParamSpec *pspec)
+{
+	PurpleNotificationConnectionError *error = NULL;
+
+	error = PURPLE_NOTIFICATION_CONNECTION_ERROR(obj);
+
+	switch(param_id) {
+	case PROP_ERROR_INFO:
+		purple_notification_connection_error_set_error_info(error,
+		                                                    g_value_get_boxed(value));
+		break;
+	/* Overrides */
+	case PROP_ACCOUNT:
+		purple_notification_connection_error_set_account(error,
+		                                                 g_value_get_object(value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+		break;
+	}
+}
+
+static void
+purple_notification_connection_error_init(G_GNUC_UNUSED PurpleNotificationConnectionError *notification)
+{
+}
+
+static void
+purple_notification_connection_error_class_init(PurpleNotificationConnectionErrorClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->finalize = purple_notification_connection_error_finalize;
+	obj_class->get_property = purple_notification_connection_error_get_property;
+	obj_class->set_property = purple_notification_connection_error_set_property;
+
+	/**
+	 * PurpleNotificationConnectionError:info:
+	 *
+	 * The [type@ConnectionErrorInfo] that this notification is for.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_ERROR_INFO] = g_param_spec_boxed(
+		"info", NULL, NULL,
+		PURPLE_TYPE_CONNECTION_ERROR_INFO,
+		G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+	g_object_class_override_property(obj_class, PROP_ACCOUNT, "account");
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleNotification *
+purple_notification_connection_error_new(const char *id,
+                                         PurpleAccount *account,
+                                         PurpleConnectionErrorInfo *info)
+{
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+	g_return_val_if_fail(info != NULL, NULL);
+
+	return g_object_new(
+		PURPLE_TYPE_NOTIFICATION_CONNECTION_ERROR,
+		"id", id,
+		"account", account,
+		"info", info,
+		NULL);
+}
diff --git a/libpurple/purplenotificationconnectionerror.h b/libpurple/purplenotificationconnectionerror.h
new file mode 100644
--- /dev/null
+++ b/libpurple/purplenotificationconnectionerror.h
@@ -0,0 +1,73 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This library 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 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This library 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 library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <purple.h> may be included directly"
+#endif
+
+#ifndef PURPLE_NOTIFICATION_CONNECTION_ERROR_H
+#define PURPLE_NOTIFICATION_CONNECTION_ERROR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "purpleconnectionerrorinfo.h"
+#include "purplenotification.h"
+#include "purpleversion.h"
+
+G_BEGIN_DECLS
+
+/**
+ * PurpleNotificationConnectionError:
+ *
+ * A [class@Notification] for connection errors on [class@Account].
+ *
+ * Since: 3.0
+ */
+
+#define PURPLE_TYPE_NOTIFICATION_CONNECTION_ERROR (purple_notification_connection_error_get_type())
+
+PURPLE_AVAILABLE_IN_3_0
+G_DECLARE_FINAL_TYPE(PurpleNotificationConnectionError,
+                     purple_notification_connection_error, PURPLE,
+                     NOTIFICATION_CONNECTION_ERROR, PurpleNotification)
+
+#include "purpleaccount.h"
+
+/**
+ * purple_notification_connection_error_new:
+ * @id: (nullable): An identifier for this notification.
+ * @account: The account.
+ * @info: The [type@ConnectionErrorInfo] of the error.
+ *
+ * Creates a new [class@Notification] for a connection error on @account.
+ *
+ * Returns: (nullable): The new notification.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+PurpleNotification *purple_notification_connection_error_new(const char *id, PurpleAccount *account, PurpleConnectionErrorInfo *info);
+
+G_END_DECLS
+
+#endif /* PURPLE_NOTIFICATION_CONNECTION_ERROR_H */
diff --git a/libpurple/purplenotificationmanager.c b/libpurple/purplenotificationmanager.c
--- a/libpurple/purplenotificationmanager.c
+++ b/libpurple/purplenotificationmanager.c
@@ -24,6 +24,7 @@
 
 #include "purplenotificationmanager.h"
 
+#include "purplenotificationconnectionerror.h"
 #include "purpleprivate.h"
 
 enum {
@@ -508,7 +509,6 @@
 	for(pos = 0; pos < manager->notifications->len; pos++) {
 		PurpleAccount *account2 = NULL;
 		PurpleNotification *notification = NULL;
-		PurpleNotificationType type;
 		gboolean can_remove = TRUE;
 
 		notification = g_ptr_array_index(manager->notifications, pos);
@@ -516,10 +516,7 @@
 		/* If the notification's type is connection error, set can_remove to
 		 * the value of the all parameter.
 		 */
-		G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-		type = purple_notification_get_notification_type(notification);
-		G_GNUC_END_IGNORE_DEPRECATIONS
-		if(type == PURPLE_NOTIFICATION_TYPE_CONNECTION_ERROR) {
+		if(PURPLE_IS_NOTIFICATION_CONNECTION_ERROR(notification)) {
 			can_remove = all;
 		}
 
diff --git a/libpurple/tests/test_notification_manager.c b/libpurple/tests/test_notification_manager.c
--- a/libpurple/tests/test_notification_manager.c
+++ b/libpurple/tests/test_notification_manager.c
@@ -284,7 +284,8 @@
 	/* Add a connection error notification with the account. */
 	info = purple_connection_error_info_new(PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 	                                        "the network is borked.");
-	notification = purple_notification_new_from_connection_error(account, info);
+	notification = purple_notification_connection_error_new(NULL, account,
+	                                                        info);
 	purple_connection_error_info_free(info);
 	purple_notification_manager_add(manager, notification);
 	g_clear_object(&notification);
@@ -301,27 +302,24 @@
 	g_clear_object(&notification);
 
 	/* Verify that we have all of the notifications in the manager. */
-	g_assert_cmpuint(4, ==, g_list_model_get_n_items(model));
+	g_assert_cmpuint(g_list_model_get_n_items(model), ==, 4);
 
 	/* Remove the transient notifications for the account. */
 	purple_notification_manager_remove_with_account(manager, account, FALSE);
-	g_assert_cmpuint(2, ==, g_list_model_get_n_items(model));
+	g_assert_cmpuint(g_list_model_get_n_items(model), ==, 2);
 
 	/* Make sure that the second item is the connection error. */
 	notification = g_list_model_get_item(G_LIST_MODEL(model), 1);
-	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-	g_assert_cmpint(purple_notification_get_notification_type(notification),
-	                ==, PURPLE_NOTIFICATION_TYPE_CONNECTION_ERROR);
-	G_GNUC_END_IGNORE_DEPRECATIONS
+	g_assert_true(PURPLE_IS_NOTIFICATION_CONNECTION_ERROR(notification));
 	g_clear_object(&notification);
 
 	/* Remove the non-transient notifications for the account. */
 	purple_notification_manager_remove_with_account(manager, account, TRUE);
-	g_assert_cmpuint(1, ==, g_list_model_get_n_items(model));
+	g_assert_cmpuint(g_list_model_get_n_items(model), ==, 1);
 
 	/* Remove the generic notification that's not tied to an account. */
 	purple_notification_manager_clear(manager);
-	g_assert_cmpuint(0, ==, g_list_model_get_n_items(model));
+	g_assert_cmpuint(g_list_model_get_n_items(model), ==, 0);
 
 	g_assert_finalize_object(manager);
 	g_assert_finalize_object(account);
