1 cwtch group overlays DRAFT
Dan Ballard edited this page 2019-09-24 13:56:22 -07:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Cwtch Group Overlays Originally written by Erinn


This document specifies the desired functionality of the primary Cwtch group reader user interface (hereafter simply “Cwtch”) and sketches a technical plan for its implementation. It is not intended as a binding specification, but rather as a medium for soliciting comments and feedback.


2018-12-12: initial version

Current implementation

Currently the Cwtch UI is implemented as a collection of “panes”: a StackView QML widget represents most UI content (except for the contact list and current-user profile information) and switches the active pane based on signals from other parts of the interface. For example, there is a pane for viewing and editing account settings, a pane for displaying p2p messages, a pane for p2p per-contact settings, a pane for displaying Cwtch group messages, a pane for per-group settings, and so forth. Each pane is implemented as a QML widget itself and can have arbitrary contents and methods.

As an example, lets consider the case of viewing a groups messages and then editing its display name. First, the user clicks on the groups name in the contact list. The triggers the onClick method of the contact entry, which does three things: first, it clears the contents of the (possibly hidden) group message pane; next, it populates the group message pane with historical messages from the group (which could be stored on disk or stored remotely on a Cwtch server or a combination of both); finally, it switches the active pane of the StackView to the newly-repopulated Cwtch group message pane.

This clear-and-repopulate method of displaying messages seems inefficient at first glance. However, the alternative is to dynamically create a pane for every contact entry and keep their contents preloaded at all times. This would then mean we are creating at least two redundant copies of users messages: one in a golang data structure and one in Qt (plus optional on-disk storage). We could drop the golang message store, but this would mean proxying all interaction with messages through the UI and thus having to (re)implement much of our core functionality in javascript. It would also mean we cannot (in the future) implement smarter caching/history browsing, such as only loading old messages from disk into memory when the user scrolls to the top of the currently-loaded history in the messge pane. Better to work with raw message data and attributes in golang and only send to the UI the minimum information required to display authenticated messages.

This message-population mechanism currently works via Qts signals-and-slots functionality. When a contact entry is clicked, it makes an API call to the Grand Central Dispatcher (GCD) requesting the historical messages for its associated group ID. The GCD receives this request, looks up the messages in the appropriate places*, and delivers them one-at-a-time to the UI by sending an AppendMessage signal containing all information necessary to render the message (sender, profile picture, timestamp, message contents, acknowledgement status, etc.). The group message display pane is subscribed (“connected”) to this AppendMessage signal, and simply adds a new Message QML widget to itself (technically, it uses a Qt ListModel for this) when called.

  • As noted previously, there are two places historical group messages may be stored: locally on disk, or remotely on the groups Cwtch server. Loading and merging these sometimes-overlapping message stores into a canonical in-memory data structure is handled by golang code and the precise algorithm for this is outside the scope of this document.

Continuing with our example, now that historical messages have been loaded into the UI, it is possible for new messages to be delivered from the group server. On the golang side, we have to worry about two scenarios: either the group message pane is currently active in the StackView and is displaying messages for the incoming messages associated group, or not. If it is, another AppendMessage signal is sent through the GCD and the message is appended to the ListModel, no different from loading historical messages. If the message pane is not active or displaying a different group, the message is added only to the in-memory golang message store, and a signal is sent through the GCD notifying the contact list entry of a new message (so it can, for instance, increase its “unread” counter/badge).

Now, recall that the user wishes to edit the groups display name (a local setting defined in a per-group custom attribute called “nick”). To do so, the user clicks the group settings button located on the group message pane. The code for this button works similarly to the code for loading the group message pane: first it requests the necessary settings from golang by signalling the GCD (which responds by in turn signalling the group settings pane to populate its controls with groups particular current settings), then it switches the StackViews active pane to the group settings pane. From here, the user can edit the group nick (and other settings) in a standard text editing control, and the “save” button sends signals to the GCD (a) providing any changed settings that need to be updated and (b) asking to restore the previous group message pane. (There is an obvious optimization to be done here in that the message pane doesnt actually need to be wiped and repopulated in this instance, since the contents of the group message pane likely havent changed while the user was viewing the group settings).

Message acknowledgements for in-UI messages are an interesting case. Currently, when golang code receives a message acknowledgement, it sends a signal through the GCD containing the message ID to be acknowledged iff the messages group/sender is the current active pane in the StackView (the GCD keeps track of the currently active conversation/group). All Message QML elements subscribe to this message; when received, they check if the message ID is theirs and, if so, mark themselves as acknowledged. (There are more obvious optimizations to be made here, ranging from having messages unsubscribe from this signal once acknowledged [so that only unacknowledged messages, of which there are typically few, maintain active connections] to somehow indexing all messages and sending them signals directly.)

Sending group messages is extremely simplistic at the moment: users compose a message in the text editor located within the group message pane, and click the Send button when finished. The Send button takes (and clears) the composed message and sends a signal via the GCD asking golang code to handle sending the message on its behalf.


We would like to support many different “views” or “interpretations” of structured Cwtch group messages, such as allowing messages to have hierarchical presentation (messages that are replies to other messages) or allowing messages to represent moves on a game board (or the entire game state, or...). A good solution here means one that allows for the most flexibility in making unplanned, novel, creative views in the future without needing changes to any Cwtch code outside a narrow and clearly-scoped area of code. We are currently calling these views/interpretations “overlays”.

Based on previous discussions, we would like a single group to have multiple valid overlays. For example, a group of people may want to play a game of Chinese Checkers while simultaneously chatting about the game. The natural solution to this is to maintain an enumeration of post types -> overlay mapping. A question arises as to whether we should permit multiple overlays for the same post type (eg lowfi vs hifi Checkers graphics).


Overlays are a single .QML file (possibly referencing other widgets, of course) implementing a pane. The API is defined by the GCD, as follows:

  • AppendMessage: a message has been loaded from the message store OR from the Cwtch server historical archive on initial connection OR been streamed from the Cwtch server. Messages are JSON-formatted with some maximum byte size, and overlays can interpret them as they see fit.

  • Acknowledge: a message acknowledgement has been received for a previously sent message (currently, for groups, this actually means seeing your own message appear in the live feed from the server). Question: do we want to support any other special functionality like acknowledgements as first-class functions in Cwtch?

  • SendMessage: overlays are responsible for producing messages that conform to their own JSON-based format. Golang will truncate them to the maximum message size if the overlay has not done so already (all overlays should deal with the length restriction, though).

  • Settings: overlays should be provided a simple key-value store with native saving/loading of preferences.

  • Events: we may in the future need to support events like “group ownership has changed” or “X wants to fork the group”. Related to the acknowledgements question, should we implement each such event as a first-class function for overlays, or should we introduce a generic event-notification call and allow overlays to handle potentially-unexpected events?