From 6abae85926fd0e352485cb500d0523f4d29f655c Mon Sep 17 00:00:00 2001
From: Nico Alt <nicoalt@posteo.org>
Date: Wed, 11 Dec 2019 11:53:35 +0100
Subject: [PATCH] Automatically refresh contact list on added contact

Also refactor socket_listener.py to make it more general.
---
 src/briar/api/models/contacts.py        | 11 ++++++++++
 src/briar/api/models/private_chat.py    | 27 +++++++++++++++++--------
 src/briar/api/models/socket_listener.py | 20 ++++++++----------
 src/briar/gtk/containers/chat.py        | 10 ++++-----
 src/briar/gtk/containers/main.py        | 23 ++++++++++++++++++---
 5 files changed, 63 insertions(+), 28 deletions(-)

diff --git a/src/briar/api/models/contacts.py b/src/briar/api/models/contacts.py
index a706a90..6d4f6cf 100644
--- a/src/briar/api/models/contacts.py
+++ b/src/briar/api/models/contacts.py
@@ -15,6 +15,8 @@ class Contacts(Model):
 
     API_ENDPOINT = "contacts/"
 
+    _on_contact_added_callback = None
+
     def add_pending(self, link, alias):
         url = urljoin(BASE_HTTP_URL, self.API_ENDPOINT + "add/" + "pending/")
         _post(url, headers=self._headers, json={"link": link, "alias": alias})
@@ -28,3 +30,12 @@ class Contacts(Model):
         url = urljoin(BASE_HTTP_URL, self.API_ENDPOINT + "add/" + "link/")
         request = _get(url, headers=self._headers).json()
         return request['link']
+
+    def watch_contacts(self, callback):
+        self._on_contact_added_callback = callback
+        self._api.socket_listener.watch("ContactAddedEvent",
+                                        self._on_contact_added)
+
+    # pylint: disable=unused-argument
+    def _on_contact_added(self, event):
+        self._on_contact_added_callback()
diff --git a/src/briar/api/models/private_chat.py b/src/briar/api/models/private_chat.py
index 1d3351d..40caa46 100644
--- a/src/briar/api/models/private_chat.py
+++ b/src/briar/api/models/private_chat.py
@@ -15,16 +15,27 @@ class PrivateChat(Model):
 
     API_ENDPOINT = "messages/"
 
-    def get(self, contact_id):
-        url = urljoin(BASE_HTTP_URL, self.API_ENDPOINT + "/%i" % contact_id)
+    _contact_id = 0
+    _on_message_received_callback = None
+
+    def __init__(self, api, contact_id):
+        self._api = api
+        self._initialize_headers()
+        self._contact_id = contact_id
+
+    def get(self):
+        url = urljoin(BASE_HTTP_URL, self.API_ENDPOINT + "/%d" % self._contact_id)
         request = _get(url, headers=self._headers)
         return request.json()
 
-    def watch_messages(self, contact_id, callback):
-        self._api.socket_listener.watch(callback,
-                                        "ConversationMessageReceivedEvent",
-                                        contact_id=contact_id)
+    def watch_messages(self, callback):
+        self._on_message_received_callback = callback
+        self._api.socket_listener.watch("ConversationMessageReceivedEvent",
+                                        self._on_message_received)
+
+    def _on_message_received(self, event):
+        self._on_message_received_callback(event['data'])
 
-    def send(self, contact_id, message):
-        url = urljoin(BASE_HTTP_URL, self.API_ENDPOINT + "/%i" % contact_id)
+    def send(self, message):
+        url = urljoin(BASE_HTTP_URL, self.API_ENDPOINT + "/%i" % self._contact_id)
         _post(url, headers=self._headers, json={"text": message})
diff --git a/src/briar/api/models/socket_listener.py b/src/briar/api/models/socket_listener.py
index 93808a0..c5b39ee 100644
--- a/src/briar/api/models/socket_listener.py
+++ b/src/briar/api/models/socket_listener.py
@@ -12,26 +12,22 @@ from briar.api.constants import WEBSOCKET_URL
 from briar.api.model import Model
 
 
-# TODO: Make more general; currently very specific to private messages
-# TODO: remove pylint disable once we have more methods
 class SocketListener(Model):  # pylint: disable=too-few-public-methods
 
-    def watch(self, callback, event, contact_id="0"):
+    def watch(self, event, callback):
         websocket_thread = Thread(target=self._start_watch_loop,
-                                  args=(callback, event, contact_id),
+                                  args=(event, callback),
                                   daemon=True)
         websocket_thread.start()
 
-    def _start_watch_loop(self, callback, event, contact_id="0"):
+    def _start_watch_loop(self, event, callback):
         loop = asyncio.new_event_loop()
         asyncio.set_event_loop(loop)
-        loop.create_task(self._start_websocket(callback, event, contact_id))
+        loop.create_task(self._start_websocket(event, callback))
         loop.run_forever()
         loop.close()
 
-    # TODO: use contact id
-    # pylint: disable=unused-argument
-    async def _start_websocket(self, callback, event, contact_id="0"):
+    async def _start_websocket(self, event, callback):
         async with websockets.connect(WEBSOCKET_URL) as websocket:
             await websocket.send(self._api.auth_token)
             await self._watch_messages(websocket, event, callback)
@@ -39,10 +35,10 @@ class SocketListener(Model):  # pylint: disable=too-few-public-methods
     async def _watch_messages(self, websocket, event, callback):
         while not websocket.closed and not\
                 asyncio.get_event_loop().is_closed():
-            message = await websocket.recv()
-            message = json.loads(message)
+            message_json = await websocket.recv()
+            message = json.loads(message_json)
             if message['name'] == event:
-                callback(message['data'])
+                callback(message)
         if not asyncio.get_event_loop().is_closed():
             asyncio.get_event_loop().create_task(
                 self._watch_messages(websocket, event, callback))
diff --git a/src/briar/gtk/containers/chat.py b/src/briar/gtk/containers/chat.py
index 12b7103..b2e00b0 100644
--- a/src/briar/gtk/containers/chat.py
+++ b/src/briar/gtk/containers/chat.py
@@ -28,12 +28,12 @@ class ChatContainer(Container):
         chat_entry.connect("key-press-event", self._key_pressed)
 
     def _load_content(self):
-        private_chat = PrivateChat(self._api)
-        messages_list = private_chat.get(self._contact_id)
+        private_chat = PrivateChat(self._api, self._contact_id)
+        messages_list = private_chat.get()
         self._messages_list_box = self.builder.get_object("messages_list")
         for message in messages_list:
             self._add_message(message["text"], message["local"])
-        private_chat.watch_messages(self._contact_id, self._add_message_async)
+        private_chat.watch_messages(self._add_message_async)
 
     def _add_message(self, message, local):
         message_label = Gtk.Label(message)
@@ -52,8 +52,8 @@ class ChatContainer(Container):
             return
         chat_entry = self.builder.get_object("chat_entry")
         message = chat_entry.get_text()
-        private_chat = PrivateChat(self._api)
-        private_chat.send(self._contact_id, message)
+        private_chat = PrivateChat(self._api, self._contact_id)
+        private_chat.send(message)
 
         self._add_message(message, True)
         chat_entry.set_text("")
diff --git a/src/briar/gtk/containers/main.py b/src/briar/gtk/containers/main.py
index 5cdadcc..af6c04e 100644
--- a/src/briar/gtk/containers/main.py
+++ b/src/briar/gtk/containers/main.py
@@ -25,16 +25,33 @@ class MainContainer(Container):
         self.builder.connect_signals(self)
 
     def _load_content(self):
-        contacts = Contacts(self._api)
-        contacts_list = contacts.get()
+        self._contacts = Contacts(self._api)
+        self._load_contacts()
+        self._contacts.watch_contacts(self._refresh_contacts_async)
+
+    def _load_contacts(self):
+        contacts_list = self._contacts.get()
         contacts_list_box = self.builder.get_object("contacts_list")
         for contact in contacts_list:
-            contact_button = Gtk.Button(contact["author"]["name"])
+            contact_button = Gtk.Button(contact["alias"])
             contact_button.connect("clicked", MainContainer._contact_clicked,
                                    contact["contactId"])
             contact_button.show()
             contacts_list_box.add(contact_button)
 
+    def _refresh_contacts_async(self):
+        GLib.idle_add(self._refresh_contacts)
+
+    def _refresh_contacts(self):
+        self._clear_contact_list()
+        self._load_contacts()
+
+    def _clear_contact_list(self):
+        contacts_list_box = self.builder.get_object("contacts_list")
+        contacts_list_box_children = contacts_list_box.get_children()
+        for child in contacts_list_box_children:
+            contacts_list_box.remove(child)
+
     # pylint: disable=unused-argument
     @staticmethod
     def _contact_clicked(widget, contact_id):
-- 
GitLab