Skip to content

Conversation View

Jonathan Daugherty edited this page Sep 18, 2019 · 14 revisions

The web client has a "conversation view." The conversation view is a pop-up window containing a message area and a message editor. Messages shown in the message area are only those messages that are part of a reply chain of a particular post, and the message editor always implicitly posts messages in reply to that message thread.

This page is about capturing ideas for a similar feature in Matterhorn.

Concerns

It would be nice for the conversation view to coexist nicely with the usual client functionality. It would be nice if a user can easily go back and forth between conversations or between conversations and channels, including the channel in which a conversation is taking place. This allows conversations to exist on equal footing with channels, meaning that users might use conversations more with a UI that doesn't interrupt their usage of other channels. The web client only allows a user to have one active conversation at a time, which limits the usefulness of conversations. In addition the UI for conversations is limited in size, resulting in another way in which conversations feel second-class.

Options

UI options for a conversation include:

1. Pop up a modal window similar to the web client with its own message editor and message listing. 2. When switching to a conversation in a channel, replace the channel's message listing with the conversation messages. 3. Treat conversations like "channels"; make them appear in the sidebar.

Option (1): this option is closest to the web client in terms of behavior. To do this we'd need to generalize our message listing and editing machinery enough to instantiate it separately for the conversation view. This option also means that the user would have to leave the conversation to respond to other channel activity, and then come back, making for a jarring experience.

Option (2): this option would allow for seamless conversation participation, but it isn't clear what the UX should be for leaving the conversation to go back to the channel. It also overloads the sidebar entry for the channel: should unread activity in the channel be indicated there? That seems like it would be confusing if the conversation itself didn't have unread activity.

Option (3): this option would allow us to re-use all of our existing machinery (channel switching, fast selection, message data structures, input history processing) for conversations. It would allow conversation switching independently of channels and allow users to participate in multiple conversations. However, it also means that which conversations a user is "in" will need to be client-side state since there is no server API for indicating conversation participation. It also means that the sidebar's value type (currently ChannelListEntry) would need to be extended to indicate conversations. That isn't hard, but the consequence is that now, everywhere the "current channel ID" is needed, we can still get it -- but it might also come with additional conversation context (the root post ID of the conversation). It means that some state transformations that currently happen in channels might be nonsensical in a conversation context. So ChannelListEntry would need a new constructor for conversations, and a way to force the application to deal with the new distinction is to make currentChannelId return a new data type that we have to unpack to get the channel ID (and/or conversation post ID). Then, everywhere we deal with that data, update it to handle the case where we're in a conversation.

On further exploration of (3) in the refactor/channel-handles and experimental/logging-channel branches, we found that using an abstract handle for channels may work well but:

  • whenever a channel ID from the server is needed, a channel handle must

be created from it to bridge from the server's data (the ID) to our data (the handle).

  • whenever a channel handle is used, the usage may have to consider the

particular kind of handle present and take different code paths. For example, the logging channel branch added a logging channel that should not respond the same way to the /leave command as a server channel will.

  • the on-disk format and in-memory representation of the input history

both assume that input history is stored on a per server-channel basis, with server channel ID being the key into the data structure. With our own channel handles we'll want to store channel input history for other kinds of channels (e.g. logging or conversation channels) so the map will need to key on the channel handle type. This means that the on-disk format (currently the Read/Show instances for the input history map) will need to change to a versioned scheme that attempts to load the new version (keyed on handle) and fall back to the old to maintain compatibility with existing history files.

Feature Sketch

Events of interest:

  • User wants to "join" a conversation

    • User enters message selection mode

    • User selects option to make a channel for the conversation

    • A new channel appears in the sidebar with the conversation's messages appearing in it. The initial name of the channel is something harmless like "Conversation <N>" and can be renamed by the user later (see below)

    • Messages appearing in the conversation channel are displayed without reply parent messages

    • Messages in the conversation displayed in the parent channel get displayed differently:

      • The root post for the conversation is displayed with a conversation channel reference in the parent channel, e.g.:

        foo: this is the parent message
          (See Conversation 2)
        
      • Other messages in the thread are not displayed in the parent channel at all by default.

      • If the user enters message selection mode in the parent channel and selects the root post for the conversation, we can provide an action to toggle the visibility of all of the other messages in the conversation in the parent channel.

      • Similarly, if the conversation messages are displayed in the parent channel, in message selection mode we provide an action to hide them, which causes all messages except for the root message in the conversation to be hidden in the parent channel.

  • User wants to "leave" a conversation

    • User is focused on a conversation channel
    • User runs either the "/leave" command or the "/hide" command and the conversation channel is removed from the state
  • A new message arrives from the server:

    • If the new message is in a conversation the user is following:
      • The message is added to both the conversation's parent channel as well as the conversation-specific channel
      • New Messages lines for both channels should be updated for the new message and should be independent of each other
      • Only the sidebar unread indicator for the conversation channel is modified and the sidebar unread indicator for the parent channel is unaffected.
    • If the new message is in a channel the user is in but is not in a conversation the user is following:
      • The message is added to the channel and the channel's unread status is updated
  • The server notifies the client that a message was edited:

    • The edited message is in a conversation the user is following
      • The edit indicator for the message is set in the conversation channel and the parent channel
      • The unread indicator for the conversation channel is updated
    • The edited message is in a channel the user is in but is not in a conversation the user is following
      • The unread status for the channel is set as usual
  • The server notifies the client that a message was deleted:

    • The deleted message is in a conversation the user is following but is not the root post for the conversation
      • The message is marked as deleted in both the conversation channel and parent channel
    • The deleted message is in a conversation the user is following and is the root post for the conversation
      • The message and its thread children are marked as deleted in the parent channel
      • The conversation channel is deleted from the sidebar
    • The deleted message is in a channel the user is in but is not in a conversation the user is following
  • The user changes channels:

    • Between conversations in the same parent channel
    • Between conversations in different parent channels
    • From a conversation in a channel to the parent channel
    • From a channel to a conversation in that same channel
  • The user sends a message in a conversation

    • The new message's root post ID is set to the post ID of the conversation channel
    • Explicit post replies in conversation channels are not supported
  • The program shuts down while the user is focused on a conversation

    • The program updates the last run state to set its focused channel to the conversation channel
  • The program starts up and the last run state indicates the user was

focused on a conversation at the time of the last shutdown

  • If the last run state indicates that the user was focused on a conversation, determine whether that channel still exists and whether the root post ID exists. If so, create the channel and make it the initially focused channel. Otherwise fall back to Town Square.
  • The program starts up and the local client state indicates the user

was following one or more conversations

  • The application reads a state file from disk indicating the channel conversations that the user is following
  • The user input history needs to be preserved for a conversation

channel

  • The input history saved on disk needs to map channel handles to input history rather than channel IDs to input history.
  • The user wants to "rename" a conversation
    • The "/rename" command is intercepted by the client.
      • If the channel is a conversation channel, update the local channel name only.
      • If the channel is not a conversation channel, send the rename command to the server.
  • The client receives a notification that a server-side channel is

renamed:

  • The channel in question is renamed
  • If the user is following any conversations in that channel, those conversations' names are unaffected
  • The user leaves a normal channel while also following one or more

conversations in that channel

  • The confirmation dialog box that appears when the user attempts to leave the channel also indicates the list of conversations that will automatically be closed if the user chooses to continue.
  • All conversation channels are also closed and removed from the sidebar.
  • A channel of which the user is a member is deleted:
    • If the user is participating in any conversations in that channel, those conversation channels are also removed from the sidebar when the parent channel is removed
  • The user runs the "focus" command with the name of a conversation

channel

  • The argument to the focus command is checked against the channel names of the conversation channels for a match. We don't know what to do about whitespace in the channel names here.
  • The user runs the "members" command in a conversation channel
    • The authors of all posts in the conversation are collected and presented in a dialog box similar to the one currently displayed by "/members".
  • The client disconnects:
    • We still add an ending gap to all channels, including conversation channels
  • The client reconnects:
    • For each conversation channel, fetch the entire conversation for each conversation channel rather than using gaps to manage the channel contents.

These events might not even be handled in the master branch so we should also check on how they get handled (if at all) today:

  • Another user that is participating in a conversation is deleted (which

we observe with the WMUserUpdated websocket event)

Operational Notes

  • We need to think about whether incoming messages get duplicated

in a parent channel as well as conversation channel, or whether the conversation channel messages are always obtained by applying a filter to the messages of the parent channel

  • We need to think about how to deal with the versioning of the "last

run state" and the conversation channel membership state

  • Due to the fact that the server API only allows us to fetch post

threads wholesale, we suspect there won't be any "gap" mechanics for conversation channels

  • In conversation channels we don't want to have the gap entry at the beginning of the message history

Server API Improvement Suggestions

  • The /channels/{channel_id}/posts endpoint currently doesn't

allow us to fetch messages scoped to a conversation the way the /posts/{post_id}/thread endpoint does. It would be great if these could be unified or combined so that we could ask for messages after/before a given message within a given thread.

  • Unrelated to conversations, but: the posts endpoint mentioned above

has before/after endpoints that are exclusive. It would be great if they could be inclusive to help us avoid having to figure out which message to back up to when we are fetching older messages.

Clone this wiki locally