aboutsummaryrefslogtreecommitdiff
path: root/libdino/src/service/chat_interaction.vala
diff options
context:
space:
mode:
Diffstat (limited to 'libdino/src/service/chat_interaction.vala')
-rw-r--r--libdino/src/service/chat_interaction.vala146
1 files changed, 146 insertions, 0 deletions
diff --git a/libdino/src/service/chat_interaction.vala b/libdino/src/service/chat_interaction.vala
new file mode 100644
index 00000000..cd6907fa
--- /dev/null
+++ b/libdino/src/service/chat_interaction.vala
@@ -0,0 +1,146 @@
+using Gee;
+
+using Xmpp;
+using Dino.Entities;
+
+namespace Dino {
+public class ChatInteraction : StreamInteractionModule, Object {
+ private const string id = "chat_interaction";
+
+ public signal void conversation_read(Conversation conversation);
+ public signal void conversation_unread(Conversation conversation);
+
+ private StreamInteractor stream_interactor;
+ private Conversation? selected_conversation;
+
+ private HashMap<Conversation, DateTime> last_input_interaction = new HashMap<Conversation, DateTime>(Conversation.hash_func, Conversation.equals_func);
+ private HashMap<Conversation, DateTime> last_interface_interaction = new HashMap<Conversation, DateTime>(Conversation.hash_func, Conversation.equals_func);
+ private bool focus_in = false;
+
+ public static void start(StreamInteractor stream_interactor) {
+ ChatInteraction m = new ChatInteraction(stream_interactor);
+ stream_interactor.add_module(m);
+ }
+
+ private ChatInteraction(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+ Timeout.add_seconds(30, update_interactions);
+ MessageManager.get_instance(stream_interactor).message_received.connect(on_message_received);
+ MessageManager.get_instance(stream_interactor).message_sent.connect(on_message_sent);
+ }
+
+ public bool is_active_focus(Conversation? conversation = null) {
+ if (conversation != null) {
+ return focus_in && conversation.equals(this.selected_conversation);
+ } else {
+ return focus_in;
+ }
+ }
+
+ public void window_focus_in(Conversation? conversation) {
+ on_conversation_selected(selected_conversation);
+ }
+
+ public void window_focus_out(Conversation? conversation) {
+ focus_in = false;
+ }
+
+ public void on_message_entered(Conversation conversation) {
+ if (Settings.instance().send_read) {
+ if (!last_input_interaction.has_key(conversation) && conversation.type_ != Conversation.Type.GROUPCHAT) {
+ send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_COMPOSING);
+ }
+ }
+ last_input_interaction[conversation] = new DateTime.now_utc();
+ last_interface_interaction[conversation] = new DateTime.now_utc();
+ }
+
+ public void on_message_cleared(Conversation conversation) {
+ if (last_input_interaction.has_key(conversation)) {
+ last_input_interaction.unset(conversation);
+ last_interface_interaction.unset(conversation);
+ send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_ACTIVE);
+ }
+ }
+
+ public void on_conversation_selected(Conversation? conversation) {
+ selected_conversation = conversation;
+ focus_in = true;
+ if (conversation != null) {
+ conversation_read(selected_conversation);
+ check_send_read();
+ selected_conversation.read_up_to = MessageManager.get_instance(stream_interactor).get_last_message(conversation);
+ }
+ }
+
+ internal string get_id() {
+ return id;
+ }
+
+ public static ChatInteraction? get_instance(StreamInteractor stream_interactor) {
+ return (ChatInteraction) stream_interactor.get_module(id);
+ }
+
+ private void check_send_read() {
+ if (selected_conversation == null || selected_conversation.type_ == Conversation.Type.GROUPCHAT) return;
+ Entities.Message? message = MessageManager.get_instance(stream_interactor).get_last_message(selected_conversation);
+ if (message != null && message.direction == Entities.Message.DIRECTION_RECEIVED &&
+ message.stanza != null && !message.equals(selected_conversation.read_up_to)) {
+ selected_conversation.read_up_to = message;
+ send_chat_marker(selected_conversation, message, Xep.ChatMarkers.MARKER_DISPLAYED);
+ }
+ }
+
+ private bool update_interactions() {
+ ArrayList<Conversation> remove_input = new ArrayList<Conversation>(Conversation.equals_func);
+ ArrayList<Conversation> remove_interface = new ArrayList<Conversation>(Conversation.equals_func);
+ foreach (Conversation conversation in last_input_interaction.keys) {
+ if (last_input_interaction.has_key(conversation) &&
+ (new DateTime.now_utc()).difference(last_input_interaction[conversation]) >= 15 * TimeSpan.SECOND) {
+ remove_input.add(conversation);
+ send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_PAUSED);
+ }
+ }
+ foreach (Conversation conversation in last_interface_interaction.keys) {
+ if (last_interface_interaction.has_key(conversation) &&
+ (new DateTime.now_utc()).difference(last_interface_interaction[conversation]) >= 1.5 * TimeSpan.MINUTE) {
+ remove_interface.add(conversation);
+ send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_GONE);
+ }
+ }
+ foreach (Conversation conversation in remove_input) last_input_interaction.unset(conversation);
+ foreach (Conversation conversation in remove_interface) last_interface_interaction.unset(conversation);
+ return true;
+ }
+
+ private void on_message_received(Entities.Message message, Conversation conversation) {
+ if (is_active_focus(conversation)) {
+ check_send_read();
+ conversation.read_up_to = message;
+ send_chat_marker(conversation, message, Xep.ChatMarkers.MARKER_DISPLAYED);
+ } else {
+ conversation_unread(conversation);
+ }
+ }
+
+ private void on_message_sent(Entities.Message message, Conversation conversation) {
+ last_input_interaction.unset(conversation);
+ last_interface_interaction.unset(conversation);
+ conversation.read_up_to = message;
+ }
+
+ private void send_chat_marker(Conversation conversation, Entities.Message message, string marker) {
+ Core.XmppStream stream = stream_interactor.get_stream(conversation.account);
+ if (stream != null && Settings.instance().send_read && Xep.ChatMarkers.Module.requests_marking(message.stanza)) {
+ Xep.ChatMarkers.Module.get_module(stream).send_marker(stream, message.stanza.from, message.stanza_id, message.get_type_string(), marker);
+ }
+ }
+
+ private void send_chat_state_notification(Conversation conversation, string state) {
+ Core.XmppStream stream = stream_interactor.get_stream(conversation.account);
+ if (stream != null && Settings.instance().send_read) {
+ Xep.ChatStateNotifications.Module.get_module(stream).send_state(stream, conversation.counterpart.to_string(), state);
+ }
+ }
+}
+} \ No newline at end of file