Skip to main content

4 posts tagged with "quiri"

View All Tags

· 2 min read
Nigel Maynard

Background

I chose Bloc as the state management solution for Quiri after a lot of reading into the different options because I felt that it had less "magic". It also had twice the Github stars of the next most popular project and seemed to have the support of larger teams. Since choosing it there have been some challenges.

  • side-effects and one-time events like page navigations and toast messages
  • streaming data from the matrix client

Streaming from the Matrix Client

I go into more detail on how the matrix Dart SDK syncs data with the server and exposes in the [Listening for New Messages With the Matrix SDK](2024-12-19-listening-for-new-messages-with-the-matrix-sdk copy.md) article. Ultimately the SDK already maintains the state of all events, etc. that come from the Matrix server. What the SDK exposes to Bloc then is a state object and a collection of streams that can be subsciribed to that provide all event details. I have been subscribing to the matrix streams in the Bloc and then adding events to the bloc for each new emission from the matrix stream. This article from the Bloc team presents emit.forEach as an option for subscribing to reactive repositories instead. They show the same pattern here in the docs.

· 2 min read
Nigel Maynard

Background

Almost all actions in matrix are expressed as users sending events to rooms. This makes intuitive sense when it comes to message events but works equally well for non-message events (e.g. add user event, change room name event, etc). When a user adds an event to a room, it is first added to the event graph on their personal client, then it is replicated to the user's home server and then it is replicated to all other clients that are in that room. Quiri is written in Dart and uses the matrix dart sdk to handle a lot of the client-server relationship. This includes maintaining the local copy of the current state of a room's state and listening for updates to that room's state. This guide goes into details about how that relationship is maintained so that we can build the quiri client appropriately.

Listening for events

On app startup, we currently call the init method. This method does a number of things including kicking off the first _sync request.

This sync call can be awaited as a whole, or you can provide the waitForFirstSync as false and instead await the roomsLoading, _accountDataLoading and userDeviceKeysLoading futures individually. We will await on the init for now for simplicity.

After the initial sync, the client will begin a _backgroundSync that long polls every 30 seconds (configurable).

Replicating the state in the client

Each sync job uses the _handleSync method to perform the appropriate updates to state.

For example, in the _handleRoomEvents sub-handler the updates are made in the following order:

  • the in-memory state (accessible from the SDK client object e.g. client.rooms)
  • the client side database for persistent storage
  • the SDK's exposed event streams

Reacting to new events in the UI

In particular, how do we know when new messages have arrived? Let's look at how Fluffychat does it. First let's look at where they render their chat boxes. The messages are found in a room's timeline. The timeline is returned from the SDK getTimeline method. An onUpdate callback is passed to allow the UI to react to new messages arriving. Here is where Fluffychat sets up the timeline listener for new messages

· 2 min read
Nigel Maynard

Background

Inviting someone to a Quiri is a core experience in the quiri app. This guide studies the matrix invite functionality as well as how the Dart SDK implements it (with references to how Fluffychat does it.) See User Discovery in Quiri for a discussion of user discoverability and maintaining a contact list.

Accepting an Invite

Lets see how Fluffychat does it. It appears that when you are invited to a room, you will be added to the room but with a membership status of invite. The SDK calls the join room endpoint. Fluffychat is then SDK's client.waitForRoomInSync method to wait for that room to be enabled after the room join event is added to the event graph.

TODO:

  • how do you list rooms that you have been invited to?

Listening for Invites

The SDK offers the waitForRoomInSync method that waits for a specific room by ID to experience an invite, join or leave event. For private rooms I don't imagine that you would know the id of the room to be able to listen for it in this way. I haven't figured out how Fluffychat handles room invites but based on the waitForRoomInSync method I am watching the onSync stream, filtering on room events.

· 6 min read
Nigel Maynard

The Problem

In the current dev build of Quiri, the only way to open a conversation with a user is to know their username and to enter it perfectly in the conversation partner field. Here we explore ways to improve this experience, including:

  • inviting a user via their email or phone number (3rd party identifier / 3PID)
    • inviting users without an account to create an account to accept the invite and start the conversation
  • a contact list or friends list to quickly open new quiris
  • adding a friend using their quiri userID or displayname

For the MVP all rooms are to be private and 1-1 and there are no plans to help users discover one another on the platform (e.g. no public spaces for posting). Public posting is being considered for a future release.

What Matrix Offers

As we are building on top of matrix, we should first attempt to leverage the features and patterns that they offer out of the box to minimize custom dev work.

Inviting a known user

This is the flow that we are using in the dev build today. Matrix users come with identifiers that look a lot like emails (e.g. @nigel:quiri.io). When you want to open a room with a user you can address it in the same way that you would address an email: know the user's email exactly and write it correctly. If the address is wrong, the room will fail to open same as an email would fail to send.

Matrix servers offer the ability to search the user directory for other users by their user ID or display name. This feature does not simply list the users though. Some search term must be passed (e.g. at least a single character like a) and only those users matching will be shown. As such, this is a feature that could help users find each other in two cases:

  • the quiri userId/displayname was shared previously and the search provides some confirmation that the user actually exists and may provide suggestions if the userID/displayname was misspelt
  • A user can try their friend's real name and hope they found the right person if the search returns a hit

Configuration Options

The synapse matrix server implementation offers some configuration of the search results but it is limited in it's configurability.

At it's most open, it returns search results for all users on the server and other servers. For the MVP this may be acceptable but as the community grows it could enable spammers to abuse the platform and harass random users.

The one restriction that is offered is to set the search_all_users property to false in which,

"...search results will only contain users visible in public rooms and users sharing a room with the requester." With this flag set we might be able to use this feature to enable searching of all users you have interacted with before as long as you are still in a room with each of those users.

Third Party Invites

Matrix offers a way to invite someone to a room using a third-party invite. If the user already has an account associated with that email/phone then they are added to the room. If they don't have an account yet, the user is sent an email inviting them to join quiri (and the room they were invited to). Once they have created an account associated with their email they can join the room.

List all users in a room

For a given room, all members in that room can be listed. this does not help us with our problems directly but may be useful as a workaround for storing a contact list.

Search a room

Matrix offers the server-side indexing and search of room event contents. This also does not help us directly but could enable the searching of a "contact list" room.

Our Solution

Inviting someone to a room using their email

The built-in third party invite functionality appears to handle this. A more thorough audit should be performed against a local instance of Synapse.

Contact list

Matrix does not currently offer a contact list functionality. If we want this functionality, the out-of-the box solution will be to create a non-chat room for each user in which only their contact list is stored. Non-chat rooms are an expected use-case for Matrix so it's not terribly hacky but the functionality will be limited. The room search feature could possibly be used for filtering the contact list but sorting will likely be client side. The client will handle the logic for when a user is added/removed from a "contact list" room and will have to be careful to not include the contact list room in the quiri list. A more thorough audit of the room invite functionality should be performed to detail how "contact list" room invites and acceptances would map to "contact list" invites and acceptances/rejections.

Adding a friend to your contact list

For the MVP there will be two ways in which you can add a user to your friends list:

  • knowing their email
  • knowing their username or display name

This feels quite restricted but making user directories searchable trades the convenience of searching for users by name with the risk of users being spammed. While this is a low risk today, as the platform grows, this is the kind of thing that crushes communication platforms.

Taking Discord as a reference, they offer adding a friend strictly by username but you can add someone to a server by email.

Conclusion

Overall, the user experience will be:

  • open conversations for the first time by providing their email (or knowing their username)
  • a one-time use shareable add-me-as-a-friend link that can be pasted into an outside chat or email would be a nice way to make friend adding easier
  • open further conversations with a user via your contact list