From 6134702d25aa89a78a1028fc4e9fa67ab4b0d8b8 Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Mon, 12 Feb 2024 18:54:58 +0100 Subject: [PATCH] Add Timeline documentation --- docs/timeline.md | 108 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/timeline.md diff --git a/docs/timeline.md b/docs/timeline.md new file mode 100644 index 0000000..8497f06 --- /dev/null +++ b/docs/timeline.md @@ -0,0 +1,108 @@ +# Timeline + +Given the complex nature of the Timeline design, it deserves some explanation of +the design. This document aims to describe how the Elm SDK designs the Timeline, +so that other projects may learn from it. + +## API endpoint disambiguations + +Generally speaking, there are a few API endpoints with similar design: + +- The [`/sync` endpoint](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3sync), +which gets the events that the homeserver received most recently. +- The [`/messages` endpoint](https://spec.matrix.org/v1.9/client-server-api/#get_matrixclientv3roomsroomidmembers), +which gets any events in the topological order. + +As noted in the Matrix spec: + +> Events are ordered in this API according to the arrival time of the event on +> the homeserver. This can conflict with other APIs which order events based on +> their partial ordering in the event graph. This can result in duplicate events +> being received (once per distinct API called). Clients SHOULD de-duplicate +> events based on the event ID when this happens. + +For this reason, the Elm SDK maintains **two independent timelines** that are tied +together when necessary to form a coherent timeline. + +## Elm design + +For those unfamiliar, the Elm Architecture breaks into three parts: + +- **Model** - the state of the application +- **View** - a way to turn your state into meaningful information +- **Update** - a way to update your state based on the Matrix API + +Since these concepts are compartmentalized, it is impossible to make an API call +while executing the **view** function; the Elm SDK must at all times find a way +to represent its state. + +## Timeline + +Concerning the Matrix timeline, it is meant to create a representation +(**Model**) of the timeline, find a way to represent (**View**) it, and find a +simple way to adjust it with every incoming Matrix API result. (**Update**) + +First, we define what a timeline batch is. + +### Timeline batch + +A timeline batch is something that most Matrix API endpoints return. It is a +little piece of the timeline and contains the following four pieces of +information: + +1. A list of events that are part of the timeline. +2. A Filter for which all provided events meet the criteria. +3. An end batch token that functions as an identifier. +4. _(Optional.)_ A start token. If not provided, it indicates the start of the + timeline. + +Here's an example of such a timeline batch: + +``` + |-->[■]->[■]->[●]->[■]->[■]->[●]-->| + | | + |<--- filter: only ■ and ● --->| + | | + start: end: + +``` + +When the Matrix API later returns a batch token that starts with ``, +we know that we can connect it to the batch above and make a longer list of +events! + +At first, this seems quite simple to connect, but there are some difficulties +that come up along the way. + +### Challenge 1: different filters, different locations + +When two timeline batches have different filters, we do not know their +respective location. For example, the following two timeline batches COULD +overlap, but it is also possible they don't: + +``` + |-->[■]->[■]->[●]->[■]->[■]->[●]-->| + | | + |<--- filter: only ■ and ● --->| + | | + start: end: + + + + |-->[★]->[★]->[★]->[★]-->| + | | + |<-- filter: only ★ -->| + | | + start: end: + +``` + +Realistically, there is currently no way of knowing without making more API +calls. However, just making more API calls isn't a solution in Elm because of +its architecture. + +> **SOLUTION:** As described in the **View** function, we may assume that +overlapping timeline batches have overlapping events. If they overlap yet have +no overlapping events, then their filters must be disjoint. If the filters are +disjoint, we do not care whether they're overlapping. +