aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard van der Hoff <richard@matrix.org>2015-10-26 11:53:52 +0000
committerRichard van der Hoff <richard@matrix.org>2015-10-26 11:53:52 +0000
commite2500a76ee35cb3be863342573495a28f47c5eb8 (patch)
tree0a5ca502cfe97e6669468ebedbccc29f0576fe78
parente6880c64f78a505e07c2ba9feb6397d304b5ed27 (diff)
downloadpurple-matrix-e2500a76ee35cb3be863342573495a28f47c5eb8.tar.gz
Better room names for one-to-one chats
Give rooms a name according to who's in it, if there is no proper name.
-rw-r--r--matrix-connection.c5
-rw-r--r--matrix-connection.h1
-rw-r--r--matrix-room.c150
-rw-r--r--matrix-room.h5
-rw-r--r--matrix-sync.c3
5 files changed, 149 insertions, 15 deletions
diff --git a/matrix-connection.c b/matrix-connection.c
index 3481287..e891818 100644
--- a/matrix-connection.c
+++ b/matrix-connection.c
@@ -61,6 +61,9 @@ void matrix_connection_free(PurpleConnection *pc)
g_free(conn->access_token);
conn->access_token = NULL;
+ g_free(conn->user_id);
+ conn->user_id = NULL;
+
conn->pc = NULL;
g_free(conn);
@@ -150,6 +153,8 @@ static void _login_completed(MatrixConnectionData *conn,
return;
}
conn->access_token = g_strdup(access_token);
+ conn->user_id = g_strdup(matrix_json_object_get_string_member(root_obj,
+ "user_id"));
/* TODO: there may be rooms which came through on a previous connection,
* but for which we never got the state table. We should update them here
diff --git a/matrix-connection.h b/matrix-connection.h
index bdf1148..9e9c166 100644
--- a/matrix-connection.h
+++ b/matrix-connection.h
@@ -33,6 +33,7 @@ struct _PurpleConnection;
typedef struct _MatrixConnectionData {
struct _PurpleConnection *pc;
gchar *homeserver; /* hostname (:port) of the homeserver */
+ gchar *user_id; /* our full user id ("@user:server") */
gchar *access_token; /* access token corresponding to our user */
/* the active sync request */
diff --git a/matrix-room.c b/matrix-room.c
index 067ac0a..632bf29 100644
--- a/matrix-room.c
+++ b/matrix-room.c
@@ -185,7 +185,7 @@ static void _state_update_complete(MatrixConnectionData *conn,
response_array = matrix_json_node_get_array(json_root);
if(response_array != NULL)
json_array_foreach_element(response_array, _parse_state_event, conv);
- matrix_room_update_buddy_list(conv);
+ matrix_room_update_buddy_list(conn, conv);
}
@@ -218,46 +218,167 @@ static MatrixRoomEvent *matrix_room_get_state_event(
return (MatrixRoomEvent *)g_hash_table_lookup(tmp, state_key);
}
+
+/**
+ * given the userid of a room member, return their display name
+ *
+ * @returns a string, which should be freed
+ */
+static gchar *_get_display_name_for_member(
+ MatrixRoomStateEventTable *state_table, const gchar *member_id)
+{
+ MatrixRoomEvent *event;
+ const gchar *displayname = NULL;
+
+ /* find this user's m.room.member event */
+ event = matrix_room_get_state_event(state_table, "m.room.member",
+ member_id);
+
+ if (event != NULL)
+ displayname = matrix_json_object_get_string_member(
+ event->content, "displayname");
+
+ if (displayname == NULL)
+ return g_strdup(member_id);
+
+ /* TODO: if there is more than one user with this displayname in this room,
+ * we need to disambiguate. But we need to do it without introducing an
+ * O(N^2) algorithm.
+ */
+
+ return g_strdup(displayname);
+}
+
+
+/**
+ * Get a list of the members in a room
+ *
+ * @returns a GList of the user ids. Don't free the pointers, but do free
+ * the list.
+ */
+static GList *_get_room_members(MatrixRoomStateEventTable *state_table)
+{
+ GHashTable *tmp;
+ GHashTableIter iter;
+ gpointer key, value;
+ GList *members = NULL; /* a list of user ids */
+
+ tmp = (GHashTable *) g_hash_table_lookup(state_table, "m.room.member");
+ if(tmp == NULL) {
+ // no member table yet
+ return NULL;
+ }
+
+ g_hash_table_iter_init(&iter, tmp);
+
+ while(g_hash_table_iter_next(&iter, &key, &value)) {
+ MatrixRoomEvent *event = value;
+ const gchar *membership;
+
+ membership = matrix_json_object_get_string_member(
+ event->content, "membership");
+ if(membership != NULL && strcmp(membership, "leave") == 0) {
+ /* not actually a member here */
+ continue;
+ }
+ members = g_list_append(members, key);
+ }
+
+ return members;
+}
+
+/**
+ * figure out the best name for a room based on its members list
+ *
+ * @returns a string which should be freedd
+ */
+static gchar *_get_room_name_from_members(MatrixConnectionData *conn,
+ MatrixRoomStateEventTable *state_table)
+{
+ GList *members, *tmp;
+ gchar *member1, *res;
+
+ members = _get_room_members(state_table);
+
+ /* remove ourselves from the list */
+ tmp = g_list_find_custom(members, conn->user_id, (GCompareFunc)strcmp);
+ if(tmp != NULL) {
+ members = g_list_delete_link(members, tmp);
+ }
+
+ if(members == NULL) {
+ /* nobody else here. Self-chat or an invitation. TODO: improve this
+ */
+ return g_strdup("invitation");
+ }
+
+ member1 = _get_display_name_for_member(state_table, members->data);
+
+ if(members->next == NULL) {
+ /* one other person */
+ res = member1;
+ } else if(members->next->next == NULL) {
+ /* two other people */
+ gchar *member2 = _get_display_name_for_member(state_table,
+ members->next->data);
+ res = g_strdup_printf(_("%s and %s"), member1, member2);
+ g_free(member1);
+ g_free(member2);
+ } else {
+ int nmembers = g_list_length(members);
+ res = g_strdup_printf(_("%s and %i others"), member1, nmembers);
+ g_free(member1);
+ }
+
+ g_list_free(members);
+ return res;
+}
+
/**
* figure out the best name for a room
+ *
+ * @returns a string which should be freed
*/
-static const char *matrix_room_get_name(MatrixRoomStateEventTable *state_table)
+static char *matrix_room_get_name(MatrixConnectionData *conn,
+ MatrixRoomStateEventTable *state_table)
{
GHashTable *tmp;
MatrixRoomEvent *event;
+ const gchar *tmpname = NULL;
/* start by looking for the official room name */
event = matrix_room_get_state_event(state_table, "m.room.name", "");
if(event != NULL) {
- const gchar *tmpname = matrix_json_object_get_string_member(
+ tmpname = matrix_json_object_get_string_member(
event->content, "name");
if(tmpname != NULL) {
- return tmpname;
+ return g_strdup(tmpname);
}
}
+
/* look for an alias */
tmp = (GHashTable *) g_hash_table_lookup(state_table, "m.room.aliases");
if(tmp != NULL) {
GHashTableIter iter;
- g_hash_table_iter_init(&iter, tmp);
gpointer key, value;
+
+ g_hash_table_iter_init(&iter, tmp);
while(g_hash_table_iter_next(&iter, &key, &value)) {
MatrixRoomEvent *event = value;
JsonArray *array = matrix_json_object_get_array_member(
event->content, "aliases");
if(array != NULL && json_array_get_length(array) > 0) {
- const gchar *tmpname = matrix_json_array_get_string_element(array, 0);
+ tmpname = matrix_json_array_get_string_element(array, 0);
if(tmpname != NULL) {
- return tmpname;
+ return g_strdup(tmpname);
}
}
}
}
- /* TODO: look for room members, and pick a name based on that */
-
- return "unknown";
+ /* look for room members, and pick a name based on that */
+ return _get_room_name_from_members(conn, state_table);
}
@@ -522,9 +643,11 @@ void matrix_room_leave_chat(PurpleConversation *conv)
*
* @param conv: conversation info
*/
-void matrix_room_update_buddy_list(PurpleConversation *conv)
+void matrix_room_update_buddy_list(MatrixConnectionData *conn,
+ PurpleConversation *conv)
{
- const gchar *room_id, *room_name;
+ const gchar *room_id;
+ gchar *room_name;
PurpleChat *chat;
room_id = conv->name;
@@ -551,8 +674,9 @@ void matrix_room_update_buddy_list(PurpleConversation *conv)
purple_blist_add_chat(chat, group, NULL);
}
- room_name = matrix_room_get_name(matrix_room_get_state_table(conv));
+ room_name = matrix_room_get_name(conn, matrix_room_get_state_table(conv));
purple_blist_alias_chat(chat, room_name);
+ g_free(room_name);
}
diff --git a/matrix-room.h b/matrix-room.h
index b454653..dd1da06 100644
--- a/matrix-room.h
+++ b/matrix-room.h
@@ -34,14 +34,17 @@
struct _PurpleConversation;
struct _PurpleConnection;
+struct _MatrixConnectionData;
/**
* Ensure the room is up to date in the buddy list (ie, it is present,
* and the alias is correct)
*
+ * @param conn connection data for the account
* @param conv conversation info
*/
-void matrix_room_update_buddy_list(struct _PurpleConversation *conv);
+void matrix_room_update_buddy_list(struct _MatrixConnectionData *conn,
+ struct _PurpleConversation *conv);
/**
* If this is an active conversation, return it; otherwise, create it anew.
diff --git a/matrix-sync.c b/matrix-sync.c
index 5386917..8cb8acd 100644
--- a/matrix-sync.c
+++ b/matrix-sync.c
@@ -115,7 +115,8 @@ static void matrix_sync_room(const gchar *room_id,
_parse_room_event_array(conv, timeline_array, event_map, FALSE);
/* ensure the buddy list is up to date*/
- matrix_room_update_buddy_list(conv);
+ matrix_room_update_buddy_list(purple_connection_get_protocol_data(pc),
+ conv);
}