From 500093ef89387aead8be8086e33c05fa250446e9 Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Wed, 7 Jan 2009 15:49:08 -0600 Subject: [PATCH] Handle the case where a user's id changes in a chatroom Telepathy-glib has a enum value for the MembersChanged signal to signify that a user's ID has changed. Previously, empathy was simply interpreting this as if a user with the old name had left the chat and a different user with the new name had entered the chat. This change handles this case more gracefully by updating the contact's id (and name) when this change reason is present One thing that does not yet work with this patch is if you are engaged in a private chat with a person and they change their nick in the middle of the chat. Then the EmpathyContact* that you are chatting with is no longer the EmpathyContact* representing the remote user, so messages won't be delivered properly. When we detect that a user has been 'renamed', we probably need to somehow go through all of the private chats with that person and swap out the old (invalid) EmpathyContact* and replace it with the new one so that the chat can continue without interruption. --- libempathy-gtk/empathy-chat.c | 29 +++++++ libempathy-gtk/empathy-contact-list-store.c | 56 ++++++++++++ libempathy/empathy-contact-list.c | 9 ++ libempathy/empathy-tp-chat.c | 95 +++++++++++++++++++++ 4 files changed, 189 insertions(+) diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c index 07bd3571..cc946b1e 100644 --- a/libempathy-gtk/empathy-chat.c +++ b/libempathy-gtk/empathy-chat.c @@ -1791,6 +1791,8 @@ chat_members_changed_cb (EmpathyTpChat *tp_chat, gboolean is_member, EmpathyChat *chat) { + g_return_if_fail (TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED != reason); + EmpathyChatPriv *priv = GET_PRIV (chat); const gchar *name = empathy_contact_get_name (contact); gchar *str; @@ -1809,6 +1811,30 @@ chat_members_changed_cb (EmpathyTpChat *tp_chat, g_free (str); } +static void +chat_member_renamed_cb (EmpathyTpChat *tp_chat, + EmpathyContact *old_contact, + EmpathyContact *new_contact, + guint reason, + gchar *message, + EmpathyChat *chat) +{ + g_return_if_fail (TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED == reason); + + EmpathyChatPriv *priv = GET_PRIV (chat); + + if (priv->block_events_timeout_id == 0) { + gchar *str; + + str = g_strdup_printf (_("%s is now known as %s"), + empathy_contact_get_name (old_contact), + empathy_contact_get_name (new_contact)); + empathy_chat_view_append_event (chat->view, str); + g_free (str); + } + +} + static gboolean chat_reset_size_request (gpointer widget) { @@ -2536,6 +2562,9 @@ empathy_chat_set_tp_chat (EmpathyChat *chat, g_signal_connect (tp_chat, "members-changed", G_CALLBACK (chat_members_changed_cb), chat); + g_signal_connect (tp_chat, "member-renamed", + G_CALLBACK (chat_member_renamed_cb), + chat); g_signal_connect_swapped (tp_chat, "notify::remote-contact", G_CALLBACK (chat_remote_contact_changed_cb), chat); diff --git a/libempathy-gtk/empathy-contact-list-store.c b/libempathy-gtk/empathy-contact-list-store.c index 6572b4c9..417250fc 100644 --- a/libempathy-gtk/empathy-contact-list-store.c +++ b/libempathy-gtk/empathy-contact-list-store.c @@ -103,6 +103,12 @@ static void contact_list_store_members_changed_cb (EmpathyCon gchar *message, gboolean is_member, EmpathyContactListStore *store); +static void contact_list_store_member_renamed_cb (EmpathyContactList *list_iface, + EmpathyContact *old_contact, + EmpathyContact *new_contact, + guint reason, + gchar *message, + EmpathyContactListStore *store); static void contact_list_store_groups_changed_cb (EmpathyContactList *list_iface, EmpathyContact *contact, gchar *group, @@ -174,6 +180,10 @@ contact_list_store_iface_setup (gpointer user_data) GList *contacts, *l; /* Signal connection. */ + g_signal_connect (priv->list, + "member-renamed", + G_CALLBACK (contact_list_store_member_renamed_cb), + store); g_signal_connect (priv->list, "members-changed", G_CALLBACK (contact_list_store_members_changed_cb), @@ -308,6 +318,9 @@ contact_list_store_dispose (GObject *object) } g_list_free (contacts); + g_signal_handlers_disconnect_by_func (priv->list, + G_CALLBACK (contact_list_store_member_renamed_cb), + object); g_signal_handlers_disconnect_by_func (priv->list, G_CALLBACK (contact_list_store_members_changed_cb), object); @@ -834,6 +847,49 @@ contact_list_store_members_changed_cb (EmpathyContactList *list_iface, } } +static void +contact_list_store_member_renamed_cb (EmpathyContactList *list_iface, + EmpathyContact *old_contact, + EmpathyContact *new_contact, + guint reason, + gchar *message, + EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (store); + + DEBUG ("Contact %s (%d) renamed to %s (%d)", + empathy_contact_get_id (old_contact), + empathy_contact_get_handle (old_contact), + empathy_contact_get_id (new_contact), + empathy_contact_get_handle (new_contact)); + + /* connect to signals of new contact */ + g_signal_connect (new_contact, "notify::presence", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (new_contact, "notify::presence-message", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (new_contact, "notify::name", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (new_contact, "notify::avatar", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (new_contact, "notify::capabilities", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + contact_list_store_add_contact (store, new_contact); + + /* disconnect from old contact */ + g_signal_handlers_disconnect_by_func (old_contact, + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + contact_list_store_remove_contact (store, old_contact); +} + static void contact_list_store_groups_changed_cb (EmpathyContactList *list_iface, EmpathyContact *contact, diff --git a/libempathy/empathy-contact-list.c b/libempathy/empathy-contact-list.c index d9493af1..d4859210 100644 --- a/libempathy/empathy-contact-list.c +++ b/libempathy/empathy-contact-list.c @@ -54,6 +54,15 @@ contact_list_base_init (gpointer klass) static gboolean initialized = FALSE; if (!initialized) { + g_signal_new ("member-renamed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _empathy_marshal_VOID__OBJECT_OBJECT_UINT_STRING, + G_TYPE_NONE, + 4, EMPATHY_TYPE_CONTACT, EMPATHY_TYPE_CONTACT, G_TYPE_UINT, G_TYPE_STRING); + g_signal_new ("members-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c index aa8c1a1c..aebeb667 100644 --- a/libempathy/empathy-tp-chat.c +++ b/libempathy/empathy-tp-chat.c @@ -946,6 +946,83 @@ chat_lookup_contact (EmpathyTpChat *chat, return NULL; } +typedef struct +{ + TpHandle old_handle; + guint reason; + const gchar *message; +} ContactRenameData; + +static ContactRenameData* +contact_rename_data_new (TpHandle handle, + guint reason, + const gchar* message) +{ + ContactRenameData *data = g_new (ContactRenameData, 1); + data->old_handle = handle; + data->reason = reason; + data->message = message; + + return data; +} + +static void +contact_rename_data_free (ContactRenameData* data) +{ + g_free (data); +} + +static void +tp_chat_got_renamed_contacts_cb (EmpathyTpContactFactory *factory, + guint n_contacts, + EmpathyContact * const * contacts, + guint n_failed, + const TpHandle *failed, + const GError *error, + gpointer user_data, + GObject *chat) +{ + EmpathyTpChatPriv *priv = GET_PRIV (chat); + const TpIntSet *members; + TpHandle handle; + EmpathyContact *old = NULL, *new = NULL; + + if (error) { + DEBUG ("Error: %s", error->message); + return; + } + + /* renamed members can only be delivered one at a time */ + g_warn_if_fail (n_contacts == 1); + + ContactRenameData *rename_data = (ContactRenameData*) user_data; + + new = contacts[0]; + + members = tp_channel_group_get_members (priv->channel); + handle = empathy_contact_get_handle (new); + + old = chat_lookup_contact (EMPATHY_TP_CHAT (chat), + rename_data->old_handle, TRUE); + + /* Make sure the contact is still member */ + if (tp_intset_is_member (members, handle)) { + priv->members = g_list_prepend (priv->members, + g_object_ref (new)); + + if (old != NULL) { + g_signal_emit_by_name (chat, "member-renamed", + old, new, rename_data->reason, + rename_data->message); + g_object_unref (old); + } + } + + tp_chat_update_remote_contact (EMPATHY_TP_CHAT (chat)); + tp_chat_check_if_ready (EMPATHY_TP_CHAT (chat)); +} + + static void tp_chat_group_members_changed_cb (TpChannel *self, gchar *message, @@ -961,6 +1038,24 @@ tp_chat_group_members_changed_cb (TpChannel *self, EmpathyContact *contact; EmpathyContact *actor_contact = NULL; guint i; + ContactRenameData *rename_data; + + /* Contact renamed */ + if (reason == TP_CHANNEL_GROUP_CHANGE_REASON_RENAMED) { + /* there can only be a single 'added' and a single 'removed' handle */ + g_warn_if_fail(removed->len == 1); + g_warn_if_fail(added->len == 1); + + TpHandle old_handle = g_array_index (removed, guint, 0); + + rename_data = contact_rename_data_new (old_handle, reason, message); + empathy_tp_contact_factory_get_from_handles (priv->factory, + added->len, (TpHandle *) added->data, + tp_chat_got_renamed_contacts_cb, + rename_data, (GDestroyNotify) contact_rename_data_free, + G_OBJECT (chat)); + return; + } if (actor != 0) { actor_contact = chat_lookup_contact (chat, actor, FALSE); -- 2.39.2