about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2015-01-11 20:20:17 +0000
committerJames Booth <boothj5@gmail.com>2015-01-11 20:20:17 +0000
commitc16871d143a00eeb04d40e7b4ab20590b8ae93c6 (patch)
treebba5887d69f2c1ac17ad30a11dfade2419ef9418
parent76bd2ec13f5ffc21ddf91eebf3cb2e6dafe3ad75 (diff)
downloadprofani-tty-c16871d143a00eeb04d40e7b4ab20590b8ae93c6.tar.gz
Added remaining chat states
-rw-r--r--Makefile.am2
-rw-r--r--src/chat_state.c169
-rw-r--r--src/chat_state.h61
-rw-r--r--src/profanity.c66
-rw-r--r--src/server_events.c59
-rw-r--r--src/server_events.h7
-rw-r--r--src/ui/core.c52
-rw-r--r--src/ui/inputwin.c24
-rw-r--r--src/ui/ui.h2
-rw-r--r--src/ui/window.c2
-rw-r--r--src/ui/window.h2
-rw-r--r--src/xmpp/message.c53
-rw-r--r--tests/ui/stub_ui.c6
13 files changed, 400 insertions, 105 deletions
diff --git a/Makefile.am b/Makefile.am
index 3a5bf9ad..9d44c003 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,6 +3,7 @@ core_sources = \
 	src/log.h src/profanity.c src/common.h \
 	src/profanity.h src/chat_session.c \
 	src/chat_session.h src/muc.c src/muc.h src/jid.h src/jid.c \
+	src/chat_state.h src/chat_state.c \
 	src/resource.c src/resource.h \
 	src/roster_list.c src/roster_list.h \
 	src/xmpp/xmpp.h src/xmpp/capabilities.c src/xmpp/connection.c \
@@ -39,6 +40,7 @@ tests_sources = \
 	src/profanity.h src/chat_session.c \
 	src/chat_session.h src/muc.c src/muc.h src/jid.h src/jid.c \
 	src/resource.c src/resource.h \
+	src/chat_state.h src/chat_state.c \
 	src/roster_list.c src/roster_list.h \
 	src/xmpp/xmpp.h src/xmpp/form.c \
 	src/ui/ui.h \
diff --git a/src/chat_state.c b/src/chat_state.c
new file mode 100644
index 00000000..6b3434d3
--- /dev/null
+++ b/src/chat_state.c
@@ -0,0 +1,169 @@
+/*
+ * chat_state.c
+ *
+ * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "chat_state.h"
+#include "chat_session.h"
+#include "xmpp/xmpp.h"
+#include "config/preferences.h"
+
+#define PAUSED_TIMEOUT 10.0
+#define INACTIVE_TIMEOUT 10.0
+#define GONE_TIMEOUT 10.0
+
+static void _send_if_supported(const char * const barejid, void(*send_func)(const char * const));
+
+ChatState*
+chat_state_new(void)
+{
+    ChatState *new_state = malloc(sizeof(struct prof_chat_state_t));
+    new_state->type = CHAT_STATE_GONE;
+    new_state->timer = g_timer_new();
+
+    return new_state;
+}
+
+void
+chat_state_free(ChatState *state)
+{
+    if (state && state->timer!= NULL) {
+        g_timer_destroy(state->timer);
+    }
+    free(state);
+}
+
+void
+chat_state_handle_idle(const char * const barejid, ChatState *state)
+{
+    // TYPING -> PAUSED
+    if (state->type == CHAT_STATE_COMPOSING && g_timer_elapsed(state->timer, NULL) > PAUSED_TIMEOUT) {
+        state->type = CHAT_STATE_PAUSED;
+        g_timer_start(state->timer);
+        if (prefs_get_boolean(PREF_STATES) && prefs_get_boolean(PREF_OUTTYPE)) {
+            _send_if_supported(barejid, message_send_paused);
+        }
+        return;
+    }
+
+    // PAUSED|ACTIVE -> INACTIVE
+    if ((state->type == CHAT_STATE_PAUSED || state->type == CHAT_STATE_ACTIVE) && g_timer_elapsed(state->timer, NULL) > INACTIVE_TIMEOUT) {
+        state->type = CHAT_STATE_INACTIVE;
+        g_timer_start(state->timer);
+        if (prefs_get_boolean(PREF_STATES)) {
+            _send_if_supported(barejid, message_send_inactive);
+        }
+        return;
+
+    }
+
+    // INACTIVE -> GONE
+    if (state->type == CHAT_STATE_INACTIVE && g_timer_elapsed(state->timer, NULL) > GONE_TIMEOUT) {
+        ChatSession *session = chat_session_get(barejid);
+        if (session) {
+            // never move to GONE when resource override
+            if (!session->resource_override) {
+                if (prefs_get_boolean(PREF_STATES)) {
+                    _send_if_supported(barejid, message_send_gone);
+                }
+                chat_session_remove(barejid);
+                state->type = CHAT_STATE_GONE;
+                g_timer_start(state->timer);
+            }
+        } else {
+            if (prefs_get_boolean(PREF_STATES)) {
+                message_send_gone(barejid);
+            }
+            state->type = CHAT_STATE_GONE;
+            g_timer_start(state->timer);
+        }
+        return;
+    }
+}
+
+void
+chat_state_handle_typing(const char * const barejid, ChatState *state)
+{
+    // ACTIVE|INACTIVE|PAUSED|GONE -> COMPOSING
+    if (state->type != CHAT_STATE_COMPOSING) {
+        state->type = CHAT_STATE_COMPOSING;
+        g_timer_start(state->timer);
+        if (prefs_get_boolean(PREF_STATES) && prefs_get_boolean(PREF_OUTTYPE)) {
+            _send_if_supported(barejid, message_send_composing);
+        }
+    }
+}
+
+void
+chat_state_active(ChatState *state)
+{
+    state->type = CHAT_STATE_ACTIVE;
+    g_timer_start(state->timer);
+}
+
+void
+chat_state_gone(const char * const barejid, ChatState *state)
+{
+    if (state->type != CHAT_STATE_GONE) {
+        if (prefs_get_boolean(PREF_STATES)) {
+            _send_if_supported(barejid, message_send_gone);
+        }
+        state->type = CHAT_STATE_GONE;
+        g_timer_start(state->timer);
+    }
+}
+
+static void
+_send_if_supported(const char * const barejid, void(*send_func)(const char * const))
+{
+    gboolean send = TRUE;
+    GString *jid = g_string_new(barejid);
+    ChatSession *session = chat_session_get(barejid);
+    if (session) {
+        if (session->send_states) {
+            g_string_append(jid, "/");
+            g_string_append(jid, session->resource);
+        } else {
+            send = FALSE;
+        }
+    }
+
+    if (send) {
+        send_func(jid->str);
+    }
+
+    g_string_free(jid, TRUE);
+}
\ No newline at end of file
diff --git a/src/chat_state.h b/src/chat_state.h
new file mode 100644
index 00000000..ba394a56
--- /dev/null
+++ b/src/chat_state.h
@@ -0,0 +1,61 @@
+/*
+ * chat_state.h
+ *
+ * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef CHAT_STATE_H
+#define CHAT_STATE_H
+
+#include <glib.h>
+
+typedef enum {
+    CHAT_STATE_ACTIVE,
+    CHAT_STATE_COMPOSING,
+    CHAT_STATE_PAUSED,
+    CHAT_STATE_INACTIVE,
+    CHAT_STATE_GONE
+} chat_state_type_t;
+
+typedef struct prof_chat_state_t {
+    chat_state_type_t type;
+    GTimer *timer;
+} ChatState;
+
+ChatState* chat_state_new(void);
+void chat_state_free(ChatState *state);
+
+void chat_state_handle_idle(const char * const barejid, ChatState *state);
+void chat_state_handle_typing(const char * const barejid, ChatState *state);
+void chat_state_active(ChatState *state);
+void chat_state_gone(const char * const barejid, ChatState *state);
+
+#endif
diff --git a/src/profanity.c b/src/profanity.c
index e8342b65..467e4c2c 100644
--- a/src/profanity.c
+++ b/src/profanity.c
@@ -46,6 +46,7 @@
 
 #include "profanity.h"
 #include "chat_session.h"
+#include "chat_state.h"
 #include "config/accounts.h"
 #include "config/preferences.h"
 #include "config/theme.h"
@@ -131,38 +132,39 @@ prof_run(const int disable_tls, char *log_level, char *account_name)
 
     g_timer_destroy(timer);
 }
-//
-//void
-//prof_handle_idle(void)
-//{
-//    jabber_conn_status_t status = jabber_get_connection_status();
-//    if (status == JABBER_CONNECTED) {
-//        GSList *recipients = ui_get_chat_recipients();
-//        GSList *curr = recipients;
-//
-//        while (curr != NULL) {
-//            char *barejid = curr->data;
-//            chat_session_on_inactivity(barejid);
-//            curr = g_slist_next(curr);
-//        }
-//
-//        if (recipients != NULL) {
-//            g_slist_free(recipients);
-//        }
-//    }
-//}
-//
-//void
-//prof_handle_activity(void)
-//{
-//    win_type_t win_type = ui_current_win_type();
-//    jabber_conn_status_t status = jabber_get_connection_status();
-//
-//    if ((status == JABBER_CONNECTED) && (win_type == WIN_CHAT)) {
-//        ProfChatWin *chatwin = wins_get_current_chat();
-//        chat_session_on_activity(chatwin->barejid);
-//    }
-//}
+
+void
+prof_handle_idle(void)
+{
+    jabber_conn_status_t status = jabber_get_connection_status();
+    if (status == JABBER_CONNECTED) {
+        GSList *recipients = ui_get_chat_recipients();
+        GSList *curr = recipients;
+
+        while (curr != NULL) {
+            char *barejid = curr->data;
+            ProfChatWin *chatwin = wins_get_chat(barejid);
+            chat_state_handle_idle(chatwin->barejid, chatwin->state);
+            curr = g_slist_next(curr);
+        }
+
+        if (recipients != NULL) {
+            g_slist_free(recipients);
+        }
+    }
+}
+
+void
+prof_handle_activity(void)
+{
+    win_type_t win_type = ui_current_win_type();
+    jabber_conn_status_t status = jabber_get_connection_status();
+
+    if ((status == JABBER_CONNECTED) && (win_type == WIN_CHAT)) {
+        ProfChatWin *chatwin = wins_get_current_chat();
+        chat_state_handle_typing(chatwin->barejid, chatwin->state);
+    }
+}
 
 /*
  * Take a line of input and process it, return TRUE if profanity is to
diff --git a/src/server_events.c b/src/server_events.c
index b5291ce5..1caa0f1f 100644
--- a/src/server_events.c
+++ b/src/server_events.c
@@ -386,9 +386,28 @@ handle_delayed_message(char *barejid, char *message, GTimeVal tv_stamp)
 }
 
 void
-handle_typing(char *from)
+handle_typing(char *barejid, char *resource)
 {
-    ui_contact_typing(from);
+    if (ui_chat_win_exists(barejid)) {
+        chat_session_on_recipient_activity(barejid, resource, TRUE);
+    }
+    ui_contact_typing(barejid);
+}
+
+void
+handle_paused(char *barejid, char *resource)
+{
+    if (ui_chat_win_exists(barejid)) {
+        chat_session_on_recipient_activity(barejid, resource, TRUE);
+    }
+}
+
+void
+handle_inactive(char *barejid, char *resource)
+{
+    if (ui_chat_win_exists(barejid)) {
+        chat_session_on_recipient_activity(barejid, resource, TRUE);
+    }
 }
 
 void
@@ -399,6 +418,12 @@ handle_gone(const char * const barejid)
 }
 
 void
+handle_activity(const char * const barejid, const char * const resource, gboolean send_states)
+{
+    chat_session_on_recipient_activity(barejid, resource, send_states);
+}
+
+void
 handle_subscription(const char *barejid, jabber_subscr_t type)
 {
     switch (type) {
@@ -433,35 +458,7 @@ handle_contact_offline(char *barejid, char *resource, char *status)
     gboolean updated = roster_contact_offline(barejid, resource, status);
 
     if (resource != NULL && updated) {
-        char *show_console = prefs_get_string(PREF_STATUSES_CONSOLE);
-        char *show_chat_win = prefs_get_string(PREF_STATUSES_CHAT);
-        Jid *jid = jid_create_from_bare_and_resource(barejid, resource);
-        PContact contact = roster_get_contact(barejid);
-        if (p_contact_subscription(contact) != NULL) {
-            if (strcmp(p_contact_subscription(contact), "none") != 0) {
-
-                // show in console if "all"
-                if (g_strcmp0(show_console, "all") == 0) {
-                    cons_show_contact_offline(contact, resource, status);
-
-                // show in console of "online"
-                } else if (g_strcmp0(show_console, "online") == 0) {
-                    cons_show_contact_offline(contact, resource, status);
-                }
-
-                // show in chat win if "all"
-                if (g_strcmp0(show_chat_win, "all") == 0) {
-                    ui_chat_win_contact_offline(contact, resource, status);
-
-                // show in char win if "online" and presence online
-                } else if (g_strcmp0(show_chat_win, "online") == 0) {
-                    ui_chat_win_contact_offline(contact, resource, status);
-                }
-            }
-        }
-        prefs_free_string(show_console);
-        prefs_free_string(show_chat_win);
-        jid_destroy(jid);
+        ui_contact_offline(barejid, resource, status);
     }
 
     rosterwin_roster();
diff --git a/src/server_events.h b/src/server_events.h
index e803f945..a4776721 100644
--- a/src/server_events.h
+++ b/src/server_events.h
@@ -73,8 +73,11 @@ void handle_incoming_message(char *barejid, char *resource, char *message);
 void handle_incoming_private_message(char *fulljid, char *message);
 void handle_delayed_message(char *fulljid, char *message, GTimeVal tv_stamp);
 void handle_delayed_private_message(char *fulljid, char *message, GTimeVal tv_stamp);
-void handle_typing(char *from);
-void handle_gone(const char * const from);
+void handle_typing(char *barejid, char *resource);
+void handle_paused(char *barejid, char *resource);
+void handle_inactive(char *barejid, char *resource);
+void handle_activity(char *barejid, char *resource, gboolean send_states);
+void handle_gone(const char * const barejid);
 void handle_subscription(const char *from, jabber_subscr_t type);
 void handle_contact_offline(char *contact, char *resource, char *status);
 void handle_contact_online(char *contact, Resource *resource,
diff --git a/src/ui/core.c b/src/ui/core.c
index 30ec8749..d9cb2d8e 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -272,6 +272,13 @@ ui_handle_stanza(const char * const msg)
     }
 }
 
+gboolean
+ui_chat_win_exists(const char * const barejid)
+{
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    return (chatwin != NULL);
+}
+
 void
 ui_contact_typing(const char * const barejid)
 {
@@ -691,6 +698,7 @@ ui_close_connected_win(int index)
                 otr_end_session(chatwin->barejid);
             }
 #endif
+            chat_state_gone(chatwin->barejid, chatwin->state);
             chat_session_remove(chatwin->barejid);
         }
     }
@@ -1365,9 +1373,9 @@ ui_outgoing_chat_msg(const char * const from, const char * const barejid,
     // create new window
     if (window == NULL) {
         window = wins_new_chat(barejid);
+        ProfChatWin *chatwin = (ProfChatWin*)window;
 #ifdef HAVE_LIBOTR
         if (otr_is_secure(barejid)) {
-            ProfChatWin *chatwin = (ProfChatWin*)window;
             chatwin->is_otr = TRUE;
         }
 #endif
@@ -1389,6 +1397,8 @@ ui_outgoing_chat_msg(const char * const from, const char * const barejid,
     } else {
         num = wins_get_num(window);
     }
+    ProfChatWin *chatwin = (ProfChatWin*)window;
+    chat_state_active(chatwin->state);
 
     win_save_print(window, '-', NULL, 0, THEME_TEXT_ME, from, message);
     ui_switch_win(num);
@@ -2235,6 +2245,46 @@ ui_chat_win_contact_offline(PContact contact, char *resource, char *status)
 }
 
 void
+ui_contact_offline(char *barejid, char *resource, char *status)
+{
+    char *show_console = prefs_get_string(PREF_STATUSES_CONSOLE);
+    char *show_chat_win = prefs_get_string(PREF_STATUSES_CHAT);
+    Jid *jid = jid_create_from_bare_and_resource(barejid, resource);
+    PContact contact = roster_get_contact(barejid);
+    if (p_contact_subscription(contact) != NULL) {
+        if (strcmp(p_contact_subscription(contact), "none") != 0) {
+
+            // show in console if "all"
+            if (g_strcmp0(show_console, "all") == 0) {
+                cons_show_contact_offline(contact, resource, status);
+
+            // show in console of "online"
+            } else if (g_strcmp0(show_console, "online") == 0) {
+                cons_show_contact_offline(contact, resource, status);
+            }
+
+            // show in chat win if "all"
+            if (g_strcmp0(show_chat_win, "all") == 0) {
+                ui_chat_win_contact_offline(contact, resource, status);
+
+            // show in char win if "online" and presence online
+            } else if (g_strcmp0(show_chat_win, "online") == 0) {
+                ui_chat_win_contact_offline(contact, resource, status);
+            }
+        }
+    }
+
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (chatwin && chatwin->resource_override && (g_strcmp0(resource, chatwin->resource_override) == 0)) {
+        FREE_SET_NULL(chatwin->resource_override);
+    }
+
+    prefs_free_string(show_console);
+    prefs_free_string(show_chat_win);
+    jid_destroy(jid);
+}
+
+void
 ui_clear_win_title(void)
 {
     printf("%c]0;%c", '\033', '\007');
diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c
index 57634385..19318745 100644
--- a/src/ui/inputwin.c
+++ b/src/ui/inputwin.c
@@ -145,18 +145,18 @@ inp_get_char(char *input, int *size, int *result)
     noecho();
     *result = wget_wch(inp_win, &ch);
 
-//    gboolean in_command = FALSE;
-//    if ((display_size > 0 && input[0] == '/') ||
-//            (display_size == 0 && ch == '/')) {
-//        in_command = TRUE;
-//    }
-
-//    if (*result == ERR) {
-//        prof_handle_idle();
-//    }
-//    if ((*result != ERR) && (*result != KEY_CODE_YES) && !in_command && _printable(ch)) {
-//        prof_handle_activity();
-//    }
+    gboolean in_command = FALSE;
+    if ((display_size > 0 && input[0] == '/') ||
+            (display_size == 0 && ch == '/')) {
+        in_command = TRUE;
+    }
+
+    if (*result == ERR) {
+        prof_handle_idle();
+    }
+    if ((*result != ERR) && (*result != KEY_CODE_YES) && !in_command && _printable(ch)) {
+        prof_handle_activity();
+    }
 
     // if it wasn't an arrow key etc
     if (!_handle_edit(*result, ch, input, size)) {
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 0056b7af..0971f4f5 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -186,6 +186,7 @@ void ui_group_added(const char * const contact, const char * const group);
 void ui_group_removed(const char * const contact, const char * const group);
 void ui_chat_win_contact_online(PContact contact, Resource *resource, GDateTime *last_activity);
 void ui_chat_win_contact_offline(PContact contact, char *resource, char *status);
+void ui_contact_offline(char *barejid, char *resource, char *status);
 void ui_handle_recipient_not_found(const char * const recipient, const char * const err_msg);
 void ui_handle_recipient_error(const char * const recipient, const char * const err_msg);
 void ui_handle_error(const char * const err_msg);
@@ -213,6 +214,7 @@ void ui_show_lines(ProfWin *window, const gchar** lines);
 void ui_redraw_all_room_rosters(void);
 void ui_show_all_room_rosters(void);
 void ui_hide_all_room_rosters(void);
+gboolean ui_chat_win_exists(const char * const barejid);
 
 void ui_tidy_wins(void);
 void ui_prune_wins(void);
diff --git a/src/ui/window.c b/src/ui/window.c
index f03faadf..caef36df 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -137,6 +137,7 @@ win_create_chat(const char * const barejid)
     new_win->is_trusted = FALSE;
     new_win->history_shown = FALSE;
     new_win->unread = 0;
+    new_win->state = chat_state_new();
 
     new_win->memcheck = PROFCHATWIN_MEMCHECK;
 
@@ -334,6 +335,7 @@ win_free(ProfWin* window)
         ProfChatWin *chatwin = (ProfChatWin*)window;
         free(chatwin->barejid);
         free(chatwin->resource_override);
+        free(chatwin->state);
     }
 
     if (window->type == WIN_MUC) {
diff --git a/src/ui/window.h b/src/ui/window.h
index b17eb262..b6bd0298 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -47,6 +47,7 @@
 #include "muc.h"
 #include "ui/buffer.h"
 #include "xmpp/xmpp.h"
+#include "chat_state.h"
 
 #define NO_ME           1
 #define NO_DATE         2
@@ -109,6 +110,7 @@ typedef struct prof_chat_win_t {
     ProfWin window;
     char *barejid;
     int unread;
+    ChatState *state;
     gboolean is_otr;
     gboolean is_trusted;
     char *resource_override;
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index 69c320ea..3b962a90 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -157,10 +157,11 @@ message_send_composing(const char * const jid)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_COMPOSING);
 
+    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_COMPOSING);
     xmpp_send(conn, stanza);
     xmpp_stanza_release(stanza);
+
 }
 
 void
@@ -168,9 +169,7 @@ message_send_paused(const char * const jid)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid,
-        STANZA_NAME_PAUSED);
-
+    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_PAUSED);
     xmpp_send(conn, stanza);
     xmpp_stanza_release(stanza);
 }
@@ -180,8 +179,7 @@ message_send_inactive(const char * const jid)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid,
-        STANZA_NAME_INACTIVE);
+    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_INACTIVE);
 
     xmpp_send(conn, stanza);
     xmpp_stanza_release(stanza);
@@ -192,9 +190,7 @@ message_send_gone(const char * const jid)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid,
-        STANZA_NAME_GONE);
-
+    xmpp_stanza_t *stanza = stanza_create_chat_state(ctx, jid, STANZA_NAME_GONE);
     xmpp_send(conn, stanza);
     xmpp_stanza_release(stanza);
 }
@@ -469,24 +465,6 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         GTimeVal tv_stamp;
         gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
 
-        // handle chat sessions and states
-        if (!delayed && jid->resourcepart) {
-            gboolean recipient_gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL;
-            if (recipient_gone) {
-                handle_gone(jid->barejid);
-            } else {
-                gboolean send_states = FALSE;
-                if (stanza_contains_chat_state(stanza)) {
-                    send_states = TRUE;
-                    gboolean typing = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL;
-                    if (typing) {
-                        handle_typing(jid->barejid);
-                    }
-                }
-                chat_session_on_recipient_activity(jid->barejid, jid->resourcepart, send_states);
-            }
-        }
-
         // check for and deal with message
         xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
         if (body != NULL) {
@@ -501,6 +479,27 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
             }
         }
 
+        // handle chat sessions and states
+        if (!delayed && jid->resourcepart) {
+            gboolean gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL;
+            gboolean typing = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL;
+            gboolean paused = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL;
+            gboolean inactive = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_INACTIVE) != NULL;
+            if (gone) {
+                handle_gone(jid->barejid);
+            } else if (typing) {
+                handle_typing(jid->barejid, jid->resourcepart);
+            } else if (paused) {
+                handle_paused(jid->barejid, jid->resourcepart);
+            } else if (inactive) {
+                handle_inactive(jid->barejid, jid->resourcepart);
+            } else if (stanza_contains_chat_state(stanza)) {
+                handle_activity(jid->barejid, jid->resourcepart, TRUE);
+            } else {
+                handle_activity(jid->barejid, jid->resourcepart, FALSE);
+            }
+        }
+
         jid_destroy(jid);
         return 1;
     }
diff --git a/tests/ui/stub_ui.c b/tests/ui/stub_ui.c
index 5465e68d..b538f27b 100644
--- a/tests/ui/stub_ui.c
+++ b/tests/ui/stub_ui.c
@@ -259,6 +259,12 @@ void ui_group_added(const char * const contact, const char * const group) {}
 void ui_group_removed(const char * const contact, const char * const group) {}
 void ui_chat_win_contact_online(PContact contact, Resource *resource, GDateTime *last_activity) {}
 void ui_chat_win_contact_offline(PContact contact, char *resource, char *status) {}
+gboolean ui_chat_win_exists(const char * const barejid)
+{
+    return TRUE;
+}
+
+void ui_contact_offline(char *barejid, char *resource, char *status) {}
 
 void ui_handle_recipient_not_found(const char * const recipient, const char * const err_msg)
 {