using GLib; using Gee; public class MessageListModel : Object, ListModel { public signal void messages_changed(); public ArrayList messages { get { return _messages; } } public bool is_group_chat { get { return participants.size > 2; } } public string conversation_guid { get; private set; } private ArrayList _messages; private HashSet participants = new HashSet(); private ulong update_handler_id = 0; private ulong reconnected_handler_id = 0; public MessageListModel(string conversation_guid) { _messages = new ArrayList(); this.conversation_guid = conversation_guid; } ~MessageListModel() { // NOTE: this won't actually get destructed automatically because of a retain cycle with the signal handler. // unwatch_updates() should be called explicitly when the model is no longer needed. unwatch_updates(); } public void watch_updates() { if (this.update_handler_id == 0) { weak MessageListModel self = this; this.update_handler_id = Repository.get_instance().messages_updated.connect((conversation_guid) => { self.got_messages_updated(conversation_guid); }); } if (this.reconnected_handler_id == 0) { weak MessageListModel self = this; this.reconnected_handler_id = Repository.get_instance().reconnected.connect(() => { // On reconnect, reload the messages that we're looking at now. self.load_messages(); }); } } public void unwatch_updates() { if (this.update_handler_id != 0) { Repository.get_instance().disconnect(this.update_handler_id); this.update_handler_id = 0; } if (this.reconnected_handler_id != 0) { Repository.get_instance().disconnect(this.reconnected_handler_id); this.reconnected_handler_id = 0; } } public void load_messages() { var previous_messages = new HashSet(); previous_messages.add_all(_messages); try { bool first_load = _messages.size == 0; Message[] messages = Repository.get_instance().get_messages(conversation_guid); // Clear existing set uint old_count = _messages.size; _messages.clear(); participants.clear(); // Notify of removal if (old_count > 0) { items_changed(0, old_count, 0); } // Process each conversation uint position = 0; for (int i = 0; i < messages.length; i++) { var message = messages[i]; participants.add(message.sender); if (!first_load && !previous_messages.contains(message)) { // This is a new message according to the UI, schedule an animation for it. message.should_animate = true; } _messages.add(message); position++; } // Notify of additions if (position > 0) { items_changed(0, 0, position); } } catch (Error e) { warning("Failed to load messages: %s", e.message); } messages_changed(); } private void got_messages_updated(string conversation_guid) { if (conversation_guid == this.conversation_guid) { load_messages(); } } // ListModel implementation public Type get_item_type() { return typeof(Message); } public uint get_n_items() { return _messages.size; } public Object? get_item(uint position) { return _messages.get((int)position); } }