mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-24 02:15:46 +03:00
Replace Riot -> Element in documentation
This commit is contained in:
parent
a600e896a0
commit
ef93ef57f9
3 changed files with 36 additions and 36 deletions
|
@ -14,7 +14,7 @@ Difference though (list not exhaustive):
|
||||||
- Only API v2 is supported (see https://matrix.org/docs/spec/identity_service/latest)
|
- Only API v2 is supported (see https://matrix.org/docs/spec/identity_service/latest)
|
||||||
- Homeserver has to be up to date to support binding (Versions.isLoginAndRegistrationSupportedBySdk() has to return true)
|
- Homeserver has to be up to date to support binding (Versions.isLoginAndRegistrationSupportedBySdk() has to return true)
|
||||||
- The SDK managed the session and client secret when binding ThreePid. Those data are not exposed to the client.
|
- The SDK managed the session and client secret when binding ThreePid. Those data are not exposed to the client.
|
||||||
- The SDK supports incremental sendAttempt (this is not used by RiotX)
|
- The SDK supports incremental sendAttempt (this is not used by Element)
|
||||||
- The "Continue" button is now under the information, and not as the same place that the checkbox
|
- The "Continue" button is now under the information, and not as the same place that the checkbox
|
||||||
- The app can cancel a binding. Current data are erased from DB.
|
- The app can cancel a binding. Current data are erased from DB.
|
||||||
- The API (IdentityService) is improved.
|
- The API (IdentityService) is improved.
|
||||||
|
@ -22,7 +22,7 @@ Difference though (list not exhaustive):
|
||||||
|
|
||||||
Missing features (list not exhaustive):
|
Missing features (list not exhaustive):
|
||||||
- Invite by 3Pid (will be in a dedicated PR)
|
- Invite by 3Pid (will be in a dedicated PR)
|
||||||
- Add email or phone to account (not P1, can be done on Riot-Web)
|
- Add email or phone to account (not P1, can be done on Element-Web)
|
||||||
- List email and phone of the account (could be done in a dedicated PR)
|
- List email and phone of the account (could be done in a dedicated PR)
|
||||||
- Search contact (not P1)
|
- Search contact (not P1)
|
||||||
- Logout from identity server when user sign out or deactivate his account.
|
- Logout from identity server when user sign out or deactivate his account.
|
||||||
|
@ -55,7 +55,7 @@ The list can be found here: https://matrix.org/blog/2019/09/27/privacy-improveme
|
||||||
|
|
||||||
- Default identity server URL, from Wellknown data is proposed to the user.
|
- Default identity server URL, from Wellknown data is proposed to the user.
|
||||||
- Identity server can be set
|
- Identity server can be set
|
||||||
- Identity server can be changed on another user's device, so when the change is detected (thanks to account data sync) RiotX should properly disconnect from a previous identity server (I think it was not the case in Riot-Android, where we keep the token forever)
|
- Identity server can be changed on another user's device, so when the change is detected (thanks to account data sync) Element should properly disconnect from a previous identity server (I think it was not the case in Riot-Android, where we keep the token forever)
|
||||||
- Registration to the identity server is managed with an openId token
|
- Registration to the identity server is managed with an openId token
|
||||||
- Terms of service can be accepted when configuring the identity server.
|
- Terms of service can be accepted when configuring the identity server.
|
||||||
- Terms of service can be accepted after, if they change.
|
- Terms of service can be accepted after, if they change.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
This document aims to describe how RiotX android displays notifications to the end user. It also clarifies notifications and background settings in the app.
|
This document aims to describe how Element android displays notifications to the end user. It also clarifies notifications and background settings in the app.
|
||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
1. [Prerequisites Knowledge](#prerequisites-knowledge)
|
1. [Prerequisites Knowledge](#prerequisites-knowledge)
|
||||||
|
@ -9,7 +9,7 @@ This document aims to describe how RiotX android displays notifications to the e
|
||||||
* [How does the Home Server knows when to notify a client?](#how-does-the-home-server-knows-when-to-notify-a-client)
|
* [How does the Home Server knows when to notify a client?](#how-does-the-home-server-knows-when-to-notify-a-client)
|
||||||
* [Push vs privacy, and mitigation](#push-vs-privacy-and-mitigation)
|
* [Push vs privacy, and mitigation](#push-vs-privacy-and-mitigation)
|
||||||
* [Background processing limitations](#background-processing-limitations)
|
* [Background processing limitations](#background-processing-limitations)
|
||||||
2. [RiotX Notification implementations](#riotx-notification-implementations)
|
2. [Element Notification implementations](#element-notification-implementations)
|
||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
* [Foreground sync mode (Gplay & F-Droid)](#foreground-sync-mode-gplay-f-droid)
|
* [Foreground sync mode (Gplay & F-Droid)](#foreground-sync-mode-gplay-f-droid)
|
||||||
* [Push (FCM) received in background](#push-fcm-received-in-background)
|
* [Push (FCM) received in background](#push-fcm-received-in-background)
|
||||||
|
@ -50,7 +50,7 @@ By default, this is 0, so the server will return immediately even if the respons
|
||||||
|
|
||||||
**delay** is a client preference. When the server responds to a sync request, the client waits for `delay`before calling a new sync.
|
**delay** is a client preference. When the server responds to a sync request, the client waits for `delay`before calling a new sync.
|
||||||
|
|
||||||
When the RiotX Android app is open (i.e in foreground state), the default timeout is 30 seconds, and delay is 0.
|
When the Element Android app is open (i.e in foreground state), the default timeout is 30 seconds, and delay is 0.
|
||||||
|
|
||||||
## How does a mobile app receives push notification
|
## How does a mobile app receives push notification
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ This need some disambiguation, because it is the source of common confusion:
|
||||||
In order to send a push to a mobile, App developers need to have a server that will use the FCM APIs, and these APIs requires authentication!
|
In order to send a push to a mobile, App developers need to have a server that will use the FCM APIs, and these APIs requires authentication!
|
||||||
This server is called a **Push Gateway** in the matrix world
|
This server is called a **Push Gateway** in the matrix world
|
||||||
|
|
||||||
That means that RiotX Android, a matrix client created by New Vector, is using a **Push Gateway** with the needed credentials (FCM API secret Key) in order to send push to the New Vector client.
|
That means that Element Android, a matrix client created by New Vector, is using a **Push Gateway** with the needed credentials (FCM API secret Key) in order to send push to the New Vector client.
|
||||||
|
|
||||||
If you create your own matrix client, you will also need to deploy an instance of a **Push Gateway** with the credentials needed to use FCM for your app.
|
If you create your own matrix client, you will also need to deploy an instance of a **Push Gateway** with the credentials needed to use FCM for your app.
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ A Home Server can be configured with default rules (for Direct messages, group m
|
||||||
|
|
||||||
There are different kind of push rules, it can be per room (each new message on this room should be notified), it can also define a pattern that a message should match (when you are mentioned, or key word based).
|
There are different kind of push rules, it can be per room (each new message on this room should be notified), it can also define a pattern that a message should match (when you are mentioned, or key word based).
|
||||||
|
|
||||||
Notifications have 2 'levels' (`highlighted = true/false sound = default/custom`). In RiotX these notifications level are reflected as Noisy/Silent.
|
Notifications have 2 'levels' (`highlighted = true/false sound = default/custom`). In Element these notifications level are reflected as Noisy/Silent.
|
||||||
|
|
||||||
**What about encrypted messages?**
|
**What about encrypted messages?**
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ In a nutshell, apps can't do much in background now.
|
||||||
|
|
||||||
If the devices is not plugged and stays IDLE for a certain amount of time, radio (mobile connectivity) and CPU can/will be turned off.
|
If the devices is not plugged and stays IDLE for a certain amount of time, radio (mobile connectivity) and CPU can/will be turned off.
|
||||||
|
|
||||||
For an application like RiotX, where users can receive important information at anytime, the best option is to rely on a push system (Google's Firebase Message a.k.a FCM). FCM high priority push can wake up the device and whitelist an application to perform background task (for a limited but unspecified amount of time).
|
For an application like Element, where users can receive important information at anytime, the best option is to rely on a push system (Google's Firebase Message a.k.a FCM). FCM high priority push can wake up the device and whitelist an application to perform background task (for a limited but unspecified amount of time).
|
||||||
|
|
||||||
Notice that this is still evolving, and in future versions application that has been 'background restricted' by users won't be able to wake up even when a high priority push is received. Also high priority notifications could be rate limited (not defined anywhere)
|
Notice that this is still evolving, and in future versions application that has been 'background restricted' by users won't be able to wake up even when a high priority push is received. Also high priority notifications could be rate limited (not defined anywhere)
|
||||||
|
|
||||||
|
@ -167,41 +167,41 @@ The documentation on this subject is vague, and as per our experiments not alway
|
||||||
|
|
||||||
It is getting more and more complex to have reliable notifications when FCM is not used.
|
It is getting more and more complex to have reliable notifications when FCM is not used.
|
||||||
|
|
||||||
# RiotX Notification implementations
|
# Element Notification implementations
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
RiotX Android must work with and without FCM.
|
Element Android must work with and without FCM.
|
||||||
* The RiotX android app published on F-Droid do not rely on FCM (all related dependencies are not present)
|
* The Element android app published on F-Droid do not rely on FCM (all related dependencies are not present)
|
||||||
* The RiotX android app published on google play rely on FCM, with a fallback mode when FCM registration has failed (e.g outdated or missing Google Play Services)
|
* The Element android app published on google play rely on FCM, with a fallback mode when FCM registration has failed (e.g outdated or missing Google Play Services)
|
||||||
|
|
||||||
## Foreground sync mode (Gplay & F-Droid)
|
## Foreground sync mode (Gplay & F-Droid)
|
||||||
|
|
||||||
When in foreground, RiotX performs sync continuously with a timeout value set to 10 seconds (see HttpPooling).
|
When in foreground, Element performs sync continuously with a timeout value set to 10 seconds (see HttpPooling).
|
||||||
|
|
||||||
As this mode does not need to live beyond the scope of the application, and as per Google recommendation, RiotX uses the internal app resources (Thread and Timers) to perform the syncs.
|
As this mode does not need to live beyond the scope of the application, and as per Google recommendation, Element uses the internal app resources (Thread and Timers) to perform the syncs.
|
||||||
|
|
||||||
This mode is turned on when the app enters foreground, and off when enters background.
|
This mode is turned on when the app enters foreground, and off when enters background.
|
||||||
|
|
||||||
In background, and depending on wether push is available or not, RiotX will use different methods to perform the syncs (Workers / Alarms / Service)
|
In background, and depending on wether push is available or not, Element will use different methods to perform the syncs (Workers / Alarms / Service)
|
||||||
|
|
||||||
## Push (FCM) received in background
|
## Push (FCM) received in background
|
||||||
|
|
||||||
In order to enable Push, RiotX must first get a push token from the firebase SDK, then register a pusher with this token on the HomeServer.
|
In order to enable Push, Element must first get a push token from the firebase SDK, then register a pusher with this token on the HomeServer.
|
||||||
|
|
||||||
When a message should be notified to a user, the user's homeserver notifies the registered `push gateway` for RiotX, that is [sygnal](https://github.com/matrix-org/sygnal) _- The reference implementation for push gateways -_ hosted by matrix.org.
|
When a message should be notified to a user, the user's homeserver notifies the registered `push gateway` for Element, that is [sygnal](https://github.com/matrix-org/sygnal) _- The reference implementation for push gateways -_ hosted by matrix.org.
|
||||||
|
|
||||||
This sygnal instance is configured with the required FCM API authentication token, and will then use the FCM API in order to notify the user's device running riotX.
|
This sygnal instance is configured with the required FCM API authentication token, and will then use the FCM API in order to notify the user's device running Element.
|
||||||
|
|
||||||
```
|
```
|
||||||
Homeserver ----> Sygnal (configured for RiotX) ----> FCM ----> RiotX
|
Homeserver ----> Sygnal (configured for Element) ----> FCM ----> Element
|
||||||
```
|
```
|
||||||
|
|
||||||
The push gateway is configured to only send `(eventId,roomId)` in the push payload (for better [privacy](#push-vs-privacy-and-mitigation)).
|
The push gateway is configured to only send `(eventId,roomId)` in the push payload (for better [privacy](#push-vs-privacy-and-mitigation)).
|
||||||
|
|
||||||
RiotX needs then to synchronise with the user's HomeServer, in order to resolve the event and create a notification.
|
Element needs then to synchronise with the user's HomeServer, in order to resolve the event and create a notification.
|
||||||
|
|
||||||
As per [Google recommendation](https://android-developers.googleblog.com/2018/09/notifying-your-users-with-fcm.html), RiotX will then use the WorkManager API in order to trigger a background sync.
|
As per [Google recommendation](https://android-developers.googleblog.com/2018/09/notifying-your-users-with-fcm.html), Element will then use the WorkManager API in order to trigger a background sync.
|
||||||
|
|
||||||
**Google recommendations:**
|
**Google recommendations:**
|
||||||
> We recommend using FCM messages in combination with the WorkManager 1 or JobScheduler API
|
> We recommend using FCM messages in combination with the WorkManager 1 or JobScheduler API
|
||||||
|
@ -209,7 +209,7 @@ As per [Google recommendation](https://android-developers.googleblog.com/2018/09
|
||||||
> Avoid background services. One common pitfall is using a background service to fetch data in the FCM message handler, since background service will be stopped by the system per recent changes to Google Play Policy
|
> Avoid background services. One common pitfall is using a background service to fetch data in the FCM message handler, since background service will be stopped by the system per recent changes to Google Play Policy
|
||||||
|
|
||||||
```
|
```
|
||||||
Homeserver ----> Sygnal ----> FCM ----> RiotX
|
Homeserver ----> Sygnal ----> FCM ----> Element
|
||||||
(Sync) ----> Homeserver
|
(Sync) ----> Homeserver
|
||||||
<----
|
<----
|
||||||
Display notification
|
Display notification
|
||||||
|
@ -217,24 +217,24 @@ Homeserver ----> Sygnal ----> FCM ----> RiotX
|
||||||
|
|
||||||
**Possible outcomes**
|
**Possible outcomes**
|
||||||
|
|
||||||
Upon reception of the FCM push, RiotX will perform a sync call to the Home Server, during this process it is possible that:
|
Upon reception of the FCM push, Element will perform a sync call to the Home Server, during this process it is possible that:
|
||||||
* Happy path, the sync is performed, the message resolved and displayed in the notification drawer
|
* Happy path, the sync is performed, the message resolved and displayed in the notification drawer
|
||||||
* The notified message is not in the sync. Can happen if a lot of things did happen since the push (`gappy sync`)
|
* The notified message is not in the sync. Can happen if a lot of things did happen since the push (`gappy sync`)
|
||||||
* The sync generates additional notifications (e.g an encrypted message where the user is mentioned detected locally)
|
* The sync generates additional notifications (e.g an encrypted message where the user is mentioned detected locally)
|
||||||
* The sync takes too long and the process is killed before completion, or network is not reliable and the sync fails.
|
* The sync takes too long and the process is killed before completion, or network is not reliable and the sync fails.
|
||||||
|
|
||||||
RiotX implements several strategies in these cases (TODO document)
|
Element implements several strategies in these cases (TODO document)
|
||||||
|
|
||||||
## FCM Fallback mode
|
## FCM Fallback mode
|
||||||
|
|
||||||
It is possible that RiotX is not able to get a FCM push token.
|
It is possible that Element is not able to get a FCM push token.
|
||||||
Common errors (amoung several others) that can cause that:
|
Common errors (amoung several others) that can cause that:
|
||||||
* Google Play Services is outdated
|
* Google Play Services is outdated
|
||||||
* Google Play Service fails in someways with FCM servers (infamous `SERVICE_NOT_AVAILABLE`)
|
* Google Play Service fails in someways with FCM servers (infamous `SERVICE_NOT_AVAILABLE`)
|
||||||
|
|
||||||
If RiotX is able to detect one of this cases, it will notifies it to the users and when possible help him fix it via a dedicated troubleshoot screen.
|
If Element is able to detect one of this cases, it will notifies it to the users and when possible help him fix it via a dedicated troubleshoot screen.
|
||||||
|
|
||||||
Meanwhile, in order to offer a minimal service, and as per Google's recommendation for background activities, RiotX will launch periodic background sync in order to stays in sync with servers.
|
Meanwhile, in order to offer a minimal service, and as per Google's recommendation for background activities, Element will launch periodic background sync in order to stays in sync with servers.
|
||||||
|
|
||||||
The fallback mode is impacted by all the battery life saving mechanism implemented by android. Meaning that if the app is not used for a certain amount of time (`App-Standby`), or the device stays still and unplugged (`Light Doze`) , the sync will become less frequent.
|
The fallback mode is impacted by all the battery life saving mechanism implemented by android. Meaning that if the app is not used for a certain amount of time (`App-Standby`), or the device stays still and unplugged (`Light Doze`) , the sync will become less frequent.
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ The fallback mode is supposed to be a temporary state waiting for the user to fi
|
||||||
|
|
||||||
## F-Droid background Mode
|
## F-Droid background Mode
|
||||||
|
|
||||||
The F-Droid RiotX flavor has no dependencies to FCM, therefore cannot relies on Push.
|
The F-Droid Element flavor has no dependencies to FCM, therefore cannot relies on Push.
|
||||||
|
|
||||||
Also Google's recommended background processing method cannot be applied. This is because all of these methods are affected by IDLE modes, and will result on the user not being notified at all when the app is in a Doze mode (only in maintenance windows that could happens only after hours).
|
Also Google's recommended background processing method cannot be applied. This is because all of these methods are affected by IDLE modes, and will result on the user not being notified at all when the app is in a Doze mode (only in maintenance windows that could happens only after hours).
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ F-Droid version will schedule alarms that will then trigger a Broadcast Receiver
|
||||||
|
|
||||||
Depending on the system status (or device make), it is still possible that the app is not given enough time to launch the service, or that the radio is still turned off thus preventing the sync to success (that's why Alarms are not recommended for network related tasks).
|
Depending on the system status (or device make), it is still possible that the app is not given enough time to launch the service, or that the radio is still turned off thus preventing the sync to success (that's why Alarms are not recommended for network related tasks).
|
||||||
|
|
||||||
That is why on RiotX F-Droid, the broadcast receiver will acquire a temporary WAKE_LOCK for several seconds (thus securing cpu/network), and launch the service in foreground. The service performs the sync.
|
That is why on Element F-Droid, the broadcast receiver will acquire a temporary WAKE_LOCK for several seconds (thus securing cpu/network), and launch the service in foreground. The service performs the sync.
|
||||||
|
|
||||||
Note that foreground services require to put a notification informing the user that the app is doing something even if not launched).
|
Note that foreground services require to put a notification informing the user that the app is doing something even if not launched).
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ Useful links:
|
||||||
│ │ │ │ │ mx event │ │ │ │
|
│ │ │ │ │ mx event │ │ │ │
|
||||||
│ │ │ └────────────────────┘ │ │ │ │
|
│ │ │ └────────────────────┘ │ │ │ │
|
||||||
│ │ │ │ │ │ │
|
│ │ │ │ │ │ │
|
||||||
│ Riot.im │ │ │ │ │ Riot.im │
|
│ Element │ │ │ │ │ Element │
|
||||||
┌──│ App │ │ │ │ │ App │
|
┌──│ App │ │ │ │ │ App │
|
||||||
│ │ │ │ │ │ │ │
|
│ │ │ │ │ │ │ │
|
||||||
│ │ │ │ │ │ │ │
|
│ │ │ │ │ │ │ │
|
||||||
|
@ -103,7 +103,7 @@ Useful links:
|
||||||
│ │ ┌────┐ │ │ └────────────────────┘ │ │ │
|
│ │ ┌────┐ │ │ └────────────────────┘ │ │ │
|
||||||
│ │ │ 3 │ │ ┌────────────────────┐ │ │ │ │
|
│ │ │ 3 │ │ ┌────────────────────┐ │ │ │ │
|
||||||
│ │──────┴────┴───────┼──────────────────┼─▶│ m.call.candidates │ │ │ │
|
│ │──────┴────┴───────┼──────────────────┼─▶│ m.call.candidates │ │ │ │
|
||||||
│ Riot.im │ │ │ mx event │ │ │ │ Riot.im │
|
│ Element │ │ │ mx event │ │ │ │ Element │
|
||||||
│ App │ │ │ └────────────────────┘ │ │ App │
|
│ App │ │ │ └────────────────────┘ │ │ App │
|
||||||
│ │ │ │ │ │ │
|
│ │ │ │ │ │ │
|
||||||
│ │ │ │ │ │ │
|
│ │ │ │ │ │ │
|
||||||
|
@ -195,9 +195,9 @@ Useful links:
|
||||||
│ │ │ m.call.invite │───┼────────────────────────────┬────┬───▶│ │
|
│ │ │ m.call.invite │───┼────────────────────────────┬────┬───▶│ │
|
||||||
┌─────────────────┐ │ │ mx event │ │ │ │ 1 │ │ │
|
┌─────────────────┐ │ │ mx event │ │ │ │ 1 │ │ │
|
||||||
│ │ │ │ └────────────────────┘ │ └────┘ │ │
|
│ │ │ │ └────────────────────┘ │ └────┘ │ │
|
||||||
│ │ │ ┌────────────────────┐ │ │ │ Riot.im │
|
│ │ │ ┌────────────────────┐ │ │ │ Element │
|
||||||
│ │ │ │ │ m.call.candidates │ │ │ App │
|
│ │ │ │ │ m.call.candidates │ │ │ App │
|
||||||
│ Riot.im │ │ │ mx event │ │ │ │ │
|
│ Element │ │ │ mx event │ │ │ │ │
|
||||||
│ App │ │ │ └────────────────────┘ │ │ │
|
│ App │ │ │ └────────────────────┘ │ │ │
|
||||||
│ │ │ ┌────────────────────┐◀──┼─────────────────┼───┬────┬───────────┤ │
|
│ │ │ ┌────────────────────┐◀──┼─────────────────┼───┬────┬───────────┤ │
|
||||||
│ │◀──────────────────┼──────────────────┼──│ m.call.answer │ │ │ 4 │ └──┬──────────────┘
|
│ │◀──────────────────┼──────────────────┼──│ m.call.answer │ │ │ 4 │ └──┬──────────────┘
|
||||||
|
@ -275,7 +275,7 @@ Useful links:
|
||||||
│ │ │ │ └────────────────────┘ │ │ │
|
│ │ │ │ └────────────────────┘ │ │ │
|
||||||
│ │ │ ┌────────────────────┐ │ │ │ │
|
│ │ │ ┌────────────────────┐ │ │ │ │
|
||||||
│ │ │ │ │ m.call.candidates │ │ │ │
|
│ │ │ │ │ m.call.candidates │ │ │ │
|
||||||
│ Riot.im │ │ │ mx event │ │ │ │ Riot.im │
|
│ Element │ │ │ mx event │ │ │ │ Element │
|
||||||
│ App │ │ │ └────────────────────┘ │ ┌────┐ │ App │
|
│ App │ │ │ └────────────────────┘ │ ┌────┐ │ App │
|
||||||
│ │ │ ┌────────────────────┐ │ │ │ 3 │ │ │
|
│ │ │ ┌────────────────────┐ │ │ │ 3 │ │ │
|
||||||
│ │◀──────────────────┼┐ │ │ m.call.answer │ │ ┌───────┴────┴────────│ │
|
│ │◀──────────────────┼┐ │ │ m.call.answer │ │ ┌───────┴────┴────────│ │
|
||||||
|
@ -370,7 +370,7 @@ Useful links:
|
||||||
│ │ │ │ │
|
│ │ │ │ │
|
||||||
│ │ │ │ │
|
│ │ │ │ │
|
||||||
│ │ │ │ │
|
│ │ │ │ │
|
||||||
│ Riot.im │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ Riot.im │
|
│ Element │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ Element │
|
||||||
│ App │ │ App │
|
│ App │ │ App │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
|
|
Loading…
Reference in a new issue