about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/command/command.c31
-rw-r--r--src/contact.c109
-rw-r--r--src/contact.h3
-rw-r--r--src/muc.c6
4 files changed, 112 insertions, 37 deletions
diff --git a/src/command/command.c b/src/command/command.c
index ae3d51d6..73c8b07a 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -1539,9 +1539,7 @@ _cmd_who(gchar **args, struct cmd_help_t help)
 
                     while (list != NULL) {
                         PContact contact = list->data;
-                        const char * const contact_presence = (p_contact_presence(contact));
-                        if ((strcmp(contact_presence, "online") == 0)
-                                || (strcmp(contact_presence, "chat") == 0)) {
+                        if (p_contact_is_available(contact)) {
                             filtered = g_list_append(filtered, contact);
                         }
                         list = g_list_next(list);
@@ -1555,11 +1553,7 @@ _cmd_who(gchar **args, struct cmd_help_t help)
 
                     while (list != NULL) {
                         PContact contact = list->data;
-                        const char * const contact_presence = (p_contact_presence(contact));
-                        if ((strcmp(contact_presence, "offline") == 0)
-                                || (strcmp(contact_presence, "away") == 0)
-                                || (strcmp(contact_presence, "dnd") == 0)
-                                || (strcmp(contact_presence, "xa") == 0)) {
+                        if (!p_contact_is_available(contact)) {
                             filtered = g_list_append(filtered, contact);
                         }
                         list = g_list_next(list);
@@ -1573,12 +1567,7 @@ _cmd_who(gchar **args, struct cmd_help_t help)
 
                     while (list != NULL) {
                         PContact contact = list->data;
-                        const char * const contact_presence = (p_contact_presence(contact));
-                        if ((strcmp(contact_presence, "online") == 0)
-                                || (strcmp(contact_presence, "away") == 0)
-                                || (strcmp(contact_presence, "dnd") == 0)
-                                || (strcmp(contact_presence, "xa") == 0)
-                                || (strcmp(contact_presence, "chat") == 0)) {
+                        if (p_contact_has_available_resource(contact)) {
                             filtered = g_list_append(filtered, contact);
                         }
                         list = g_list_next(list);
@@ -1586,6 +1575,20 @@ _cmd_who(gchar **args, struct cmd_help_t help)
 
                     win_show_room_roster(room, filtered, "online");
 
+                // online, show all status that indicate online
+                } else if (strcmp("offline", presence) == 0) {
+                    GList *filtered = NULL;
+
+                    while (list != NULL) {
+                        PContact contact = list->data;
+                        if (!p_contact_has_available_resource(contact)) {
+                            filtered = g_list_append(filtered, contact);
+                        }
+                        list = g_list_next(list);
+                    }
+
+                    win_show_room_roster(room, filtered, "offline");
+
                 // show specific status
                 } else {
                     GList *filtered = NULL;
diff --git a/src/contact.c b/src/contact.c
index 372ccf5f..ece79ca9 100644
--- a/src/contact.c
+++ b/src/contact.c
@@ -66,15 +66,6 @@ p_contact_new(const char * const barejid, const char * const name,
     return contact;
 }
 
-void
-p_contact_add_resource(PContact contact, Resource *resource)
-{
-    assert(contact != NULL);
-    assert(resource != NULL);
-
-    g_hash_table_insert(contact->available_resources, strdup(resource->name), resource);
-}
-
 gboolean
 p_contact_remove_resource(PContact contact, const char * const resource)
 {
@@ -132,26 +123,78 @@ p_contact_name(const PContact contact)
     return contact->name;
 }
 
+static resource_presence_t
+_highest_presence(resource_presence_t first, resource_presence_t second)
+{
+    if (first == RESOURCE_CHAT) {
+        return first;
+    } else if (second == RESOURCE_CHAT) {
+        return second;
+    } else if (first == RESOURCE_ONLINE) {
+        return first;
+    } else if (second == RESOURCE_ONLINE) {
+        return second;
+    } else if (first == RESOURCE_AWAY) {
+        return first;
+    } else if (second == RESOURCE_AWAY) {
+        return second;
+    } else if (first == RESOURCE_XA) {
+        return first;
+    } else if (second == RESOURCE_XA) {
+        return second;
+    } else {
+        return first;
+    }
+}
+
 const char *
 p_contact_presence(const PContact contact)
 {
+    assert(contact != NULL);
+
+    // no available resources, offline
     if (g_hash_table_size(contact->available_resources) == 0) {
         return "offline";
-    } else {
-        Resource *resource = g_hash_table_lookup(contact->available_resources, "default");
-        return string_from_resource_presence(resource->presence);
     }
+
+    // find resource with highest priority, if more than one,
+    // use highest availability, in the following order:
+    //      chat
+    //      online
+    //      away
+    //      xa
+    //      dnd
+    GList *resources = g_hash_table_get_values(contact->available_resources);
+    Resource *resource = resources->data;
+    int highest_priority = resource->priority;
+    resource_presence_t presence = resource->presence;
+    resources = g_list_next(resources);
+    while (resources != NULL) {
+        resource = resources->data;
+
+        // priority is same as current highest, choose presence
+        if (resource->priority == highest_priority) {
+            presence = _highest_presence(presence, resource->presence);
+
+        // priority higher than current highest, set new presence
+        } else if (resource->priority > highest_priority) {
+            highest_priority = resource->priority;
+            presence = resource->presence;
+        }
+
+        resources = g_list_next(resources);
+    }
+
+    return string_from_resource_presence(presence);
 }
 
 const char *
-p_contact_status(const PContact contact)
+p_contact_status(const PContact contact, const char * const resource)
 {
-    if (g_hash_table_size(contact->available_resources) == 0) {
-        return NULL;
-    } else {
-        Resource *resource = g_hash_table_lookup(contact->available_resources, "default");
-        return resource->status;
-    }
+    assert(contact != NULL);
+    assert(resource != NULL);
+    Resource *resourcep = g_hash_table_lookup(contact->available_resources, "default");
+    return resourcep->status;
 }
 
 const char *
@@ -183,6 +226,34 @@ p_contact_caps_str(const PContact contact)
     }
 }
 
+gboolean
+p_contact_is_available(const PContact contact)
+{
+    // no available resources, unavailable
+    if (g_hash_table_size(contact->available_resources) == 0) {
+        return FALSE;
+    }
+
+    // if any resource is CHAT or ONLINE, available
+    GList *resources = g_hash_table_get_values(contact->available_resources);
+    while (resources != NULL) {
+        Resource *resource = resources->data;
+        resource_presence_t presence = resource->presence;
+        if ((presence == RESOURCE_ONLINE) || (presence == RESOURCE_CHAT)) {
+            return TRUE;
+        }
+        resources = g_list_next(resources);
+    }
+
+    return FALSE;
+}
+
+gboolean
+p_contact_has_available_resource(const PContact contact)
+{
+    return (g_hash_table_size(contact->available_resources) > 0);
+}
+
 void
 p_contact_set_presence(const PContact contact, Resource *resource)
 {
diff --git a/src/contact.h b/src/contact.h
index 0b376d81..91bc1024 100644
--- a/src/contact.h
+++ b/src/contact.h
@@ -37,7 +37,7 @@ void p_contact_free(PContact contact);
 const char* p_contact_barejid(PContact contact);
 const char* p_contact_name(PContact contact);
 const char* p_contact_presence(PContact contact);
-const char* p_contact_status(PContact contact);
+const char* p_contact_status(PContact contact, const char * const resource);
 const char* p_contact_subscription(const PContact contact);
 const char* p_contact_caps_str(const PContact contact);
 GDateTime* p_contact_last_activity(const PContact contact);
@@ -48,5 +48,6 @@ void p_contact_set_subscription(const PContact contact, const char * const subsc
 void p_contact_set_caps_str(const PContact contact, const char * const caps_str);
 void p_contact_set_pending_out(const PContact contact, gboolean pending_out);
 void p_contact_set_last_activity(const PContact contact, GDateTime *last_activity);
+gboolean p_contact_is_available(const PContact contact);
 
 #endif
diff --git a/src/muc.c b/src/muc.c
index a59ac96b..778b2ce3 100644
--- a/src/muc.c
+++ b/src/muc.c
@@ -217,13 +217,13 @@ muc_add_to_roster(const char * const room, const char * const nick,
             updated = TRUE;
             autocomplete_add(chat_room->nick_ac, strdup(nick));
         } else if ((g_strcmp0(p_contact_presence(old), show) != 0) ||
-                    (g_strcmp0(p_contact_status(old), status) != 0)) {
+                    (g_strcmp0(p_contact_status(old, nick), status) != 0)) {
             updated = TRUE;
         }
         PContact contact = p_contact_new(nick, NULL, NULL, FALSE);
         resource_presence_t resource_presence = resource_presence_from_string(show);
-        Resource *resource = resource_new("default", resource_presence, status, 0, caps_str);
-        p_contact_add_resource(contact, resource);
+        Resource *resource = resource_new(nick, resource_presence, status, 0, caps_str);
+        p_contact_set_presence(contact, resource);
         g_hash_table_replace(chat_room->roster, strdup(nick), contact);
     }