atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
bindings/matrix-sdk-ffi/src/event.rsRust743 lines31.1 KB
1
// Copyright 2025 The Matrix.org Foundation C.I.C.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for that specific language governing permissions and
13
// limitations under the License.
14
15
use anyhow::{Context, bail};
16
use matrix_sdk::IdParseError;
17
use matrix_sdk_ui::timeline::TimelineEventItemId;
18
use ruma::{
19
    EventId,
20
    events::{
21
        AnySyncMessageLikeEvent, AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent,
22
        MessageLikeEventContent as RumaMessageLikeEventContent, RedactContent,
23
        RedactedStateEventContent, StaticStateEventContent, SyncMessageLikeEvent, SyncStateEvent,
24
        TimelineEventType as RumaTimelineEventType,
25
        room::{
26
            encrypted,
27
            message::{MessageType as RumaMessageType, Relation},
28
            redaction::SyncRoomRedactionEvent,
29
        },
30
    },
31
};
32
33
use crate::{
34
    ClientError,
35
    room_member::MembershipState,
36
    ruma::{MessageType, RtcCallIntent, RtcNotificationType},
37
    utils::Timestamp,
38
};
39
40
#[derive(uniffi::Object)]
41
pub struct TimelineEvent(pub(crate) Box<AnySyncTimelineEvent>);
42
43
#[matrix_sdk_ffi_macros::export]
44
impl TimelineEvent {
45
    pub fn event_id(&self) -> String {
46
        self.0.event_id().to_string()
47
    }
48
49
    pub fn sender_id(&self) -> String {
50
        self.0.sender().to_string()
51
    }
52
53
    pub fn timestamp(&self) -> Timestamp {
54
        self.0.origin_server_ts().into()
55
    }
56
57
    pub fn content(&self) -> Result<TimelineEventContent, ClientError> {
58
        let content = match &*self.0 {
59
            AnySyncTimelineEvent::MessageLike(event) => {
60
                TimelineEventContent::MessageLike { content: event.clone().try_into()? }
61
            }
62
            AnySyncTimelineEvent::State(event) => {
63
                TimelineEventContent::State { content: event.clone().try_into()? }
64
            }
65
        };
66
        Ok(content)
67
    }
68
69
    /// Returns the thread root event id for the event, if it's part of a
70
    /// thread.
71
    pub fn thread_root_event_id(&self) -> Option<String> {
72
        match &*self.0 {
73
            AnySyncTimelineEvent::MessageLike(event) => {
74
                match event.original_content().and_then(|content| content.relation()) {
75
                    Some(encrypted::Relation::Thread(thread)) => Some(thread.event_id.to_string()),
76
                    _ => None,
77
                }
78
            }
79
            AnySyncTimelineEvent::State(_) => None,
80
        }
81
    }
82
}
83
84
impl From<AnyTimelineEvent> for TimelineEvent {
85
    fn from(event: AnyTimelineEvent) -> Self {
86
        Self(Box::new(event.into()))
87
    }
88
}
89
90
/// The timeline event type.
91
#[derive(Clone, uniffi::Enum, PartialEq, Eq, Hash)]
92
#[uniffi::export(Eq, Hash)]
93
pub enum TimelineEventType {
94
    /// The event is a message-like one and should be displayed as such.
95
    MessageLike { value: MessageLikeEventType },
96
    /// The event is a state event, and may or may not be displayed in the
97
    /// timeline.
98
    State { value: StateEventType },
99
}
100
101
impl From<RumaTimelineEventType> for TimelineEventType {
102
    fn from(value: RumaTimelineEventType) -> Self {
103
        match value {
104
            RumaTimelineEventType::Audio => {
105
                Self::MessageLike { value: MessageLikeEventType::Audio }
106
            }
107
            RumaTimelineEventType::File => Self::MessageLike { value: MessageLikeEventType::File },
108
            RumaTimelineEventType::Image => {
109
                Self::MessageLike { value: MessageLikeEventType::Image }
110
            }
111
            RumaTimelineEventType::Video => {
112
                Self::MessageLike { value: MessageLikeEventType::Video }
113
            }
114
            RumaTimelineEventType::Voice => {
115
                Self::MessageLike { value: MessageLikeEventType::Voice }
116
            }
117
            RumaTimelineEventType::Emote => {
118
                Self::MessageLike { value: MessageLikeEventType::Emote }
119
            }
120
            RumaTimelineEventType::Encrypted => {
121
                Self::MessageLike { value: MessageLikeEventType::Encrypted }
122
            }
123
            RumaTimelineEventType::RoomMessage => {
124
                Self::MessageLike { value: MessageLikeEventType::RoomMessage }
125
            }
126
            RumaTimelineEventType::CallAnswer => {
127
                Self::MessageLike { value: MessageLikeEventType::CallAnswer }
128
            }
129
            RumaTimelineEventType::CallInvite => {
130
                Self::MessageLike { value: MessageLikeEventType::CallInvite }
131
            }
132
            RumaTimelineEventType::CallHangup => {
133
                Self::MessageLike { value: MessageLikeEventType::CallHangup }
134
            }
135
            RumaTimelineEventType::CallCandidates => {
136
                Self::MessageLike { value: MessageLikeEventType::CallCandidates }
137
            }
138
            RumaTimelineEventType::CallNegotiate => {
139
                Self::MessageLike { value: MessageLikeEventType::CallNegotiate }
140
            }
141
            RumaTimelineEventType::CallReject => {
142
                Self::MessageLike { value: MessageLikeEventType::CallReject }
143
            }
144
            RumaTimelineEventType::CallSdpStreamMetadataChanged => {
145
                Self::MessageLike { value: MessageLikeEventType::CallSdpStreamMetadataChanged }
146
            }
147
            RumaTimelineEventType::CallSelectAnswer => {
148
                Self::MessageLike { value: MessageLikeEventType::CallSelectAnswer }
149
            }
150
            RumaTimelineEventType::KeyVerificationReady => {
151
                Self::MessageLike { value: MessageLikeEventType::KeyVerificationReady }
152
            }
153
            RumaTimelineEventType::KeyVerificationStart => {
154
                Self::MessageLike { value: MessageLikeEventType::KeyVerificationStart }
155
            }
156
            RumaTimelineEventType::KeyVerificationCancel => {
157
                Self::MessageLike { value: MessageLikeEventType::KeyVerificationCancel }
158
            }
159
            RumaTimelineEventType::KeyVerificationAccept => {
160
                Self::MessageLike { value: MessageLikeEventType::KeyVerificationAccept }
161
            }
162
            RumaTimelineEventType::KeyVerificationKey => {
163
                Self::MessageLike { value: MessageLikeEventType::KeyVerificationKey }
164
            }
165
            RumaTimelineEventType::KeyVerificationMac => {
166
                Self::MessageLike { value: MessageLikeEventType::KeyVerificationMac }
167
            }
168
            RumaTimelineEventType::KeyVerificationDone => {
169
                Self::MessageLike { value: MessageLikeEventType::KeyVerificationDone }
170
            }
171
            RumaTimelineEventType::Location => {
172
                Self::MessageLike { value: MessageLikeEventType::Location }
173
            }
174
            RumaTimelineEventType::Message => {
175
                Self::MessageLike { value: MessageLikeEventType::Message }
176
            }
177
            RumaTimelineEventType::PollStart => {
178
                Self::MessageLike { value: MessageLikeEventType::PollStart }
179
            }
180
            RumaTimelineEventType::UnstablePollStart => {
181
                Self::MessageLike { value: MessageLikeEventType::UnstablePollStart }
182
            }
183
            RumaTimelineEventType::PollResponse => {
184
                Self::MessageLike { value: MessageLikeEventType::PollResponse }
185
            }
186
            RumaTimelineEventType::UnstablePollResponse => {
187
                Self::MessageLike { value: MessageLikeEventType::UnstablePollResponse }
188
            }
189
            RumaTimelineEventType::PollEnd => {
190
                Self::MessageLike { value: MessageLikeEventType::PollEnd }
191
            }
192
            RumaTimelineEventType::UnstablePollEnd => {
193
                Self::MessageLike { value: MessageLikeEventType::UnstablePollEnd }
194
            }
195
            RumaTimelineEventType::Beacon => {
196
                Self::MessageLike { value: MessageLikeEventType::Beacon }
197
            }
198
            RumaTimelineEventType::Reaction => {
199
                Self::MessageLike { value: MessageLikeEventType::Reaction }
200
            }
201
            RumaTimelineEventType::RoomEncrypted => {
202
                Self::MessageLike { value: MessageLikeEventType::RoomEncrypted }
203
            }
204
            RumaTimelineEventType::RoomRedaction => {
205
                Self::MessageLike { value: MessageLikeEventType::RoomRedaction }
206
            }
207
            RumaTimelineEventType::Sticker => {
208
                Self::MessageLike { value: MessageLikeEventType::Sticker }
209
            }
210
            RumaTimelineEventType::CallNotify => {
211
                Self::MessageLike { value: MessageLikeEventType::CallNotify }
212
            }
213
            RumaTimelineEventType::RtcNotification => {
214
                Self::MessageLike { value: MessageLikeEventType::RtcNotification }
215
            }
216
            RumaTimelineEventType::RtcDecline => {
217
                Self::MessageLike { value: MessageLikeEventType::RtcDecline }
218
            }
219
            RumaTimelineEventType::PolicyRuleRoom => {
220
                Self::State { value: StateEventType::PolicyRuleRoom }
221
            }
222
            RumaTimelineEventType::PolicyRuleServer => {
223
                Self::State { value: StateEventType::PolicyRuleServer }
224
            }
225
            RumaTimelineEventType::PolicyRuleUser => {
226
                Self::State { value: StateEventType::PolicyRuleUser }
227
            }
228
            RumaTimelineEventType::RoomAvatar => Self::State { value: StateEventType::RoomAvatar },
229
            RumaTimelineEventType::RoomCanonicalAlias => {
230
                Self::State { value: StateEventType::RoomCanonicalAlias }
231
            }
232
            RumaTimelineEventType::RoomCreate => Self::State { value: StateEventType::RoomCreate },
233
            RumaTimelineEventType::RoomEncryption => {
234
                Self::State { value: StateEventType::RoomEncryption }
235
            }
236
            RumaTimelineEventType::RoomGuestAccess => {
237
                Self::State { value: StateEventType::RoomGuestAccess }
238
            }
239
            RumaTimelineEventType::RoomHistoryVisibility => {
240
                Self::State { value: StateEventType::RoomHistoryVisibility }
241
            }
242
            RumaTimelineEventType::RoomJoinRules => {
243
                Self::State { value: StateEventType::RoomJoinRules }
244
            }
245
            RumaTimelineEventType::RoomMember => {
246
                Self::State { value: StateEventType::RoomMemberEvent }
247
            }
248
            RumaTimelineEventType::RoomLanguage => {
249
                Self::State { value: StateEventType::RoomLanguage }
250
            }
251
            RumaTimelineEventType::RoomName => Self::State { value: StateEventType::RoomName },
252
            RumaTimelineEventType::RoomImagePack => {
253
                Self::State { value: StateEventType::RoomImagePack }
254
            }
255
            RumaTimelineEventType::RoomPinnedEvents => {
256
                Self::State { value: StateEventType::RoomPinnedEvents }
257
            }
258
            RumaTimelineEventType::RoomPowerLevels => {
259
                Self::State { value: StateEventType::RoomPowerLevels }
260
            }
261
            RumaTimelineEventType::RoomServerAcl => {
262
                Self::State { value: StateEventType::RoomServerAcl }
263
            }
264
            RumaTimelineEventType::RoomThirdPartyInvite => {
265
                Self::State { value: StateEventType::RoomThirdPartyInvite }
266
            }
267
            RumaTimelineEventType::RoomTombstone => {
268
                Self::State { value: StateEventType::RoomTombstone }
269
            }
270
            RumaTimelineEventType::RoomTopic => Self::State { value: StateEventType::RoomTopic },
271
            RumaTimelineEventType::SpaceChild => Self::State { value: StateEventType::SpaceChild },
272
            RumaTimelineEventType::SpaceParent => {
273
                Self::State { value: StateEventType::SpaceParent }
274
            }
275
            RumaTimelineEventType::BeaconInfo => Self::State { value: StateEventType::BeaconInfo },
276
            RumaTimelineEventType::CallMember => Self::State { value: StateEventType::CallMember },
277
            RumaTimelineEventType::MemberHints => {
278
                Self::State { value: StateEventType::MemberHints }
279
            }
280
            RumaTimelineEventType::_Custom(_) => {
281
                Self::State { value: StateEventType::Custom { value: value.to_string() } }
282
            }
283
            _ => Self::MessageLike { value: MessageLikeEventType::Other(value.to_string()) },
284
        }
285
    }
286
}
287
288
#[derive(uniffi::Enum)]
289
// A note about this `allow(clippy::large_enum_variant)`.
290
// In order to reduce the size of `TimelineEventContent`, we would need to
291
// put some parts in a `Box`, or an `Arc`. Sadly, it doesn't play well with
292
// UniFFI. We would need to change the `uniffi::Record` of the subtypes into
293
// `uniffi::Object`, which is a radical change. It would simplify the memory
294
// usage, but it would slow down the performance around the FFI border. Thus,
295
// let's consider this is a false-positive lint in this particular case.
296
#[allow(clippy::large_enum_variant)]
297
pub enum TimelineEventContent {
298
    MessageLike { content: MessageLikeEventContent },
299
    State { content: StateEventContent },
300
}
301
302
#[derive(uniffi::Enum)]
303
pub enum StateEventContent {
304
    PolicyRuleRoom,
305
    PolicyRuleServer,
306
    PolicyRuleUser,
307
    RoomAvatar,
308
    RoomCanonicalAlias,
309
    RoomCreate,
310
    RoomEncryption,
311
    RoomGuestAccess,
312
    RoomHistoryVisibility,
313
    RoomJoinRules,
314
    RoomMemberContent { user_id: String, membership_state: MembershipState },
315
    RoomName,
316
    RoomPinnedEvents,
317
    RoomPowerLevels,
318
    RoomServerAcl,
319
    RoomThirdPartyInvite,
320
    RoomTombstone,
321
    RoomTopic { topic: String },
322
    SpaceChild,
323
    SpaceParent,
324
}
325
326
impl TryFrom<AnySyncStateEvent> for StateEventContent {
327
    type Error = anyhow::Error;
328
329
    fn try_from(value: AnySyncStateEvent) -> anyhow::Result<Self> {
330
        let event = match value {
331
            AnySyncStateEvent::PolicyRuleRoom(_) => StateEventContent::PolicyRuleRoom,
332
            AnySyncStateEvent::PolicyRuleServer(_) => StateEventContent::PolicyRuleServer,
333
            AnySyncStateEvent::PolicyRuleUser(_) => StateEventContent::PolicyRuleUser,
334
            AnySyncStateEvent::RoomAvatar(_) => StateEventContent::RoomAvatar,
335
            AnySyncStateEvent::RoomCanonicalAlias(_) => StateEventContent::RoomCanonicalAlias,
336
            AnySyncStateEvent::RoomCreate(_) => StateEventContent::RoomCreate,
337
            AnySyncStateEvent::RoomEncryption(_) => StateEventContent::RoomEncryption,
338
            AnySyncStateEvent::RoomGuestAccess(_) => StateEventContent::RoomGuestAccess,
339
            AnySyncStateEvent::RoomHistoryVisibility(_) => StateEventContent::RoomHistoryVisibility,
340
            AnySyncStateEvent::RoomJoinRules(_) => StateEventContent::RoomJoinRules,
341
            AnySyncStateEvent::RoomMember(content) => {
342
                let state_key = content.state_key().to_string();
343
                let original_content = get_state_event_original_content(content)?;
344
                StateEventContent::RoomMemberContent {
345
                    user_id: state_key,
346
                    membership_state: original_content.membership.try_into()?,
347
                }
348
            }
349
            AnySyncStateEvent::RoomName(_) => StateEventContent::RoomName,
350
            AnySyncStateEvent::RoomPinnedEvents(_) => StateEventContent::RoomPinnedEvents,
351
            AnySyncStateEvent::RoomPowerLevels(_) => StateEventContent::RoomPowerLevels,
352
            AnySyncStateEvent::RoomServerAcl(_) => StateEventContent::RoomServerAcl,
353
            AnySyncStateEvent::RoomThirdPartyInvite(_) => StateEventContent::RoomThirdPartyInvite,
354
            AnySyncStateEvent::RoomTombstone(_) => StateEventContent::RoomTombstone,
355
            AnySyncStateEvent::RoomTopic(content) => {
356
                let content = get_state_event_original_content(content)?;
357
358
                StateEventContent::RoomTopic { topic: content.topic }
359
            }
360
            AnySyncStateEvent::SpaceChild(_) => StateEventContent::SpaceChild,
361
            AnySyncStateEvent::SpaceParent(_) => StateEventContent::SpaceParent,
362
            _ => bail!("Unsupported state event: {:?}", value.event_type()),
363
        };
364
        Ok(event)
365
    }
366
}
367
368
#[derive(uniffi::Enum)]
369
// A note about this `allow(clippy::large_enum_variant)`.
370
// In order to reduce the size of `MessageLineEventContent`, we would need to
371
// put some parts in a `Box`, or an `Arc`. Sadly, it doesn't play well with
372
// UniFFI. We would need to change the `uniffi::Record` of the subtypes into
373
// `uniffi::Object`, which is a radical change. It would simplify the memory
374
// usage, but it would slow down the performance around the FFI border. Thus,
375
// let's consider this is a false-positive lint in this particular case.
376
#[allow(clippy::large_enum_variant)]
377
pub enum MessageLikeEventContent {
378
    CallAnswer,
379
    CallInvite,
380
    RtcNotification {
381
        notification_type: RtcNotificationType,
382
        /// The timestamp at which this notification is considered invalid.
383
        expiration_ts: Timestamp,
384
        /// Soft indication of whether it is an audio or video call.
385
        call_intent: Option<RtcCallIntent>,
386
    },
387
    CallHangup,
388
    CallCandidates,
389
    KeyVerificationReady,
390
    KeyVerificationStart,
391
    KeyVerificationCancel,
392
    KeyVerificationAccept,
393
    KeyVerificationKey,
394
    KeyVerificationMac,
395
    KeyVerificationDone,
396
    Poll {
397
        question: String,
398
    },
399
    ReactionContent {
400
        related_event_id: String,
401
    },
402
    RoomEncrypted,
403
    RoomMessage {
404
        message_type: MessageType,
405
        in_reply_to_event_id: Option<String>,
406
    },
407
    RoomRedaction {
408
        redacted_event_id: Option<String>,
409
        reason: Option<String>,
410
    },
411
    Sticker,
412
}
413
414
impl TryFrom<AnySyncMessageLikeEvent> for MessageLikeEventContent {
415
    type Error = anyhow::Error;
416
417
    fn try_from(value: AnySyncMessageLikeEvent) -> anyhow::Result<Self> {
418
        let content = match value {
419
            AnySyncMessageLikeEvent::CallAnswer(_) => MessageLikeEventContent::CallAnswer,
420
            AnySyncMessageLikeEvent::CallInvite(_) => MessageLikeEventContent::CallInvite,
421
            AnySyncMessageLikeEvent::RtcNotification(event) => {
422
                let origin_server_ts = event.origin_server_ts();
423
                let original_content = get_message_like_event_original_content(event)?;
424
                let expiration_ts = original_content.expiration_ts(origin_server_ts, None).into();
425
                MessageLikeEventContent::RtcNotification {
426
                    notification_type: original_content.notification_type.into(),
427
                    expiration_ts,
428
                    call_intent: original_content.call_intent.map(|intent| intent.into()),
429
                }
430
            }
431
            AnySyncMessageLikeEvent::CallHangup(_) => MessageLikeEventContent::CallHangup,
432
            AnySyncMessageLikeEvent::CallCandidates(_) => MessageLikeEventContent::CallCandidates,
433
            AnySyncMessageLikeEvent::KeyVerificationReady(_) => {
434
                MessageLikeEventContent::KeyVerificationReady
435
            }
436
            AnySyncMessageLikeEvent::KeyVerificationStart(_) => {
437
                MessageLikeEventContent::KeyVerificationStart
438
            }
439
            AnySyncMessageLikeEvent::KeyVerificationCancel(_) => {
440
                MessageLikeEventContent::KeyVerificationCancel
441
            }
442
            AnySyncMessageLikeEvent::KeyVerificationAccept(_) => {
443
                MessageLikeEventContent::KeyVerificationAccept
444
            }
445
            AnySyncMessageLikeEvent::KeyVerificationKey(_) => {
446
                MessageLikeEventContent::KeyVerificationKey
447
            }
448
            AnySyncMessageLikeEvent::KeyVerificationMac(_) => {
449
                MessageLikeEventContent::KeyVerificationMac
450
            }
451
            AnySyncMessageLikeEvent::KeyVerificationDone(_) => {
452
                MessageLikeEventContent::KeyVerificationDone
453
            }
454
            AnySyncMessageLikeEvent::UnstablePollStart(content) => {
455
                let original_content = get_message_like_event_original_content(content)?;
456
                MessageLikeEventContent::Poll {
457
                    question: original_content.poll_start().question.text.clone(),
458
                }
459
            }
460
            AnySyncMessageLikeEvent::Reaction(content) => {
461
                let original_content = get_message_like_event_original_content(content)?;
462
                MessageLikeEventContent::ReactionContent {
463
                    related_event_id: original_content.relates_to.event_id.to_string(),
464
                }
465
            }
466
            AnySyncMessageLikeEvent::RoomEncrypted(_) => MessageLikeEventContent::RoomEncrypted,
467
            AnySyncMessageLikeEvent::RoomMessage(content) => {
468
                let original_content = get_message_like_event_original_content(content)?;
469
                let in_reply_to_event_id =
470
                    original_content.relates_to.and_then(|relation| match relation {
471
                        Relation::Reply(reply) => Some(reply.in_reply_to.event_id.to_string()),
472
                        _ => None,
473
                    });
474
                MessageLikeEventContent::RoomMessage {
475
                    message_type: original_content.msgtype.try_into()?,
476
                    in_reply_to_event_id,
477
                }
478
            }
479
            AnySyncMessageLikeEvent::RoomRedaction(c) => {
480
                let (redacted_event_id, reason) = match c {
481
                    SyncRoomRedactionEvent::Original(o) => {
482
                        let id =
483
                            if o.content.redacts.is_some() { o.content.redacts } else { o.redacts };
484
                        (id.map(|id| id.to_string()), o.content.reason)
485
                    }
486
                    SyncRoomRedactionEvent::Redacted(_) => (None, None),
487
                };
488
                MessageLikeEventContent::RoomRedaction { redacted_event_id, reason }
489
            }
490
            AnySyncMessageLikeEvent::Sticker(_) => MessageLikeEventContent::Sticker,
491
            _ => bail!("Unsupported Event Type: {:?}", value.event_type()),
492
        };
493
        Ok(content)
494
    }
495
}
496
497
fn get_state_event_original_content<C>(event: SyncStateEvent<C>) -> anyhow::Result<C>
498
where
499
    C: StaticStateEventContent + RedactContent + Clone,
500
    <C as RedactContent>::Redacted: RedactedStateEventContent<StateKey = C::StateKey>,
501
{
502
    let original_content =
503
        event.as_original().context("Failed to get original content")?.content.clone();
504
    Ok(original_content)
505
}
506
507
fn get_message_like_event_original_content<C>(event: SyncMessageLikeEvent<C>) -> anyhow::Result<C>
508
where
509
    C: RumaMessageLikeEventContent + RedactContent + Clone,
510
    <C as ruma::events::RedactContent>::Redacted: ruma::events::RedactedMessageLikeEventContent,
511
{
512
    let original_content =
513
        event.as_original().context("Failed to get original content")?.content.clone();
514
    Ok(original_content)
515
}
516
517
#[derive(Clone, uniffi::Enum, PartialEq, Eq, Hash)]
518
pub enum StateEventType {
519
    BeaconInfo,
520
    CallMember,
521
    MemberHints,
522
    PolicyRuleRoom,
523
    PolicyRuleServer,
524
    PolicyRuleUser,
525
    RoomAvatar,
526
    RoomCanonicalAlias,
527
    RoomCreate,
528
    RoomEncryption,
529
    RoomGuestAccess,
530
    RoomHistoryVisibility,
531
    RoomImagePack,
532
    RoomJoinRules,
533
    RoomMemberEvent,
534
    RoomLanguage,
535
    RoomName,
536
    RoomPinnedEvents,
537
    RoomPowerLevels,
538
    RoomServerAcl,
539
    RoomThirdPartyInvite,
540
    RoomTombstone,
541
    RoomTopic,
542
    SpaceChild,
543
    SpaceParent,
544
    Custom { value: String },
545
}
546
547
impl From<StateEventType> for ruma::events::StateEventType {
548
    fn from(val: StateEventType) -> Self {
549
        match val {
550
            StateEventType::BeaconInfo => Self::BeaconInfo,
551
            StateEventType::CallMember => Self::CallMember,
552
            StateEventType::MemberHints => Self::MemberHints,
553
            StateEventType::PolicyRuleRoom => Self::PolicyRuleRoom,
554
            StateEventType::PolicyRuleServer => Self::PolicyRuleServer,
555
            StateEventType::PolicyRuleUser => Self::PolicyRuleUser,
556
            StateEventType::RoomAvatar => Self::RoomAvatar,
557
            StateEventType::RoomCanonicalAlias => Self::RoomCanonicalAlias,
558
            StateEventType::RoomCreate => Self::RoomCreate,
559
            StateEventType::RoomEncryption => Self::RoomEncryption,
560
            StateEventType::RoomGuestAccess => Self::RoomGuestAccess,
561
            StateEventType::RoomHistoryVisibility => Self::RoomHistoryVisibility,
562
            StateEventType::RoomImagePack => Self::RoomImagePack,
563
            StateEventType::RoomJoinRules => Self::RoomJoinRules,
564
            StateEventType::RoomLanguage => Self::RoomLanguage,
565
            StateEventType::RoomMemberEvent => Self::RoomMember,
566
            StateEventType::RoomName => Self::RoomName,
567
            StateEventType::RoomPinnedEvents => Self::RoomPinnedEvents,
568
            StateEventType::RoomPowerLevels => Self::RoomPowerLevels,
569
            StateEventType::RoomServerAcl => Self::RoomServerAcl,
570
            StateEventType::RoomThirdPartyInvite => Self::RoomThirdPartyInvite,
571
            StateEventType::RoomTombstone => Self::RoomTombstone,
572
            StateEventType::RoomTopic => Self::RoomTopic,
573
            StateEventType::SpaceChild => Self::SpaceChild,
574
            StateEventType::SpaceParent => Self::SpaceParent,
575
            StateEventType::Custom { value } => value.into(),
576
        }
577
    }
578
}
579
580
#[derive(Clone, uniffi::Enum, PartialEq, Eq, Hash)]
581
pub enum MessageLikeEventType {
582
    Audio,
583
    Beacon,
584
    CallAnswer,
585
    CallCandidates,
586
    CallHangup,
587
    CallInvite,
588
    CallNegotiate,
589
    CallNotify,
590
    CallReject,
591
    CallSdpStreamMetadataChanged,
592
    CallSelectAnswer,
593
    Emote,
594
    Encrypted,
595
    File,
596
    Image,
597
    KeyVerificationAccept,
598
    KeyVerificationCancel,
599
    KeyVerificationDone,
600
    KeyVerificationKey,
601
    KeyVerificationMac,
602
    KeyVerificationReady,
603
    KeyVerificationStart,
604
    Location,
605
    Message,
606
    PollEnd,
607
    PollResponse,
608
    PollStart,
609
    Reaction,
610
    RoomEncrypted,
611
    RoomMessage,
612
    RoomRedaction,
613
    RtcDecline,
614
    RtcNotification,
615
    Sticker,
616
    UnstablePollEnd,
617
    UnstablePollResponse,
618
    UnstablePollStart,
619
    Video,
620
    Voice,
621
    Other(String),
622
}
623
624
impl From<MessageLikeEventType> for ruma::events::MessageLikeEventType {
625
    fn from(val: MessageLikeEventType) -> Self {
626
        match val {
627
            MessageLikeEventType::Audio => Self::Audio,
628
            MessageLikeEventType::File => Self::File,
629
            MessageLikeEventType::Image => Self::Image,
630
            MessageLikeEventType::Video => Self::Video,
631
            MessageLikeEventType::Voice => Self::Voice,
632
            MessageLikeEventType::Beacon => Self::Beacon,
633
            MessageLikeEventType::CallAnswer => Self::CallAnswer,
634
            MessageLikeEventType::CallCandidates => Self::CallCandidates,
635
            MessageLikeEventType::CallInvite => Self::CallInvite,
636
            MessageLikeEventType::CallHangup => Self::CallHangup,
637
            MessageLikeEventType::CallNegotiate => Self::CallNegotiate,
638
            MessageLikeEventType::CallNotify => Self::CallNotify,
639
            MessageLikeEventType::CallReject => Self::CallReject,
640
            MessageLikeEventType::CallSdpStreamMetadataChanged => {
641
                Self::CallSdpStreamMetadataChanged
642
            }
643
            MessageLikeEventType::CallSelectAnswer => Self::CallSelectAnswer,
644
            MessageLikeEventType::Emote => Self::Emote,
645
            MessageLikeEventType::Encrypted => Self::Encrypted,
646
            MessageLikeEventType::KeyVerificationReady => Self::KeyVerificationReady,
647
            MessageLikeEventType::KeyVerificationStart => Self::KeyVerificationStart,
648
            MessageLikeEventType::KeyVerificationCancel => Self::KeyVerificationCancel,
649
            MessageLikeEventType::KeyVerificationAccept => Self::KeyVerificationAccept,
650
            MessageLikeEventType::KeyVerificationKey => Self::KeyVerificationKey,
651
            MessageLikeEventType::KeyVerificationMac => Self::KeyVerificationMac,
652
            MessageLikeEventType::KeyVerificationDone => Self::KeyVerificationDone,
653
            MessageLikeEventType::Location => Self::Location,
654
            MessageLikeEventType::Message => Self::Message,
655
            MessageLikeEventType::Reaction => Self::Reaction,
656
            MessageLikeEventType::RoomEncrypted => Self::RoomEncrypted,
657
            MessageLikeEventType::RoomMessage => Self::RoomMessage,
658
            MessageLikeEventType::RoomRedaction => Self::RoomRedaction,
659
            MessageLikeEventType::RtcDecline => Self::RtcDecline,
660
            MessageLikeEventType::Sticker => Self::Sticker,
661
            MessageLikeEventType::PollEnd => Self::PollEnd,
662
            MessageLikeEventType::PollResponse => Self::PollResponse,
663
            MessageLikeEventType::PollStart => Self::PollStart,
664
            MessageLikeEventType::RtcNotification => Self::RtcNotification,
665
            MessageLikeEventType::UnstablePollEnd => Self::UnstablePollEnd,
666
            MessageLikeEventType::UnstablePollResponse => Self::UnstablePollResponse,
667
            MessageLikeEventType::UnstablePollStart => Self::UnstablePollStart,
668
            MessageLikeEventType::Other(msgtype) => Self::from(msgtype),
669
        }
670
    }
671
}
672
673
#[derive(Debug, PartialEq, Clone, uniffi::Enum)]
674
pub enum RoomMessageEventMessageType {
675
    Audio,
676
    Emote,
677
    File,
678
    #[cfg(feature = "unstable-msc4274")]
679
    Gallery,
680
    Image,
681
    Location,
682
    Notice,
683
    ServerNotice,
684
    Text,
685
    Video,
686
    VerificationRequest,
687
    Other,
688
}
689
690
impl From<RumaMessageType> for RoomMessageEventMessageType {
691
    fn from(val: ruma::events::room::message::MessageType) -> Self {
692
        match val {
693
            RumaMessageType::Audio { .. } => Self::Audio,
694
            RumaMessageType::Emote { .. } => Self::Emote,
695
            RumaMessageType::File { .. } => Self::File,
696
            #[cfg(feature = "unstable-msc4274")]
697
            RumaMessageType::Gallery { .. } => Self::Gallery,
698
            RumaMessageType::Image { .. } => Self::Image,
699
            RumaMessageType::Location { .. } => Self::Location,
700
            RumaMessageType::Notice { .. } => Self::Notice,
701
            RumaMessageType::ServerNotice { .. } => Self::ServerNotice,
702
            RumaMessageType::Text { .. } => Self::Text,
703
            RumaMessageType::Video { .. } => Self::Video,
704
            RumaMessageType::VerificationRequest { .. } => Self::VerificationRequest,
705
            _ => Self::Other,
706
        }
707
    }
708
}
709
710
/// Contains the 2 possible identifiers of an event, either it has a remote
711
/// event id or a local transaction id, never both or none.
712
#[derive(Clone, uniffi::Enum)]
713
pub enum EventOrTransactionId {
714
    EventId { event_id: String },
715
    TransactionId { transaction_id: String },
716
}
717
718
impl From<TimelineEventItemId> for EventOrTransactionId {
719
    fn from(value: TimelineEventItemId) -> Self {
720
        match value {
721
            TimelineEventItemId::EventId(event_id) => {
722
                EventOrTransactionId::EventId { event_id: event_id.to_string() }
723
            }
724
            TimelineEventItemId::TransactionId(transaction_id) => {
725
                EventOrTransactionId::TransactionId { transaction_id: transaction_id.to_string() }
726
            }
727
        }
728
    }
729
}
730
731
impl TryFrom<EventOrTransactionId> for TimelineEventItemId {
732
    type Error = IdParseError;
733
    fn try_from(value: EventOrTransactionId) -> Result<Self, Self::Error> {
734
        match value {
735
            EventOrTransactionId::EventId { event_id } => {
736
                Ok(TimelineEventItemId::EventId(EventId::parse(event_id)?))
737
            }
738
            EventOrTransactionId::TransactionId { transaction_id } => {
739
                Ok(TimelineEventItemId::TransactionId(transaction_id.into()))
740
            }
741
        }
742
    }
743
}