mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-10 17:03:20 -04:00
feat(ffi): Add methods for observing account data changes
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
This commit is contained in:
committed by
Damir Jelić
parent
afabfb97b6
commit
175d854a9b
@@ -6,6 +6,7 @@ use std::{
|
||||
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use async_compat::get_runtime_handle;
|
||||
use futures_util::StreamExt;
|
||||
use matrix_sdk::{
|
||||
authentication::oauth::{
|
||||
AccountManagementActionFull, ClientId, OAuthAuthorizationData, OAuthSession,
|
||||
@@ -45,8 +46,13 @@ use mime::Mime;
|
||||
use ruma::{
|
||||
api::client::{alias::get_alias, error::ErrorKind, uiaa::UserIdentifier},
|
||||
events::{
|
||||
direct::DirectEventContent,
|
||||
fully_read::FullyReadEventContent,
|
||||
identity_server::IdentityServerEventContent,
|
||||
ignored_user_list::IgnoredUserListEventContent,
|
||||
key::verification::request::ToDeviceKeyVerificationRequestEvent,
|
||||
marked_unread::{MarkedUnreadEventContent, UnstableMarkedUnreadEventContent},
|
||||
push_rules::PushRulesEventContent,
|
||||
room::{
|
||||
history_visibility::RoomHistoryVisibilityEventContent,
|
||||
join_rules::{
|
||||
@@ -55,7 +61,13 @@ use ruma::{
|
||||
message::OriginalSyncRoomMessageEvent,
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
},
|
||||
GlobalAccountDataEventType,
|
||||
secret_storage::{
|
||||
default_key::SecretStorageDefaultKeyEventContent, key::SecretStorageKeyEventContent,
|
||||
},
|
||||
tag::TagEventContent,
|
||||
GlobalAccountDataEvent as RumaGlobalAccountDataEvent,
|
||||
GlobalAccountDataEventType as RumaGlobalAccountDataEventType,
|
||||
RoomAccountDataEvent as RumaRoomAccountDataEvent,
|
||||
},
|
||||
push::{HttpPusherData as RumaHttpPusherData, PushFormat as RumaPushFormat},
|
||||
OwnedServerName, RoomAliasId, RoomOrAliasId, ServerName,
|
||||
@@ -76,7 +88,10 @@ use crate::{
|
||||
room::RoomHistoryVisibility,
|
||||
room_directory_search::RoomDirectorySearch,
|
||||
room_preview::RoomPreview,
|
||||
ruma::{AuthData, MediaSource},
|
||||
ruma::{
|
||||
AccountDataEvent, AccountDataEventType, AuthData, MediaSource, RoomAccountDataEvent,
|
||||
RoomAccountDataEventType,
|
||||
},
|
||||
sync_service::{SyncService, SyncServiceBuilder},
|
||||
task_handle::TaskHandle,
|
||||
utils::AsyncRuntimeDropped,
|
||||
@@ -168,6 +183,20 @@ pub trait SendQueueRoomErrorListener: Sync + Send {
|
||||
fn on_error(&self, room_id: String, error: ClientError);
|
||||
}
|
||||
|
||||
/// A listener for changes of global account data events.
|
||||
#[matrix_sdk_ffi_macros::export(callback_interface)]
|
||||
pub trait AccountDataListener: Sync + Send {
|
||||
/// Called when a global account data event has changed.
|
||||
fn on_change(&self, event: AccountDataEvent);
|
||||
}
|
||||
|
||||
/// A listener for changes of room account data events.
|
||||
#[matrix_sdk_ffi_macros::export(callback_interface)]
|
||||
pub trait RoomAccountDataListener: Sync + Send {
|
||||
/// Called when a room account data event was changed.
|
||||
fn on_change(&self, event: RoomAccountDataEvent, room_id: String);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, uniffi::Record)]
|
||||
pub struct TransmissionProgress {
|
||||
pub current: u64,
|
||||
@@ -545,6 +574,198 @@ impl Client {
|
||||
})))
|
||||
}
|
||||
|
||||
/// Subscribe to updates of global account data events.
|
||||
///
|
||||
/// Be careful that only the most recent value can be observed. Subscribers
|
||||
/// are notified when a new value is sent, but there is no guarantee that
|
||||
/// they will see all values.
|
||||
pub fn observe_account_data_event(
|
||||
&self,
|
||||
event_type: AccountDataEventType,
|
||||
listener: Box<dyn AccountDataListener>,
|
||||
) -> Arc<TaskHandle> {
|
||||
match event_type {
|
||||
AccountDataEventType::Direct => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(
|
||||
self.inner
|
||||
.observe_events::<RumaGlobalAccountDataEvent<DirectEventContent>, ()>(),
|
||||
);
|
||||
|
||||
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
listener.on_change(next.0.into());
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
AccountDataEventType::IdentityServer => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(self.inner.observe_events::<RumaGlobalAccountDataEvent<IdentityServerEventContent>, ()>());
|
||||
|
||||
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
listener.on_change(next.0.into());
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
AccountDataEventType::IgnoredUserList => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(self.inner.observe_events::<RumaGlobalAccountDataEvent<IgnoredUserListEventContent>, ()>());
|
||||
|
||||
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
listener.on_change(next.0.into());
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
AccountDataEventType::PushRules => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(
|
||||
self.inner
|
||||
.observe_events::<RumaGlobalAccountDataEvent<PushRulesEventContent>, ()>(),
|
||||
);
|
||||
|
||||
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
if let Ok(event) = next.0.try_into() {
|
||||
listener.on_change(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
AccountDataEventType::SecretStorageDefaultKey => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(self.inner.observe_events::<RumaGlobalAccountDataEvent<SecretStorageDefaultKeyEventContent>, ()>());
|
||||
|
||||
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
listener.on_change(next.0.into());
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
AccountDataEventType::SecretStorageKey(key_id) => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(self.inner.observe_events::<RumaGlobalAccountDataEvent<SecretStorageKeyEventContent>, ()>());
|
||||
|
||||
Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
if next.0.content.key_id != key_id {
|
||||
continue;
|
||||
}
|
||||
if let Ok(event) = next.0.try_into() {
|
||||
listener.on_change(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subscribe to updates of room account data events.
|
||||
///
|
||||
/// Be careful that only the most recent value can be observed. Subscribers
|
||||
/// are notified when a new value is sent, but there is no guarantee that
|
||||
/// they will see all values.
|
||||
pub fn observe_room_account_data_event(
|
||||
&self,
|
||||
room_id: String,
|
||||
event_type: RoomAccountDataEventType,
|
||||
listener: Box<dyn RoomAccountDataListener>,
|
||||
) -> Result<Arc<TaskHandle>, ClientError> {
|
||||
match event_type {
|
||||
RoomAccountDataEventType::FullyRead => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(
|
||||
self.inner
|
||||
.observe_room_events::<RumaRoomAccountDataEvent<FullyReadEventContent>, ()>(
|
||||
&RoomId::parse(&room_id)?,
|
||||
),
|
||||
);
|
||||
|
||||
Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
listener.on_change(next.0.into(), room_id.clone());
|
||||
}
|
||||
}
|
||||
}))))
|
||||
}
|
||||
RoomAccountDataEventType::MarkedUnread => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(self.inner.observe_room_events::<RumaRoomAccountDataEvent<
|
||||
MarkedUnreadEventContent,
|
||||
>, ()>(&RoomId::parse(
|
||||
&room_id,
|
||||
)?));
|
||||
|
||||
Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
listener.on_change(next.0.into(), room_id.clone());
|
||||
}
|
||||
}
|
||||
}))))
|
||||
}
|
||||
RoomAccountDataEventType::Tag => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(
|
||||
self.inner
|
||||
.observe_room_events::<RumaRoomAccountDataEvent<TagEventContent>, ()>(
|
||||
&RoomId::parse(&room_id)?,
|
||||
),
|
||||
);
|
||||
|
||||
Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
if let Ok(event) = next.0.try_into() {
|
||||
listener.on_change(event, room_id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}))))
|
||||
}
|
||||
RoomAccountDataEventType::UnstableMarkedUnread => {
|
||||
// Using an Arc here is mandatory or else the subscriber will never trigger
|
||||
let observer = Arc::new(self.inner.observe_room_events::<RumaRoomAccountDataEvent<
|
||||
UnstableMarkedUnreadEventContent,
|
||||
>, ()>(&RoomId::parse(
|
||||
&room_id,
|
||||
)?));
|
||||
|
||||
Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
|
||||
let mut subscriber = observer.subscribe();
|
||||
loop {
|
||||
if let Some(next) = subscriber.next().await {
|
||||
listener.on_change(next.0.into(), room_id.clone());
|
||||
}
|
||||
}
|
||||
}))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows generic GET requests to be made through the SDKs internal HTTP
|
||||
/// client
|
||||
pub async fn get_url(&self, url: String) -> Result<String, ClientError> {
|
||||
@@ -953,7 +1174,7 @@ impl Client {
|
||||
if let Some(raw_content) = self
|
||||
.inner
|
||||
.account()
|
||||
.fetch_account_data(GlobalAccountDataEventType::IgnoredUserList)
|
||||
.fetch_account_data(RumaGlobalAccountDataEventType::IgnoredUserList)
|
||||
.await?
|
||||
{
|
||||
let content = raw_content.deserialize_as::<IgnoredUserListEventContent>()?;
|
||||
|
||||
@@ -161,7 +161,7 @@ pub enum PushCondition {
|
||||
}
|
||||
|
||||
impl TryFrom<SdkPushCondition> for PushCondition {
|
||||
type Error = ();
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: SdkPushCondition) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
@@ -179,7 +179,7 @@ impl TryFrom<SdkPushCondition> for PushCondition {
|
||||
SdkPushCondition::EventPropertyContains { key, value } => {
|
||||
Self::EventPropertyContains { key, value: value.into() }
|
||||
}
|
||||
_ => return Err(()),
|
||||
_ => return Err("Unsupported condition type".to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{collections::BTreeSet, sync::Arc, time::Duration};
|
||||
use std::{
|
||||
collections::{BTreeSet, HashMap},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use extension_trait::extension_trait;
|
||||
use matrix_sdk::attachment::{BaseAudioInfo, BaseFileInfo, BaseImageInfo, BaseVideoInfo};
|
||||
@@ -20,8 +24,14 @@ use ruma::{
|
||||
assign,
|
||||
events::{
|
||||
call::notify::NotifyType as RumaNotifyType,
|
||||
direct::DirectEventContent,
|
||||
fully_read::FullyReadEventContent,
|
||||
identity_server::IdentityServerEventContent,
|
||||
ignored_user_list::{IgnoredUser as RumaIgnoredUser, IgnoredUserListEventContent},
|
||||
location::AssetType as RumaAssetType,
|
||||
marked_unread::{MarkedUnreadEventContent, UnstableMarkedUnreadEventContent},
|
||||
poll::start::PollKind as RumaPollKind,
|
||||
push_rules::PushRulesEventContent,
|
||||
room::{
|
||||
message::{
|
||||
AudioInfo as RumaAudioInfo,
|
||||
@@ -43,16 +53,39 @@ use ruma::{
|
||||
ImageInfo as RumaImageInfo, MediaSource as RumaMediaSource,
|
||||
ThumbnailInfo as RumaThumbnailInfo,
|
||||
},
|
||||
secret_storage::{
|
||||
default_key::SecretStorageDefaultKeyEventContent,
|
||||
key::{
|
||||
PassPhrase as RumaPassPhrase,
|
||||
SecretStorageEncryptionAlgorithm as RumaSecretStorageEncryptionAlgorithm,
|
||||
SecretStorageKeyEventContent,
|
||||
SecretStorageV1AesHmacSha2Properties as RumaSecretStorageV1AesHmacSha2Properties,
|
||||
},
|
||||
},
|
||||
tag::{
|
||||
TagEventContent, TagInfo as RumaTagInfo, TagName as RumaTagName,
|
||||
UserTagName as RumaUserTagName,
|
||||
},
|
||||
GlobalAccountDataEvent as RumaGlobalAccountDataEvent,
|
||||
GlobalAccountDataEventType as RumaGlobalAccountDataEventType,
|
||||
RoomAccountDataEvent as RumaRoomAccountDataEvent,
|
||||
RoomAccountDataEventType as RumaRoomAccountDataEventType,
|
||||
},
|
||||
matrix_uri::MatrixId as RumaMatrixId,
|
||||
push::{
|
||||
ConditionalPushRule as RumaConditionalPushRule, PatternedPushRule as RumaPatternedPushRule,
|
||||
Ruleset as RumaRuleset, SimplePushRule as RumaSimplePushRule,
|
||||
},
|
||||
serde::JsonObject,
|
||||
MatrixToUri, MatrixUri as RumaMatrixUri, OwnedUserId, UInt, UserId,
|
||||
KeyDerivationAlgorithm as RumaKeyDerivationAlgorithm, MatrixToUri, MatrixUri as RumaMatrixUri,
|
||||
OwnedRoomId, OwnedUserId, UInt, UserId,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::{ClientError, MediaInfoError},
|
||||
helpers::unwrap_or_clone_arc,
|
||||
notification_settings::{Action, PushCondition},
|
||||
timeline::MessageContent,
|
||||
utils::u64_to_uint,
|
||||
};
|
||||
@@ -985,3 +1018,591 @@ pub fn content_without_relation_from_message(
|
||||
let msg_type = message.msg_type.try_into()?;
|
||||
Ok(Arc::new(RoomMessageEventContentWithoutRelation::new(msg_type)))
|
||||
}
|
||||
|
||||
/// Types of global account data events.
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum AccountDataEventType {
|
||||
/// m.direct
|
||||
Direct,
|
||||
/// m.identity_server
|
||||
IdentityServer,
|
||||
/// m.ignored_user_list
|
||||
IgnoredUserList,
|
||||
/// m.push_rules
|
||||
PushRules,
|
||||
/// m.secret_storage.default_key
|
||||
SecretStorageDefaultKey,
|
||||
/// m.secret_storage.key.*
|
||||
SecretStorageKey(String),
|
||||
}
|
||||
|
||||
impl TryFrom<RumaGlobalAccountDataEventType> for AccountDataEventType {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaGlobalAccountDataEventType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
RumaGlobalAccountDataEventType::Direct => Ok(Self::Direct),
|
||||
RumaGlobalAccountDataEventType::IdentityServer => Ok(Self::IdentityServer),
|
||||
RumaGlobalAccountDataEventType::IgnoredUserList => Ok(Self::IgnoredUserList),
|
||||
RumaGlobalAccountDataEventType::PushRules => Ok(Self::PushRules),
|
||||
RumaGlobalAccountDataEventType::SecretStorageDefaultKey => {
|
||||
Ok(Self::SecretStorageDefaultKey)
|
||||
}
|
||||
RumaGlobalAccountDataEventType::SecretStorageKey(key_id) => {
|
||||
Ok(Self::SecretStorageKey(key_id))
|
||||
}
|
||||
_ => Err("Unsupported account data event type".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global account data events.
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum AccountDataEvent {
|
||||
/// m.direct
|
||||
Direct {
|
||||
/// The mapping of user ID to a list of room IDs of the ‘direct’ rooms
|
||||
/// for that user ID.
|
||||
map: HashMap<String, Vec<String>>,
|
||||
},
|
||||
/// m.identity_server
|
||||
IdentityServer {
|
||||
/// The base URL for the identity server for client-server connections.
|
||||
base_url: Option<String>,
|
||||
},
|
||||
/// m.ignored_user_list
|
||||
IgnoredUserList {
|
||||
/// The map of users to ignore. This is a mapping of user ID to empty
|
||||
/// object.
|
||||
ignored_users: HashMap<String, IgnoredUser>,
|
||||
},
|
||||
/// m.push_rules
|
||||
PushRules {
|
||||
/// The global ruleset.
|
||||
global: Ruleset,
|
||||
},
|
||||
/// m.secret_storage.default_key
|
||||
SecretStorageDefaultKey {
|
||||
/// The ID of the default key.
|
||||
key_id: String,
|
||||
},
|
||||
/// m.secret_storage.key.*
|
||||
SecretStorageKey {
|
||||
/// The ID of the key.
|
||||
key_id: String,
|
||||
|
||||
/// The name of the key.
|
||||
name: Option<String>,
|
||||
|
||||
/// The encryption algorithm used for this key.
|
||||
///
|
||||
/// Currently, only `m.secret_storage.v1.aes-hmac-sha2` is supported.
|
||||
algorithm: SecretStorageEncryptionAlgorithm,
|
||||
|
||||
/// The passphrase from which to generate the key.
|
||||
passphrase: Option<PassPhrase>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Details about an ignored user.
|
||||
///
|
||||
/// This is currently empty.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct IgnoredUser {}
|
||||
|
||||
impl From<RumaIgnoredUser> for IgnoredUser {
|
||||
fn from(_value: RumaIgnoredUser) -> Self {
|
||||
IgnoredUser {}
|
||||
}
|
||||
}
|
||||
|
||||
/// A push ruleset scopes a set of rules according to some criteria.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct Ruleset {
|
||||
/// These rules configure behavior for (unencrypted) messages that match
|
||||
/// certain patterns.
|
||||
pub content: Vec<PatternedPushRule>,
|
||||
|
||||
/// These user-configured rules are given the highest priority.
|
||||
///
|
||||
/// This field is named `override_` instead of `override` because the latter
|
||||
/// is a reserved keyword in Rust.
|
||||
pub override_: Vec<ConditionalPushRule>,
|
||||
|
||||
/// These rules change the behavior of all messages for a given room.
|
||||
pub room: Vec<SimplePushRule>,
|
||||
|
||||
/// These rules configure notification behavior for messages from a specific
|
||||
/// Matrix user ID.
|
||||
pub sender: Vec<SimplePushRule>,
|
||||
|
||||
/// These rules are identical to override rules, but have a lower priority
|
||||
/// than `content`, `room` and `sender` rules.
|
||||
pub underride: Vec<ConditionalPushRule>,
|
||||
}
|
||||
|
||||
impl TryFrom<RumaRuleset> for Ruleset {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaRuleset) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
content: value
|
||||
.content
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
override_: value
|
||||
.override_
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
room: value.room.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?,
|
||||
sender: value
|
||||
.sender
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
underride: value
|
||||
.underride
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`SimplePushRule`], but with an additional `pattern`` field.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct PatternedPushRule {
|
||||
/// Actions to determine if and how a notification is delivered for events
|
||||
/// matching this rule.
|
||||
pub actions: Vec<Action>,
|
||||
|
||||
/// Whether this is a default rule, or has been set explicitly.
|
||||
pub default: bool,
|
||||
|
||||
/// Whether the push rule is enabled or not.
|
||||
pub enabled: bool,
|
||||
|
||||
/// The ID of this rule.
|
||||
pub rule_id: String,
|
||||
|
||||
/// The glob-style pattern to match against.
|
||||
pub pattern: String,
|
||||
}
|
||||
|
||||
impl TryFrom<RumaPatternedPushRule> for PatternedPushRule {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaPatternedPushRule) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
actions: value
|
||||
.actions
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
default: value.default,
|
||||
enabled: value.enabled,
|
||||
rule_id: value.rule_id,
|
||||
pattern: value.pattern,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`SimplePushRule`], but with an additional `conditions` field.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct ConditionalPushRule {
|
||||
/// Actions to determine if and how a notification is delivered for events
|
||||
/// matching this rule.
|
||||
pub actions: Vec<Action>,
|
||||
|
||||
/// Whether this is a default rule, or has been set explicitly.
|
||||
pub default: bool,
|
||||
|
||||
/// Whether the push rule is enabled or not.
|
||||
pub enabled: bool,
|
||||
|
||||
/// The ID of this rule.
|
||||
pub rule_id: String,
|
||||
|
||||
/// The conditions that must hold true for an event in order for a rule to
|
||||
/// be applied to an event.
|
||||
///
|
||||
/// A rule with no conditions always matches.
|
||||
pub conditions: Vec<PushCondition>,
|
||||
}
|
||||
|
||||
impl TryFrom<RumaConditionalPushRule> for ConditionalPushRule {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaConditionalPushRule) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
actions: value
|
||||
.actions
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
default: value.default,
|
||||
enabled: value.enabled,
|
||||
rule_id: value.rule_id,
|
||||
conditions: value
|
||||
.conditions
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A push rule is a single rule that states under what conditions an event
|
||||
/// should be passed onto a push gateway and how the notification should be
|
||||
/// presented.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct SimplePushRule {
|
||||
/// Actions to determine if and how a notification is delivered for events
|
||||
/// matching this rule.
|
||||
pub actions: Vec<Action>,
|
||||
|
||||
/// Whether this is a default rule, or has been set explicitly.
|
||||
pub default: bool,
|
||||
|
||||
/// Whether the push rule is enabled or not.
|
||||
pub enabled: bool,
|
||||
|
||||
/// The ID of this rule.
|
||||
///
|
||||
/// This is generally the Matrix ID of the entity that it applies to.
|
||||
pub rule_id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<RumaSimplePushRule<OwnedRoomId>> for SimplePushRule {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaSimplePushRule<OwnedRoomId>) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
actions: value
|
||||
.actions
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
default: value.default,
|
||||
enabled: value.enabled,
|
||||
rule_id: value.rule_id.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RumaSimplePushRule<OwnedUserId>> for SimplePushRule {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaSimplePushRule<OwnedUserId>) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
actions: value
|
||||
.actions
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
default: value.default,
|
||||
enabled: value.enabled,
|
||||
rule_id: value.rule_id.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An algorithm and its properties, used to encrypt a secret.
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum SecretStorageEncryptionAlgorithm {
|
||||
/// Encrypted using the `m.secret_storage.v1.aes-hmac-sha2` algorithm.
|
||||
///
|
||||
/// Secrets using this method are encrypted using AES-CTR-256 and
|
||||
/// authenticated using HMAC-SHA-256.
|
||||
V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties),
|
||||
}
|
||||
|
||||
impl TryFrom<RumaSecretStorageEncryptionAlgorithm> for SecretStorageEncryptionAlgorithm {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaSecretStorageEncryptionAlgorithm) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
RumaSecretStorageEncryptionAlgorithm::V1AesHmacSha2(properties) => {
|
||||
Ok(Self::V1AesHmacSha2(properties.into()))
|
||||
}
|
||||
_ => Err("Unsupported encryption algorithm".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The key properties for the `m.secret_storage.v1.aes-hmac-sha2`` algorithm.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct SecretStorageV1AesHmacSha2Properties {
|
||||
/// The 16-byte initialization vector, encoded as base64.
|
||||
pub iv: Option<String>,
|
||||
|
||||
/// The MAC, encoded as base64.
|
||||
pub mac: Option<String>,
|
||||
}
|
||||
|
||||
impl From<RumaSecretStorageV1AesHmacSha2Properties> for SecretStorageV1AesHmacSha2Properties {
|
||||
fn from(value: RumaSecretStorageV1AesHmacSha2Properties) -> Self {
|
||||
Self {
|
||||
iv: value.iv.map(|base64| base64.encode()),
|
||||
mac: value.mac.map(|base64| base64.encode()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A passphrase from which a key is to be derived.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct PassPhrase {
|
||||
/// The algorithm to use to generate the key from the passphrase.
|
||||
///
|
||||
/// Must be `m.pbkdf2`.
|
||||
pub algorithm: KeyDerivationAlgorithm,
|
||||
|
||||
/// The salt used in PBKDF2.
|
||||
pub salt: String,
|
||||
|
||||
/// The number of iterations to use in PBKDF2.
|
||||
pub iterations: u64,
|
||||
|
||||
/// The number of bits to generate for the key.
|
||||
///
|
||||
/// Defaults to 256
|
||||
pub bits: u64,
|
||||
}
|
||||
|
||||
impl TryFrom<RumaPassPhrase> for PassPhrase {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaPassPhrase) -> Result<Self, Self::Error> {
|
||||
Ok(PassPhrase {
|
||||
algorithm: value.algorithm.try_into()?,
|
||||
salt: value.salt,
|
||||
iterations: value.iterations.into(),
|
||||
bits: value.bits.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A key algorithm to be used to generate a key from a passphrase.
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum KeyDerivationAlgorithm {
|
||||
/// PBKDF2
|
||||
Pbkfd2,
|
||||
}
|
||||
|
||||
impl TryFrom<RumaKeyDerivationAlgorithm> for KeyDerivationAlgorithm {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaKeyDerivationAlgorithm) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
RumaKeyDerivationAlgorithm::Pbkfd2 => Ok(Self::Pbkfd2),
|
||||
_ => Err("Unsupported key derivation algorithm".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaGlobalAccountDataEvent<DirectEventContent>> for AccountDataEvent {
|
||||
fn from(value: RumaGlobalAccountDataEvent<DirectEventContent>) -> Self {
|
||||
Self::Direct {
|
||||
map: value
|
||||
.content
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|(user_id, room_ids)| {
|
||||
(user_id.to_string(), room_ids.iter().map(ToString::to_string).collect())
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaGlobalAccountDataEvent<IdentityServerEventContent>> for AccountDataEvent {
|
||||
fn from(value: RumaGlobalAccountDataEvent<IdentityServerEventContent>) -> Self {
|
||||
Self::IdentityServer { base_url: value.content.base_url.into_option() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaGlobalAccountDataEvent<IgnoredUserListEventContent>> for AccountDataEvent {
|
||||
fn from(value: RumaGlobalAccountDataEvent<IgnoredUserListEventContent>) -> Self {
|
||||
Self::IgnoredUserList {
|
||||
ignored_users: value
|
||||
.content
|
||||
.ignored_users
|
||||
.into_iter()
|
||||
.map(|(user_id, ignored_user)| {
|
||||
(user_id.to_string(), IgnoredUser::from(ignored_user))
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RumaGlobalAccountDataEvent<PushRulesEventContent>> for AccountDataEvent {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(
|
||||
value: RumaGlobalAccountDataEvent<PushRulesEventContent>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self::PushRules { global: value.content.global.try_into()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaGlobalAccountDataEvent<SecretStorageDefaultKeyEventContent>> for AccountDataEvent {
|
||||
fn from(value: RumaGlobalAccountDataEvent<SecretStorageDefaultKeyEventContent>) -> Self {
|
||||
Self::SecretStorageDefaultKey { key_id: value.content.key_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RumaGlobalAccountDataEvent<SecretStorageKeyEventContent>> for AccountDataEvent {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(
|
||||
value: RumaGlobalAccountDataEvent<SecretStorageKeyEventContent>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self::SecretStorageKey {
|
||||
key_id: value.content.key_id,
|
||||
name: value.content.name,
|
||||
algorithm: value.content.algorithm.try_into()?,
|
||||
passphrase: value.content.passphrase.map(TryInto::try_into).transpose()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Types of room account data events.
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum RoomAccountDataEventType {
|
||||
/// m.fully_read
|
||||
FullyRead,
|
||||
/// m.marked_unread
|
||||
MarkedUnread,
|
||||
/// m.tag
|
||||
Tag,
|
||||
/// com.famedly.marked_unread
|
||||
UnstableMarkedUnread,
|
||||
}
|
||||
|
||||
impl TryFrom<RumaRoomAccountDataEventType> for RoomAccountDataEventType {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaRoomAccountDataEventType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
RumaRoomAccountDataEventType::FullyRead => Ok(Self::FullyRead),
|
||||
RumaRoomAccountDataEventType::MarkedUnread => Ok(Self::MarkedUnread),
|
||||
RumaRoomAccountDataEventType::Tag => Ok(Self::Tag),
|
||||
RumaRoomAccountDataEventType::UnstableMarkedUnread => Ok(Self::UnstableMarkedUnread),
|
||||
_ => Err("Unsupported account data event type".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Room account data events.
|
||||
#[derive(Clone, uniffi::Enum)]
|
||||
pub enum RoomAccountDataEvent {
|
||||
/// m.fully_read
|
||||
FullyReadEvent {
|
||||
/// The event the user's read marker is located at in the room.
|
||||
event_id: String,
|
||||
},
|
||||
/// m.marked_unread
|
||||
MarkedUnread {
|
||||
/// The current unread state.
|
||||
unread: bool,
|
||||
},
|
||||
/// m.tag
|
||||
Tag { tags: HashMap<TagName, TagInfo> },
|
||||
/// com.famedly.marked_unread
|
||||
UnstableMarkedUnread {
|
||||
/// The current unread state.
|
||||
unread: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// The name of a tag.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, uniffi::Enum)]
|
||||
pub enum TagName {
|
||||
/// `m.favourite`: The user's favorite rooms.
|
||||
Favorite,
|
||||
|
||||
/// `m.lowpriority`: These should be shown with lower precedence than
|
||||
/// others.
|
||||
LowPriority,
|
||||
|
||||
/// `m.server_notice`: Used to identify
|
||||
ServerNotice,
|
||||
|
||||
/// `u.*`: User-defined tag
|
||||
User(UserTagName),
|
||||
}
|
||||
|
||||
impl TryFrom<RumaTagName> for TagName {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaTagName) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
RumaTagName::Favorite => Ok(Self::Favorite),
|
||||
RumaTagName::LowPriority => Ok(Self::LowPriority),
|
||||
RumaTagName::ServerNotice => Ok(Self::ServerNotice),
|
||||
RumaTagName::User(name) => Ok(Self::User(name.into())),
|
||||
_ => Err("Unsupported tag name".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A user-defined tag name.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, uniffi::Record)]
|
||||
pub struct UserTagName {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl From<RumaUserTagName> for UserTagName {
|
||||
fn from(value: RumaUserTagName) -> Self {
|
||||
Self { name: value.as_ref().to_owned() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a tag.
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct TagInfo {
|
||||
/// Value to use for lexicographically ordering rooms with this tag.
|
||||
pub order: Option<f64>,
|
||||
}
|
||||
|
||||
impl From<RumaTagInfo> for TagInfo {
|
||||
fn from(value: RumaTagInfo) -> Self {
|
||||
Self { order: value.order }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaRoomAccountDataEvent<FullyReadEventContent>> for RoomAccountDataEvent {
|
||||
fn from(value: RumaRoomAccountDataEvent<FullyReadEventContent>) -> Self {
|
||||
Self::FullyReadEvent { event_id: value.content.event_id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaRoomAccountDataEvent<MarkedUnreadEventContent>> for RoomAccountDataEvent {
|
||||
fn from(value: RumaRoomAccountDataEvent<MarkedUnreadEventContent>) -> Self {
|
||||
Self::MarkedUnread { unread: value.content.unread }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RumaRoomAccountDataEvent<TagEventContent>> for RoomAccountDataEvent {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: RumaRoomAccountDataEvent<TagEventContent>) -> Result<Self, Self::Error> {
|
||||
Ok(Self::Tag {
|
||||
tags: value
|
||||
.content
|
||||
.tags
|
||||
.into_iter()
|
||||
.map(|(name, info)| name.try_into().map(|name| (name, info.into())))
|
||||
.collect::<Result<HashMap<TagName, _>, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaRoomAccountDataEvent<UnstableMarkedUnreadEventContent>> for RoomAccountDataEvent {
|
||||
fn from(value: RumaRoomAccountDataEvent<UnstableMarkedUnreadEventContent>) -> Self {
|
||||
Self::UnstableMarkedUnread { unread: value.content.unread }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user