diff --git a/src/core/ObservableList.vala b/src/core/ObservableList.vala index 0be22fa..48310c8 100644 --- a/src/core/ObservableList.vala +++ b/src/core/ObservableList.vala @@ -23,6 +23,7 @@ namespace Venom { public class ObservableList : GLib.Object { public signal void added(GLib.Object item, uint index); public signal void removed(GLib.Object item, uint index); + public signal void changed(); private Gee.List list = new Gee.ArrayList(); @@ -44,12 +45,14 @@ namespace Venom { var idx = list.size; list.add(item); added(item, idx); + changed(); } public void remove(GLib.Object item) { var idx = list.index_of(item); removed(item, idx); list.remove_at(idx); + changed(); } public uint length() { diff --git a/src/meson.build b/src/meson.build index 955b2a9..4990a3f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -69,6 +69,7 @@ venom_source = files( 'view/AddContactWidget.vala', 'view/ApplicationWindow.vala', 'view/ConferenceInfoWidget.vala', + 'view/ConferenceInviteEntry.vala', 'view/ConferenceWindow.vala', 'view/ContactListEntry.vala', 'view/ContactListRequestEntry.vala', diff --git a/src/testing/mocks/MockToxSession.vala b/src/testing/mocks/MockToxSession.vala index cfa86f5..f4679c8 100644 --- a/src/testing/mocks/MockToxSession.vala +++ b/src/testing/mocks/MockToxSession.vala @@ -148,6 +148,13 @@ namespace Mock { .create(); mock().actual_call(this, "conference_invite", args).get_throws(); } + public void conference_join(uint32 friend_number, ConferenceType type, uint8[] cookie) throws ToxError { + var args = Arguments.builder() + .uint(friend_number) + .int(type) + .create(); + mock().actual_call(this, "conference_join", args).get_throws(); + } public void conference_send_message(uint32 conference_number, string message) throws ToxError { var args = Arguments.builder() .uint(conference_number) diff --git a/src/tox/FriendRequest.vala b/src/tox/FriendRequest.vala index e3d6500..c4ba4c0 100644 --- a/src/tox/FriendRequest.vala +++ b/src/tox/FriendRequest.vala @@ -20,30 +20,27 @@ */ namespace Venom { - public class FriendRequest : IContact, GLib.Object { - private string id; - private string message; + public class FriendRequest : GLib.Object { + public string id { get; set; } + public string message { get; set; } + public FriendRequest(string id, string message) { this.id = id; this.message = message; } - public string get_id() { - return id; - } - public string get_name_string() { - return _("Friend request"); + } + + public class ConferenceInvite : GLib.Object { + public IContact sender { get; set; } + public ConferenceType conference_type { get; set; } + public uint8[] get_cookie() { + return cookie; } - public string get_status_string() { - return message; + private uint8[] cookie; + public ConferenceInvite(IContact sender, ConferenceType conference_type, uint8[] cookie) { + this.sender = sender; + this.conference_type = conference_type; + this.cookie = cookie; } - public UserStatus get_status() { return UserStatus.NONE; } - public bool is_connected() { return false; } - public Gdk.Pixbuf get_image() { return null; } - public bool get_requires_attention() { return true; } - public void clear_attention() {} - public bool is_typing() { return false; } - public bool show_notifications() { return true; } - public bool is_conference() { return false; } } - } diff --git a/src/tox/ToxAdapterConferenceListener.vala b/src/tox/ToxAdapterConferenceListener.vala index 33c96d2..eaed165 100644 --- a/src/tox/ToxAdapterConferenceListener.vala +++ b/src/tox/ToxAdapterConferenceListener.vala @@ -20,19 +20,22 @@ */ namespace Venom { - public class ToxAdapterConferenceListenerImpl : ToxAdapterConferenceListener, ConferenceWidgetListener, ConferenceInfoWidgetListener, CreateGroupchatWidgetListener, GLib.Object { + public class ToxAdapterConferenceListenerImpl : ToxAdapterConferenceListener, ConferenceInviteEntryListener, ConferenceWidgetListener, ConferenceInfoWidgetListener, CreateGroupchatWidgetListener, GLib.Object { private unowned ToxSession session; private ILogger logger; private ObservableList contacts; + private ObservableList conference_invites; private NotificationListener notification_listener; private GLib.HashTable conversations; private GLib.HashTable conferences; + private unowned GLib.HashTable friends; - public ToxAdapterConferenceListenerImpl(ILogger logger, ObservableList contacts, GLib.HashTable conversations, NotificationListener notification_listener) { + public ToxAdapterConferenceListenerImpl(ILogger logger, ObservableList contacts, ObservableList conference_invites, GLib.HashTable conversations, NotificationListener notification_listener) { logger.d("ToxAdapterConferenceListenerImpl created."); this.logger = logger; this.contacts = contacts; + this.conference_invites = conference_invites; this.conversations = conversations; this.notification_listener = notification_listener; @@ -46,6 +49,7 @@ namespace Venom { public virtual void attach_to_session(ToxSession session) { this.session = session; session.set_conference_listener(this); + friends = session.get_friends(); } public virtual void on_remove_conference(IContact c) throws Error { @@ -63,12 +67,12 @@ namespace Venom { session.conference_send_message(conference.conference_number, message); } - public virtual void on_create_groupchat(string title, GroupchatType type) throws Error { + public virtual void on_create_groupchat(string title, ConferenceType type) throws Error { session.conference_new(title); } - public virtual void on_conference_invite(IContact c, string id) throws Error { - logger.d("on_conference_invite"); + public virtual void on_send_conference_invite(IContact c, string id) throws Error { + logger.d("on_send_conference_invite"); if (c is Contact) { var contact = c as Contact; if (id == "") { @@ -84,6 +88,49 @@ namespace Venom { } } + public virtual void on_accept_conference_invite(ConferenceInvite invite) throws Error { + var c = invite.sender as Contact; + session.conference_join(c.tox_friend_number, invite.conference_type, invite.get_cookie()); + conference_invites.remove(invite); + } + + public virtual void on_reject_conference_invite(ConferenceInvite invite) throws Error { + conference_invites.remove(invite); + } + + private bool invite_equals(ConferenceInvite invite, uint32 friend_number, ConferenceType type, uint8[] cookie) { + var cmp_sender = invite.sender as Contact; + var cmp_cookie = invite.get_cookie(); + return (cmp_sender.tox_friend_number == friend_number + && invite.conference_type == type + && cmp_cookie.length == cookie.length + && Memory.cmp(cookie, cmp_cookie, cookie.length) == 0); + } + + public virtual void on_conference_invite_received(uint32 friend_number, ConferenceType type, uint8[] cookie) { + logger.d("on_conference_invite_received"); + + if (friend_number == uint32.MAX) { + session.conference_join(friend_number, type, cookie); + } else { + for (var i = 0; i < conference_invites.length(); i++) { + var invite = conference_invites.nth_data(i) as ConferenceInvite; + if (invite_equals(invite, friend_number, type, cookie)) { + logger.d("duplicate invite received, discarding"); + return; + } + } + + var contact = friends.@get(friend_number) as Contact; + if (contact.auto_conference) { + session.conference_join(friend_number, type, cookie); + } else { + var invite = new ConferenceInvite(contact, type, cookie); + conference_invites.append(invite); + } + } + } + public virtual void on_conference_new(uint32 conference_number, string title) { logger.d("on_conference_new"); var contact = new Conference(conference_number, title); diff --git a/src/tox/ToxAdapterFriendListener.vala b/src/tox/ToxAdapterFriendListener.vala index 4c5726b..75b535c 100644 --- a/src/tox/ToxAdapterFriendListener.vala +++ b/src/tox/ToxAdapterFriendListener.vala @@ -29,20 +29,21 @@ namespace Venom { private GLib.HashTable messages_waiting_for_rr; private unowned GLib.HashTable friends; - private GLib.HashTable friend_requests; + private Gee.Map tox_friend_requests; + private ObservableList friend_requests; public bool show_typing { get; set; } - public ToxAdapterFriendListenerImpl(ILogger logger, ObservableList contacts, GLib.HashTable conversations, NotificationListener notification_listener) { + public ToxAdapterFriendListenerImpl(ILogger logger, ObservableList contacts, ObservableList friend_requests, GLib.HashTable conversations, NotificationListener notification_listener) { logger.d("ToxAdapterFriendListenerImpl created."); this.logger = logger; this.contacts = contacts; + this.friend_requests = friend_requests; this.conversations = conversations; this.notification_listener = notification_listener; messages_waiting_for_rr = new GLib.HashTable(null, null); - - friend_requests = new GLib.HashTable(str_hash, str_equal); + tox_friend_requests = new Gee.HashMap(); } ~ToxAdapterFriendListenerImpl() { @@ -75,17 +76,19 @@ namespace Venom { } public virtual void on_accept_friend_request(string id) throws Error { - var friend_request = friend_requests.@get(id); + var friend_request = tox_friend_requests.@get(id); var public_key = Tools.hexstring_to_bin(id); session.friend_add_norequest(public_key); - friend_requests.remove(id); - contacts.remove(friend_request); + + friend_requests.remove(friend_request); + tox_friend_requests.remove(id); } public virtual void on_reject_friend_request(string id) throws Error { - var friend_request = friend_requests.@get(id); - friend_requests.remove(id); - contacts.remove(friend_request); + var friend_request = tox_friend_requests.@get(id); + + friend_requests.remove(friend_request); + tox_friend_requests.remove(id); } public virtual void on_send_message(IContact c, string message) throws Error { @@ -148,10 +151,10 @@ namespace Venom { public virtual void on_friend_request(uint8[] public_key, string message) { logger.d("on_friend_request"); - var str_id = Tools.bin_to_hexstring(public_key); - var contact = new FriendRequest(str_id, message); - contacts.append(contact); - friend_requests.@set(str_id, contact); + var id = Tools.bin_to_hexstring(public_key); + var request = new FriendRequest(id, message); + friend_requests.append(request); + tox_friend_requests.@set(id, request); } public virtual void on_friend_status_changed(uint32 friend_number, UserStatus status) { diff --git a/src/tox/ToxSession.vala b/src/tox/ToxSession.vala index b7ca9a1..da160e4 100644 --- a/src/tox/ToxSession.vala +++ b/src/tox/ToxSession.vala @@ -26,6 +26,11 @@ namespace Venom { GENERIC } + public enum ConferenceType { + TEXT, + AV + } + public class ToxConferencePeer : GLib.Object { public uint32 peer_number; public string? peer_name; @@ -73,6 +78,7 @@ namespace Venom { public abstract uint32 conference_new(string title) throws ToxError; public abstract void conference_delete(uint32 conference_number) throws ToxError; public abstract void conference_invite(uint32 friend_number, uint32 conference_number) throws ToxError; + public abstract void conference_join(uint32 friend_number, ConferenceType type, uint8[] cookie) throws ToxError; public abstract void conference_send_message(uint32 conference_number, string message) throws ToxError; public abstract void conference_set_title(uint32 conference_number, string title) throws ToxError; @@ -108,6 +114,7 @@ namespace Venom { public interface ToxAdapterConferenceListener : GLib.Object { public abstract void on_conference_new(uint32 conference_number, string title); public abstract void on_conference_deleted(uint32 conference_number); + public abstract void on_conference_invite_received(uint32 friend_number, Venom.ConferenceType type, uint8[] cookie); public abstract void on_conference_title_changed(uint32 conference_number, uint32 peer_number, string title); public abstract void on_conference_peer_list_changed(uint32 conference_number, ToxConferencePeer[] peers); @@ -419,27 +426,12 @@ namespace Venom { Idle.add(() => { session.conference_listener.on_conference_title_changed(conference_number, peer_number, title_str); return false; }); } - private static void on_conference_invite_cb(Tox self, uint32 friend_number, ConferenceType type, uint8[] cookie, void *user_data) { + private static void on_conference_invite_cb(Tox self, uint32 friend_number, ToxCore.ConferenceType type, uint8[] cookie, void *user_data) { var session = (ToxSessionImpl) user_data; session.logger.d("on_conference_invite_cb type:" + type.to_string()); - if (type == ConferenceType.TEXT) { - var err = ErrConferenceJoin.OK; - var conference_number = self.conference_join(friend_number, cookie, out err); - if (err != ErrConferenceJoin.OK) { - session.logger.e("Conference join failed: " + err.to_string()); - return; - } - Idle.add(() => { session.conference_listener.on_conference_new(conference_number, ""); return false; }); - } else if (type == ConferenceType.AV) { - var conference_number = ToxAV.ToxAV.join_av_groupchat(self, friend_number, cookie, session.on_av_conference_audio_frame); - if (conference_number < 0) { - session.logger.e(@"Conference AV join failed: $conference_number"); - return; - } - Idle.add(() => { session.conference_listener.on_conference_new(conference_number, ""); return false; }); - } else { - session.logger.e("Conference join failed: Invalid Conference Type"); - } + var gc_type = (type == ToxCore.ConferenceType.AV) ? ConferenceType.AV : ConferenceType.TEXT; + var cookie_copy = copy_data(cookie, cookie.length); + Idle.add(() => { session.conference_listener.on_conference_invite_received(friend_number, gc_type, cookie_copy); return false; }); } private void on_av_conference_audio_frame() { @@ -774,6 +766,30 @@ namespace Venom { return conference_number; } + public virtual void conference_join(uint32 friend_number, ConferenceType type, uint8[] cookie) throws ToxError { + uint32 conference_number; + switch (type) { + case ConferenceType.AV: + conference_number = ToxAV.ToxAV.join_av_groupchat(handle, friend_number, cookie, on_av_conference_audio_frame); + if (conference_number < 0) { + var message = @"Conference AV join failed: $conference_number"; + logger.e(message); + throw new ToxError.GENERIC(message); + } + break; + default: + var err = ErrConferenceJoin.OK; + conference_number = handle.conference_join(friend_number, cookie, out err); + if (err != ErrConferenceJoin.OK) { + logger.e("Conference join failed: " + err.to_string()); + throw new ToxError.GENERIC(err.to_string()); + } + break; + } + + conference_listener.on_conference_new(conference_number, ""); + } + public virtual void conference_delete(uint32 conference_number) throws ToxError { var e = ErrConferenceDelete.OK; handle.conference_delete(conference_number, out e); diff --git a/src/ui/add_contact_widget.ui b/src/ui/add_contact_widget.ui index 32961af..0f1af88 100644 --- a/src/ui/add_contact_widget.ui +++ b/src/ui/add_contact_widget.ui @@ -1,7 +1,44 @@ - + + + True + False + center + center + 6 + vertical + 6 + + + True + False + contact-new-symbolic + 6 + + + False + True + 0 + + + + + True + False + No new friend requests + + + False + True + 1 + + + + Please let me add you to my contact list. šŸ˜ @@ -32,14 +69,11 @@ vertical 6 - + True False - Send a friend request - 0 - - - + center + stack False @@ -48,183 +82,286 @@ - - 450 + True False - none - False + crossfade - + True - True + False + vertical + 6 - + True False - 6 - vertical - 6 - - - True - False - _ID: - True - 0 - - - - - - False - True - 0 - - + Send a friend request + 0 + + + + + + False + True + 0 + + + + + 450 + True + False + none + False - + True True - Enter a Tox ID or URI here - True - True - edit-paste-symbolic - paste from clipboard - - - False - True - 1 - - - - - True - False - + True False - Error id - 0 - - - + 6 + vertical + 6 + + + True + False + _ID: + True + 0 + + + + + + False + True + 0 + + + + + True + True + Enter a Tox ID or URI here + True + True + edit-paste-symbolic + paste from clipboard + + + False + True + 1 + + + + + True + False + + + True + False + Error id + 0 + + + + + + + + False + True + 2 + + + + + True + False + <small>Enter your friends Tox ID</small> + True + 0 + + + + False + True + 3 + + - - False - True - 2 - - + True False - <small>Enter your friends Tox ID</small> - True - 0 - + + + True + False + 6 + vertical + 6 + + + True + False + _Message: + True + 0 + + + + + + False + True + 0 + + + + + 100 + True + False + + + True + False + + + True + True + Send a custom message to be displayed to the friend you are adding + 6 + word + textbuffer1 + + + + + + + + False + True + 1 + + + + + True + False + <small>Send your friend a short message</small> + True + True + 0 + + + + False + True + 2 + + + + - - False - True - 3 - + + + + False + True + 1 + + + + + Send + True + True + True + + + False + True + 2 + + + page0 + Add a friend + - + True False + vertical + 6 - + True False - 6 - vertical - 6 - - - True - False - _Message: - True - 0 - - - - - - False - True - 0 - - - - - 100 - True - False - - - True - False - - - True - True - Send a custom message to be displayed to the friend you are adding - 6 - word - textbuffer1 - - - - - - - - False - True - 1 - - - - - True - False - <small>Send your friend a short message</small> - True - True - 0 - - - - False - True - 2 - - + Friend requests + 0 + + + + + False + True + 0 + + + + + 450 + 350 + True + False + none + False + + + + True + True + 1 + + + page1 + Friend requests + 1 + - False @@ -232,25 +369,9 @@ 1 - - - Send - True - True - True - - - - False - True - 2 - - - True + False True 1 diff --git a/src/ui/conference_invite_entry.ui b/src/ui/conference_invite_entry.ui new file mode 100644 index 0000000..0059d8d --- /dev/null +++ b/src/ui/conference_invite_entry.ui @@ -0,0 +1,147 @@ + + + + + + diff --git a/src/ui/contact_list_widget.ui b/src/ui/contact_list_widget.ui index 8c8743f..ebba8fa 100644 --- a/src/ui/contact_list_widget.ui +++ b/src/ui/contact_list_widget.ui @@ -154,6 +154,56 @@ True False vertical + + + True + False + slide-up + + + friend_request_button + True + True + True + win.add_contact + + + + + + False + True + end + 0 + + + + + True + False + slide-up + + + conference_invite_button + True + True + True + win.groupchats + + + + + + False + True + end + 1 + + True @@ -208,7 +258,7 @@ False True - 0 + 2 @@ -219,7 +269,7 @@ False True - 1 + 3 @@ -243,7 +293,7 @@ True True - 2 + 4 @@ -254,7 +304,7 @@ False True - 3 + 5 diff --git a/src/ui/create_groupchat_widget.ui b/src/ui/create_groupchat_widget.ui index a91eefb..d2621ff 100644 --- a/src/ui/create_groupchat_widget.ui +++ b/src/ui/create_groupchat_widget.ui @@ -29,14 +29,11 @@ vertical 6 - + True False - Create a conference - 0 - - - + center + stack False @@ -45,196 +42,299 @@ - - 400 + True False - none - False + crossfade - + True - True + False + vertical + 6 - + True False - 6 - vertical - 6 - - - True - False - _Title: - True - 0 - - - - - - False - True - 0 - - + Create a conference + 0 + + + + + + False + True + 0 + + + + + 400 + True + False + none + False - + True True - True - True - Conference - paste from clipboard - - - False - True - 1 - - - - - True - False - + True False - title_error - 0 - - - + 6 + vertical + 6 + + + True + False + _Title: + True + 0 + + + + + + False + True + 0 + + + + + True + True + True + True + Conference + paste from clipboard + + + False + True + 1 + + + + + True + False + + + True + False + title_error + 0 + + + + + + + + False + True + 2 + + + + + True + False + <small>Name the conference (optional)</small> + True + 0 + + + + False + True + 3 + + - - False - True - 2 - - + True - False - <small>Name the conference (optional)</small> - True - 0 - + True + + + True + False + 6 + vertical + 6 + + + True + False + T_ype: + True + 0 + + + + + + False + True + 0 + + + + + True + False + True + + + Text + True + True + False + True + False + + + False + True + 0 + + + + + Speech + True + False + True + False + False + type_text + + + False + True + 1 + + + + + + False + True + 1 + + + + + True + False + <small>Select a conference type</small> + True + 0 + + + + False + True + 2 + + + + - - False - True - 3 - + + + + False + True + 1 + + + + + Create + True + True + True + + + False + True + 2 + + + page0 + New conference + - + True - True + False + vertical + 6 - + True False - 6 - vertical - 6 - - - True - False - T_ype: - True - 0 - - - - - - False - True - 0 - - - - - True - False - True - - - Text - True - True - False - True - False - - - False - True - 0 - - - - - Speech - True - False - True - False - False - type_text - - - False - True - 1 - - - - - - False - True - 1 - - - - - True - False - <small>Select a conference type</small> - True - 0 - - - - False - True - 2 - - + Conference invites + 0 + + + + + + False + True + 0 + + + + + 450 + 350 + True + False + none + False + + + True + True + 1 + + + page1 + Conference invites + 1 + - False @@ -242,22 +342,6 @@ 1 - - - Create - True - True - True - - - - False - True - 2 - - True @@ -278,4 +362,41 @@ + + True + False + center + center + 6 + vertical + 6 + + + True + False + conference-symbolic + 6 + + + False + True + 0 + + + + + True + False + No new conference invites + + + False + True + 1 + + + + diff --git a/src/ui/friend_request_widget.ui b/src/ui/friend_request_widget.ui index ba2a2ea..8cd09ae 100644 --- a/src/ui/friend_request_widget.ui +++ b/src/ui/friend_request_widget.ui @@ -1,77 +1,24 @@ - + -