atagen

macltok

public
Star0
Watch0
96fdf90aacd6dc2b8bd3d63773234245b389d28c
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
src/model.rsRust1368 lines36.3 KB
1
use std::{fmt, sync::Arc};
2
3
use ruma::{
4
    OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedRoomOrAliasId,
5
    OwnedServerName, OwnedTransactionId, OwnedUserId,
6
    events::{AnySyncTimelineEvent, room::member::StrippedRoomMemberEvent},
7
    serde::Raw,
8
};
9
#[cfg(feature = "serde")]
10
use serde::{Deserialize, Serialize};
11
12
#[derive(Debug, Eq, PartialEq)]
13
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14
#[non_exhaustive]
15
pub enum Command {
16
    Account(AccountCommand),
17
    Room(RoomCommand),
18
    Timeline(TimelineCommand),
19
    Media(MediaCommand),
20
    Encryption(EncryptionCommand),
21
    Notification(NotificationCommand),
22
    Lifecycle(LifecycleCommand),
23
}
24
25
#[derive(Debug, Eq, PartialEq)]
26
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27
#[non_exhaustive]
28
pub enum AccountCommand {
29
    Logout,
30
    RenameDevice {
31
        device_id: OwnedDeviceId,
32
        display_name: String,
33
    },
34
    DeleteDevices {
35
        device_ids: Vec<OwnedDeviceId>,
36
        auth: Option<crate::InteractiveAuth>,
37
    },
38
}
39
40
#[derive(Clone, Debug, Eq, PartialEq)]
41
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42
#[non_exhaustive]
43
pub enum RoomCommand {
44
    Open(OwnedRoomId),
45
    Close(OwnedRoomId),
46
    Join {
47
        room: OwnedRoomOrAliasId,
48
        via: Vec<OwnedServerName>,
49
    },
50
    Leave(OwnedRoomId),
51
    Forget(OwnedRoomId),
52
    AcceptInvite(OwnedRoomId),
53
    RejectInvite(OwnedRoomId),
54
    InviteUser {
55
        room_id: OwnedRoomId,
56
        user_id: OwnedUserId,
57
    },
58
    KickUser {
59
        room_id: OwnedRoomId,
60
        user_id: OwnedUserId,
61
        reason: Option<String>,
62
    },
63
    BanUser {
64
        room_id: OwnedRoomId,
65
        user_id: OwnedUserId,
66
        reason: Option<String>,
67
    },
68
    UnbanUser {
69
        room_id: OwnedRoomId,
70
        user_id: OwnedUserId,
71
        reason: Option<String>,
72
    },
73
    SetPowerLevels {
74
        room_id: OwnedRoomId,
75
        changes: PowerLevelChanges,
76
    },
77
    SetUserPowerLevel {
78
        room_id: OwnedRoomId,
79
        user_id: OwnedUserId,
80
        level: i64,
81
    },
82
    Create(CreateRoomOptions),
83
    CreateDirect(OwnedUserId),
84
    CancelSend {
85
        room_id: OwnedRoomId,
86
        transaction_id: OwnedTransactionId,
87
    },
88
    RetrySend {
89
        room_id: OwnedRoomId,
90
        transaction_id: OwnedTransactionId,
91
    },
92
    MarkRead {
93
        timeline_id: TimelineId,
94
        event_id: OwnedEventId,
95
    },
96
    SetTyping {
97
        room_id: OwnedRoomId,
98
        typing: bool,
99
    },
100
}
101
102
#[derive(Clone, Debug, Default, Eq, PartialEq)]
103
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
104
pub struct CreateRoomOptions {
105
    pub name: Option<String>,
106
    pub topic: Option<String>,
107
    pub alias_localpart: Option<String>,
108
    pub invite: Vec<OwnedUserId>,
109
    pub direct: bool,
110
    pub visibility: RoomVisibility,
111
    pub preset: Option<RoomPreset>,
112
}
113
114
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
115
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116
pub enum RoomVisibility {
117
    #[default]
118
    Private,
119
    Public,
120
}
121
122
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
124
pub enum RoomPreset {
125
    PrivateChat,
126
    TrustedPrivateChat,
127
    PublicChat,
128
}
129
130
#[derive(Clone, Debug, Eq, PartialEq)]
131
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
132
#[non_exhaustive]
133
pub enum TimelineCommand {
134
    OpenThread {
135
        room_id: OwnedRoomId,
136
        root_event_id: OwnedEventId,
137
    },
138
    CloseThread {
139
        room_id: OwnedRoomId,
140
        root_event_id: OwnedEventId,
141
    },
142
    /// Re-emit an authoritative reset for an open room or thread without
143
    /// closing it. The reset arrives before this command's terminal result.
144
    Resync {
145
        timeline_id: TimelineId,
146
    },
147
    Paginate {
148
        timeline_id: TimelineId,
149
        limit: u16,
150
    },
151
    Send {
152
        timeline_id: TimelineId,
153
        content: MessageContent,
154
    },
155
    Edit {
156
        timeline_id: TimelineId,
157
        event_id: OwnedEventId,
158
        content: MessageEditContent,
159
    },
160
    Redact {
161
        timeline_id: TimelineId,
162
        event_id: OwnedEventId,
163
        reason: Option<String>,
164
    },
165
    ToggleReaction {
166
        timeline_id: TimelineId,
167
        event_id: OwnedEventId,
168
        key: String,
169
    },
170
}
171
172
impl TimelineCommand {
173
    #[must_use]
174
    pub const fn room_id(&self) -> &OwnedRoomId {
175
        match self {
176
            Self::OpenThread { room_id, .. } | Self::CloseThread { room_id, .. } => room_id,
177
            Self::Resync { timeline_id }
178
            | Self::Paginate { timeline_id, .. }
179
            | Self::Send { timeline_id, .. }
180
            | Self::Edit { timeline_id, .. }
181
            | Self::Redact { timeline_id, .. }
182
            | Self::ToggleReaction { timeline_id, .. } => timeline_id.room_id(),
183
        }
184
    }
185
186
    #[must_use]
187
    pub const fn timeline_id(&self) -> Option<&TimelineId> {
188
        match self {
189
            Self::OpenThread { .. } | Self::CloseThread { .. } => None,
190
            Self::Resync { timeline_id }
191
            | Self::Paginate { timeline_id, .. }
192
            | Self::Send { timeline_id, .. }
193
            | Self::Edit { timeline_id, .. }
194
            | Self::Redact { timeline_id, .. }
195
            | Self::ToggleReaction { timeline_id, .. } => Some(timeline_id),
196
        }
197
    }
198
}
199
200
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
201
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
202
#[non_exhaustive]
203
pub enum TimelineId {
204
    Room(OwnedRoomId),
205
    Thread {
206
        room_id: OwnedRoomId,
207
        root_event_id: OwnedEventId,
208
    },
209
}
210
211
impl TimelineId {
212
    #[must_use]
213
    pub const fn room_id(&self) -> &OwnedRoomId {
214
        match self {
215
            Self::Room(room_id) | Self::Thread { room_id, .. } => room_id,
216
        }
217
    }
218
219
    #[must_use]
220
    pub fn thread(room_id: OwnedRoomId, root_event_id: OwnedEventId) -> Self {
221
        Self::Thread {
222
            room_id,
223
            root_event_id,
224
        }
225
    }
226
227
    #[must_use]
228
    pub fn thread_root(&self) -> Option<&OwnedEventId> {
229
        match self {
230
            Self::Room(_) => None,
231
            Self::Thread { root_event_id, .. } => Some(root_event_id),
232
        }
233
    }
234
}
235
236
impl From<OwnedRoomId> for TimelineId {
237
    fn from(room_id: OwnedRoomId) -> Self {
238
        Self::Room(room_id)
239
    }
240
}
241
242
#[derive(Clone, Debug, Eq, PartialEq)]
243
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
244
#[non_exhaustive]
245
pub enum MediaCommand {
246
    Fetch {
247
        id: MediaId,
248
        size: MediaSize,
249
    },
250
    Upload {
251
        timeline_id: TimelineId,
252
        filename: String,
253
        mimetype: String,
254
        bytes: Vec<u8>,
255
        caption: Option<String>,
256
    },
257
    UploadAttachment(AttachmentUpload),
258
}
259
260
#[derive(Clone, Debug, Eq, PartialEq)]
261
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
262
pub struct AttachmentUpload {
263
    pub timeline_id: TimelineId,
264
    pub filename: String,
265
    pub mimetype: String,
266
    pub bytes: Vec<u8>,
267
    pub caption: Option<String>,
268
    pub details: Option<UploadDetails>,
269
    pub thumbnail: Option<UploadThumbnail>,
270
}
271
272
impl AttachmentUpload {
273
    #[must_use]
274
    pub fn new(
275
        timeline_id: impl Into<TimelineId>,
276
        filename: impl Into<String>,
277
        mimetype: impl Into<String>,
278
        bytes: Vec<u8>,
279
    ) -> Self {
280
        Self {
281
            timeline_id: timeline_id.into(),
282
            filename: filename.into(),
283
            mimetype: mimetype.into(),
284
            bytes,
285
            caption: None,
286
            details: None,
287
            thumbnail: None,
288
        }
289
    }
290
}
291
292
#[derive(Clone, Debug, Eq, PartialEq)]
293
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
294
#[non_exhaustive]
295
pub enum UploadDetails {
296
    Image {
297
        width: Option<u32>,
298
        height: Option<u32>,
299
        blurhash: Option<String>,
300
    },
301
    Video {
302
        width: Option<u32>,
303
        height: Option<u32>,
304
        duration_ms: Option<u64>,
305
        blurhash: Option<String>,
306
    },
307
    Audio {
308
        duration_ms: Option<u64>,
309
    },
310
    Voice {
311
        duration_ms: u64,
312
        waveform: Vec<u16>,
313
    },
314
    File,
315
}
316
317
#[derive(Clone, Debug, Eq, PartialEq)]
318
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
319
pub struct UploadThumbnail {
320
    pub bytes: Vec<u8>,
321
    pub mimetype: String,
322
    pub width: u32,
323
    pub height: u32,
324
}
325
326
#[derive(Eq, PartialEq)]
327
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
328
#[non_exhaustive]
329
pub enum EncryptionCommand {
330
    Recover(String),
331
    CreateKeyBackup,
332
    EnableKeyBackup,
333
    DisableKeyBackup { delete_from_server: bool },
334
    RecoverKeyBackup(String),
335
    StartVerification(VerificationTarget),
336
    Verify(VerificationAction),
337
}
338
339
impl fmt::Debug for EncryptionCommand {
340
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
341
        match self {
342
            Self::Recover(_) => formatter.debug_tuple("Recover").field(&"...").finish(),
343
            Self::CreateKeyBackup => formatter.write_str("CreateKeyBackup"),
344
            Self::EnableKeyBackup => formatter.write_str("EnableKeyBackup"),
345
            Self::DisableKeyBackup { delete_from_server } => formatter
346
                .debug_struct("DisableKeyBackup")
347
                .field("delete_from_server", delete_from_server)
348
                .finish(),
349
            Self::RecoverKeyBackup(_) => formatter
350
                .debug_tuple("RecoverKeyBackup")
351
                .field(&"...")
352
                .finish(),
353
            Self::StartVerification(target) => formatter
354
                .debug_tuple("StartVerification")
355
                .field(target)
356
                .finish(),
357
            Self::Verify(action) => formatter.debug_tuple("Verify").field(action).finish(),
358
        }
359
    }
360
}
361
362
#[derive(Clone, Debug, Eq, PartialEq)]
363
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
364
#[non_exhaustive]
365
pub enum NotificationCommand {
366
    SetRoomMode {
367
        room_id: OwnedRoomId,
368
        mode: RoomNotificationMode,
369
    },
370
    ResetRoomMode(OwnedRoomId),
371
    SetDefaultMode {
372
        encrypted: bool,
373
        one_to_one: bool,
374
        mode: RoomNotificationMode,
375
    },
376
    AddKeyword(String),
377
    RemoveKeyword(String),
378
}
379
380
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
381
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
382
#[non_exhaustive]
383
pub enum LifecycleCommand {
384
    Shutdown,
385
}
386
387
impl Command {
388
    #[must_use]
389
    pub const fn kind(&self) -> CommandKind {
390
        match self {
391
            Self::Account(_) => CommandKind::Account,
392
            Self::Room(_) => CommandKind::Room,
393
            Self::Timeline(_) => CommandKind::Timeline,
394
            Self::Media(_) => CommandKind::Media,
395
            Self::Encryption(_) => CommandKind::Encryption,
396
            Self::Notification(_) => CommandKind::Notification,
397
            Self::Lifecycle(_) => CommandKind::Lifecycle,
398
        }
399
    }
400
}
401
402
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
403
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
404
pub enum CommandKind {
405
    Account,
406
    Room,
407
    Timeline,
408
    Media,
409
    Encryption,
410
    Notification,
411
    Lifecycle,
412
}
413
414
#[derive(Clone, Debug, Eq, PartialEq)]
415
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
416
#[non_exhaustive]
417
pub enum MessageContent {
418
    Text {
419
        content: RichText,
420
        mentions: MessageMentions,
421
    },
422
    Emote {
423
        content: RichText,
424
        mentions: MessageMentions,
425
    },
426
    Reply {
427
        event_id: OwnedEventId,
428
        content: RichText,
429
        mentions: MessageMentions,
430
        notify_replied_to: bool,
431
    },
432
}
433
434
impl MessageContent {
435
    #[must_use]
436
    pub fn text(body: impl Into<String>) -> Self {
437
        Self::Text {
438
            content: RichText::plain(body),
439
            mentions: MessageMentions::default(),
440
        }
441
    }
442
443
    #[must_use]
444
    pub fn html(body: impl Into<String>, formatted_body: impl Into<String>) -> Self {
445
        Self::Text {
446
            content: RichText::html(body, formatted_body),
447
            mentions: MessageMentions::default(),
448
        }
449
    }
450
451
    #[must_use]
452
    pub fn emote(content: RichText, mentions: MessageMentions) -> Self {
453
        Self::Emote { content, mentions }
454
    }
455
456
    #[must_use]
457
    pub fn reply(event_id: OwnedEventId, content: RichText, mentions: MessageMentions) -> Self {
458
        Self::Reply {
459
            event_id,
460
            content,
461
            mentions,
462
            notify_replied_to: true,
463
        }
464
    }
465
466
    #[must_use]
467
    pub fn reply_without_notification(
468
        event_id: OwnedEventId,
469
        content: RichText,
470
        mentions: MessageMentions,
471
    ) -> Self {
472
        Self::Reply {
473
            event_id,
474
            content,
475
            mentions,
476
            notify_replied_to: false,
477
        }
478
    }
479
}
480
481
#[derive(Clone, Debug, Eq, PartialEq)]
482
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
483
#[non_exhaustive]
484
pub enum MessageEditContent {
485
    Text {
486
        content: RichText,
487
        mentions: MessageMentions,
488
    },
489
    Emote {
490
        content: RichText,
491
        mentions: MessageMentions,
492
    },
493
}
494
495
impl MessageEditContent {
496
    #[must_use]
497
    pub fn text(body: impl Into<String>) -> Self {
498
        Self::Text {
499
            content: RichText::plain(body),
500
            mentions: MessageMentions::default(),
501
        }
502
    }
503
504
    #[must_use]
505
    pub fn html(body: impl Into<String>, formatted_body: impl Into<String>) -> Self {
506
        Self::Text {
507
            content: RichText::html(body, formatted_body),
508
            mentions: MessageMentions::default(),
509
        }
510
    }
511
512
    #[must_use]
513
    pub fn emote(content: RichText, mentions: MessageMentions) -> Self {
514
        Self::Emote { content, mentions }
515
    }
516
}
517
518
#[derive(Clone, Debug, Default, Eq, PartialEq)]
519
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
520
#[non_exhaustive]
521
pub struct RichText {
522
    pub body: String,
523
    pub formatted_body: Option<String>,
524
}
525
526
impl RichText {
527
    #[must_use]
528
    pub fn plain(body: impl Into<String>) -> Self {
529
        Self {
530
            body: body.into(),
531
            formatted_body: None,
532
        }
533
    }
534
535
    #[must_use]
536
    pub fn html(body: impl Into<String>, formatted_body: impl Into<String>) -> Self {
537
        Self {
538
            body: body.into(),
539
            formatted_body: Some(formatted_body.into()),
540
        }
541
    }
542
}
543
544
#[derive(Clone, Debug, Default, Eq, PartialEq)]
545
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
546
#[non_exhaustive]
547
pub struct MessageMentions {
548
    pub user_ids: Vec<OwnedUserId>,
549
    pub room: bool,
550
}
551
552
impl MessageMentions {
553
    #[must_use]
554
    pub fn with_user_ids(user_ids: impl IntoIterator<Item = OwnedUserId>) -> Self {
555
        Self {
556
            user_ids: user_ids.into_iter().collect(),
557
            room: false,
558
        }
559
    }
560
561
    #[must_use]
562
    pub fn room() -> Self {
563
        Self {
564
            user_ids: Vec::new(),
565
            room: true,
566
        }
567
    }
568
}
569
570
#[derive(Clone, Debug, Eq, PartialEq)]
571
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
572
#[non_exhaustive]
573
pub enum Event {
574
    ConnectionChanged(ConnectionState),
575
    Authenticated {
576
        user_id: OwnedUserId,
577
    },
578
    /// Refresh tokens changed. Persist a fresh `ClientHandle::session_record()`
579
    /// atomically; a record captured before this event may no longer restore.
580
    SessionTokensChanged,
581
    /// Authentication was rejected. Remove the saved session record and store.
582
    SessionAccessRejected,
583
    NotificationSettingsChanged,
584
    VerificationTargetChanged(VerificationTarget),
585
    InteractiveAuthRequired {
586
        id: CommandId,
587
        command: CommandKind,
588
        challenge: crate::InteractiveAuthChallenge,
589
    },
590
    RoomsChanged,
591
    /// Authoritative snapshot on open or resync. A resync reset precedes the
592
    /// corresponding terminal command event on this stream.
593
    TimelineReset {
594
        timeline_id: TimelineId,
595
        items: Vec<TimelineItem>,
596
    },
597
    TimelineChanged {
598
        timeline_id: TimelineId,
599
        diffs: Vec<TimelineDiff>,
600
    },
601
    HistoryChanged {
602
        timeline_id: TimelineId,
603
        state: HistoryState,
604
    },
605
    TypingChanged(TypingState),
606
    MediaReady {
607
        media_id: MediaId,
608
        size: MediaSize,
609
        bytes: Arc<[u8]>,
610
        mimetype: String,
611
    },
612
    MediaUploadProgress {
613
        room_id: OwnedRoomId,
614
        transaction_id: OwnedTransactionId,
615
        uploaded_bytes: u64,
616
        total_bytes: u64,
617
        complete: bool,
618
    },
619
    MediaUploadFailed {
620
        room_id: OwnedRoomId,
621
        transaction_id: OwnedTransactionId,
622
        kind: CommandErrorKind,
623
        recoverable: bool,
624
    },
625
    EncryptionChanged(EncryptionState),
626
    VerificationChanged(VerificationState),
627
    /// Terminal command success. For sends, this means the SDK send queue
628
    /// accepted the item; network delivery is reflected by its send state.
629
    CommandSucceeded {
630
        id: CommandId,
631
        command: CommandKind,
632
    },
633
    CommandSucceededWithRoom {
634
        id: CommandId,
635
        command: CommandKind,
636
        room_id: OwnedRoomId,
637
    },
638
    /// Terminal failure correlated by `CommandFailure::id`.
639
    CommandFailed(CommandFailure),
640
    /// Explicit logout completed; delete the saved authentication record.
641
    LoggedOut,
642
    Failed(EngineFailure),
643
    /// Engine stopped without logging out; retain saved credentials for restore.
644
    Stopped,
645
}
646
647
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
648
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
649
pub struct CommandId(u64);
650
651
impl CommandId {
652
    pub(crate) const fn new(value: u64) -> Self {
653
        Self(value)
654
    }
655
656
    #[must_use]
657
    pub const fn get(self) -> u64 {
658
        self.0
659
    }
660
}
661
662
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
663
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
664
pub enum ConnectionState {
665
    Idle,
666
    Connecting,
667
    Online,
668
    Offline,
669
    Stopped,
670
}
671
672
#[derive(Clone, Debug, Eq, PartialEq)]
673
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
674
#[non_exhaustive]
675
pub struct RoomSummary {
676
    pub room_id: OwnedRoomId,
677
    pub name: String,
678
    pub membership: RoomMembership,
679
    pub unread_messages: u64,
680
    pub unread_mentions: u64,
681
    pub encrypted: bool,
682
    pub space: bool,
683
    pub direct: bool,
684
    pub favourite: bool,
685
    pub low_priority: bool,
686
    pub avatar: Option<OwnedMxcUri>,
687
    pub latest_event_timestamp_ms: Option<u64>,
688
    pub notification_mode: Option<RoomNotificationMode>,
689
    pub notification_mode_custom: bool,
690
}
691
692
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
693
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
694
pub enum RoomNotificationMode {
695
    AllMessages,
696
    MentionsAndKeywordsOnly,
697
    Mute,
698
}
699
700
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
701
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
702
#[non_exhaustive]
703
pub struct RoomNotificationSettings {
704
    pub mode: RoomNotificationMode,
705
    pub custom: bool,
706
}
707
708
#[derive(Clone, Debug)]
709
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
710
#[non_exhaustive]
711
pub enum PushNotificationStatus {
712
    Event(Box<PushNotification>),
713
    NotFound,
714
    FilteredOut,
715
    Redacted,
716
}
717
718
#[derive(Clone, Debug)]
719
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
720
#[non_exhaustive]
721
pub struct PushNotification {
722
    pub event_id: OwnedEventId,
723
    pub event: PushNotificationEvent,
724
    pub sender: PushNotificationSender,
725
    pub room: PushNotificationRoom,
726
    pub noisy: Option<bool>,
727
    pub has_mention: Option<bool>,
728
    pub thread_id: Option<OwnedEventId>,
729
}
730
731
#[derive(Clone, Debug, Eq, PartialEq)]
732
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
733
#[non_exhaustive]
734
pub struct PushNotificationSender {
735
    pub user_id: OwnedUserId,
736
    pub display_name: Option<String>,
737
    pub avatar_url: Option<OwnedMxcUri>,
738
    pub name_ambiguous: bool,
739
}
740
741
#[derive(Clone, Debug, Eq, PartialEq)]
742
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
743
#[non_exhaustive]
744
pub struct PushNotificationRoom {
745
    pub room_id: OwnedRoomId,
746
    pub name: String,
747
    pub avatar_url: Option<OwnedMxcUri>,
748
    pub canonical_alias: Option<OwnedRoomAliasId>,
749
    pub topic: Option<String>,
750
    pub encrypted: Option<bool>,
751
    pub direct: bool,
752
    pub joined_members_count: u64,
753
    pub service_members: Vec<OwnedUserId>,
754
    pub active_service_members_count: u64,
755
    pub space: bool,
756
}
757
758
#[derive(Clone, Debug)]
759
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
760
#[non_exhaustive]
761
pub enum PushNotificationEvent {
762
    Timeline(Raw<AnySyncTimelineEvent>),
763
    Invite(Raw<StrippedRoomMemberEvent>),
764
}
765
766
#[derive(Clone, Debug, Eq, PartialEq)]
767
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
768
#[non_exhaustive]
769
pub struct RoomPowerLevels {
770
    pub ban: i64,
771
    pub invite: i64,
772
    pub kick: i64,
773
    pub redact: i64,
774
    pub events_default: i64,
775
    pub state_default: i64,
776
    pub users_default: i64,
777
    pub room_notification: i64,
778
    pub users: Vec<(OwnedUserId, i64)>,
779
    pub events: Vec<(String, i64)>,
780
}
781
782
#[derive(Clone, Debug, Default, Eq, PartialEq)]
783
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
784
#[non_exhaustive]
785
pub struct PowerLevelChanges {
786
    pub ban: Option<i64>,
787
    pub invite: Option<i64>,
788
    pub kick: Option<i64>,
789
    pub redact: Option<i64>,
790
    pub events_default: Option<i64>,
791
    pub state_default: Option<i64>,
792
    pub users_default: Option<i64>,
793
    pub room_notification: Option<i64>,
794
    pub room_name: Option<i64>,
795
    pub room_avatar: Option<i64>,
796
    pub room_topic: Option<i64>,
797
    pub space_child: Option<i64>,
798
    pub beacon: Option<i64>,
799
    pub beacon_info: Option<i64>,
800
}
801
802
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
803
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
804
pub enum RoomMembership {
805
    Joined,
806
    Invited,
807
    Left,
808
    Knocked,
809
    Banned,
810
}
811
812
#[derive(Clone, Debug, Default, Eq, PartialEq)]
813
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
814
#[non_exhaustive]
815
pub struct RoomListFilter {
816
    pub membership: Option<RoomMembership>,
817
    pub category: Option<RoomCategory>,
818
    pub favourite: Option<bool>,
819
    pub unread_only: bool,
820
    pub search: Option<String>,
821
}
822
823
impl RoomListFilter {
824
    #[must_use]
825
    pub fn with_membership(mut self, membership: RoomMembership) -> Self {
826
        self.membership = Some(membership);
827
        self
828
    }
829
830
    #[must_use]
831
    pub fn with_category(mut self, category: RoomCategory) -> Self {
832
        self.category = Some(category);
833
        self
834
    }
835
836
    #[must_use]
837
    pub fn with_favourite(mut self, favourite: bool) -> Self {
838
        self.favourite = Some(favourite);
839
        self
840
    }
841
842
    #[must_use]
843
    pub const fn with_unread_only(mut self, unread_only: bool) -> Self {
844
        self.unread_only = unread_only;
845
        self
846
    }
847
848
    #[must_use]
849
    pub fn with_search(mut self, search: impl Into<String>) -> Self {
850
        self.search = Some(search.into());
851
        self
852
    }
853
854
    #[must_use]
855
    pub fn matches(&self, room: &RoomSummary) -> bool {
856
        self.membership
857
            .is_none_or(|membership| room.membership == membership)
858
            && self.category.is_none_or(|category| match category {
859
                RoomCategory::People => room.direct && !room.space,
860
                RoomCategory::Rooms => !room.direct && !room.space,
861
                RoomCategory::Spaces => room.space,
862
            })
863
            && self
864
                .favourite
865
                .is_none_or(|favourite| room.favourite == favourite)
866
            && (!self.unread_only || room.unread_messages > 0 || room.unread_mentions > 0)
867
            && self.search.as_ref().is_none_or(|search| {
868
                let search = search.to_lowercase();
869
                room.name.to_lowercase().contains(&search)
870
                    || room.room_id.as_str().to_lowercase().contains(&search)
871
            })
872
    }
873
}
874
875
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
876
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
877
pub enum RoomCategory {
878
    People,
879
    Rooms,
880
    Spaces,
881
}
882
883
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
884
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
885
pub enum MemberFilter {
886
    Active,
887
    Joined,
888
    Invited,
889
    All,
890
}
891
892
#[derive(Clone, Debug, Eq, PartialEq)]
893
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
894
#[non_exhaustive]
895
pub enum MemberMembership {
896
    Joined,
897
    Invited,
898
    Left,
899
    Knocked,
900
    Banned,
901
    Custom(String),
902
}
903
904
#[derive(Clone, Debug, Eq, PartialEq)]
905
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
906
#[non_exhaustive]
907
pub struct MemberSummary {
908
    pub user_id: OwnedUserId,
909
    pub name: String,
910
    pub display_name: Option<String>,
911
    pub avatar: Option<OwnedMxcUri>,
912
    pub membership: MemberMembership,
913
    pub own: bool,
914
    pub name_ambiguous: bool,
915
    pub ignored: bool,
916
    pub service: bool,
917
}
918
919
#[derive(Clone, Debug, Eq, PartialEq)]
920
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
921
#[non_exhaustive]
922
pub struct OwnDevice {
923
    pub device_id: OwnedDeviceId,
924
    pub display_name: Option<String>,
925
    pub last_seen_ip: Option<String>,
926
    pub last_seen_timestamp_ms: Option<u64>,
927
    pub current: bool,
928
}
929
930
#[derive(Clone, Debug, Eq, PartialEq)]
931
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
932
pub enum TimelineItem {
933
    Message(Box<TimelineMessage>),
934
    DayDivider { timestamp_ms: u64 },
935
    ReadMarker,
936
    TimelineStart,
937
}
938
939
#[derive(Clone, Debug, Eq, PartialEq)]
940
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
941
#[non_exhaustive]
942
pub enum TimelineDiff {
943
    Append { values: Vec<TimelineItem> },
944
    Clear,
945
    PushFront { value: TimelineItem },
946
    PushBack { value: TimelineItem },
947
    PopFront,
948
    PopBack,
949
    Insert { index: usize, value: TimelineItem },
950
    Set { index: usize, value: TimelineItem },
951
    Remove { index: usize },
952
    Truncate { length: usize },
953
    Reset { values: Vec<TimelineItem> },
954
}
955
956
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
957
#[error("timeline diff is incompatible with the current {length}-item snapshot")]
958
pub struct TimelineDiffError {
959
    pub length: usize,
960
}
961
962
impl TimelineDiff {
963
    /// Apply a diff only when its indices match the current snapshot.
964
    /// On error the snapshot is unchanged; request `TimelineCommand::Resync`
965
    /// and ignore further diffs until the corresponding `TimelineReset`.
966
    pub fn try_apply(self, items: &mut Vec<TimelineItem>) -> Result<(), TimelineDiffError> {
967
        let length = items.len();
968
        match self {
969
            Self::Append { values } => items.extend(values),
970
            Self::Clear => items.clear(),
971
            Self::PushFront { value } => items.insert(0, value),
972
            Self::PushBack { value } => items.push(value),
973
            Self::PopFront if length == 0 => return Err(TimelineDiffError { length }),
974
            Self::PopFront => {
975
                items.remove(0);
976
            }
977
            Self::PopBack if length == 0 => return Err(TimelineDiffError { length }),
978
            Self::PopBack => {
979
                items.pop();
980
            }
981
            Self::Insert { index, .. } if index > length => {
982
                return Err(TimelineDiffError { length });
983
            }
984
            Self::Insert { index, value } => items.insert(index, value),
985
            Self::Set { index, .. } if index >= length => return Err(TimelineDiffError { length }),
986
            Self::Set { index, value } => items[index] = value,
987
            Self::Remove { index } if index >= length => return Err(TimelineDiffError { length }),
988
            Self::Remove { index } => {
989
                items.remove(index);
990
            }
991
            Self::Truncate { length: target } if target > length => {
992
                return Err(TimelineDiffError { length });
993
            }
994
            Self::Truncate { length } => items.truncate(length),
995
            Self::Reset { values } => *items = values,
996
        }
997
        Ok(())
998
    }
999
}
1000
1001
#[cfg(test)]
1002
mod tests {
1003
    use super::{TimelineDiff, TimelineItem};
1004
1005
    fn item(timestamp_ms: u64) -> TimelineItem {
1006
        TimelineItem::DayDivider { timestamp_ms }
1007
    }
1008
1009
    #[test]
1010
    fn timeline_diff_try_apply_handles_every_variant() {
1011
        let mut items = vec![item(1), item(2)];
1012
1013
        TimelineDiff::Append {
1014
            values: vec![item(3)],
1015
        }
1016
        .try_apply(&mut items)
1017
        .unwrap();
1018
        assert_eq!(items, vec![item(1), item(2), item(3)]);
1019
1020
        TimelineDiff::Clear.try_apply(&mut items).unwrap();
1021
        assert!(items.is_empty());
1022
1023
        TimelineDiff::PushFront { value: item(2) }
1024
            .try_apply(&mut items)
1025
            .unwrap();
1026
        TimelineDiff::PushBack { value: item(3) }
1027
            .try_apply(&mut items)
1028
            .unwrap();
1029
        assert_eq!(items, vec![item(2), item(3)]);
1030
1031
        TimelineDiff::PopFront.try_apply(&mut items).unwrap();
1032
        TimelineDiff::PopBack.try_apply(&mut items).unwrap();
1033
        assert!(items.is_empty());
1034
1035
        TimelineDiff::Reset {
1036
            values: vec![item(1), item(3)],
1037
        }
1038
        .try_apply(&mut items)
1039
        .unwrap();
1040
        TimelineDiff::Insert {
1041
            index: 1,
1042
            value: item(2),
1043
        }
1044
        .try_apply(&mut items)
1045
        .unwrap();
1046
        assert_eq!(items, vec![item(1), item(2), item(3)]);
1047
1048
        TimelineDiff::Set {
1049
            index: 1,
1050
            value: item(4),
1051
        }
1052
        .try_apply(&mut items)
1053
        .unwrap();
1054
        assert_eq!(items, vec![item(1), item(4), item(3)]);
1055
1056
        TimelineDiff::Remove { index: 1 }
1057
            .try_apply(&mut items)
1058
            .unwrap();
1059
        assert_eq!(items, vec![item(1), item(3)]);
1060
1061
        TimelineDiff::Truncate { length: 1 }
1062
            .try_apply(&mut items)
1063
            .unwrap();
1064
        assert_eq!(items, vec![item(1)]);
1065
    }
1066
1067
    #[test]
1068
    fn invalid_timeline_diff_leaves_snapshot_unchanged() {
1069
        let invalid = [
1070
            TimelineDiff::Insert {
1071
                index: 3,
1072
                value: item(3),
1073
            },
1074
            TimelineDiff::Set {
1075
                index: 2,
1076
                value: item(3),
1077
            },
1078
            TimelineDiff::Remove { index: 2 },
1079
            TimelineDiff::Truncate { length: 3 },
1080
        ];
1081
        for diff in invalid {
1082
            let mut items = vec![item(1), item(2)];
1083
            assert_eq!(diff.try_apply(&mut items).unwrap_err().length, 2);
1084
            assert_eq!(items, vec![item(1), item(2)]);
1085
        }
1086
        for diff in [TimelineDiff::PopFront, TimelineDiff::PopBack] {
1087
            let mut items = Vec::new();
1088
            assert_eq!(diff.try_apply(&mut items).unwrap_err().length, 0);
1089
            assert!(items.is_empty());
1090
        }
1091
    }
1092
}
1093
1094
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1095
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1096
pub struct TimelineItemId(String);
1097
1098
impl TimelineItemId {
1099
    pub(crate) fn new(value: String) -> Self {
1100
        Self(value)
1101
    }
1102
1103
    #[must_use]
1104
    pub fn as_str(&self) -> &str {
1105
        &self.0
1106
    }
1107
}
1108
1109
#[derive(Clone, Debug, Eq, PartialEq)]
1110
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1111
#[non_exhaustive]
1112
pub struct TimelineMessage {
1113
    pub item_id: TimelineItemId,
1114
    pub event_id: Option<OwnedEventId>,
1115
    pub transaction_id: Option<OwnedTransactionId>,
1116
    pub sender_id: OwnedUserId,
1117
    pub sender_name: String,
1118
    pub body: String,
1119
    pub formatted_body: Option<String>,
1120
    pub mentions: Option<MessageMentions>,
1121
    pub thread_root: Option<OwnedEventId>,
1122
    pub thread_summary: Option<ThreadSummary>,
1123
    pub timestamp_ms: u64,
1124
    pub own: bool,
1125
    pub send_state: SendState,
1126
    pub edited: bool,
1127
    pub unable_to_decrypt: bool,
1128
    pub media: Option<MediaDescriptor>,
1129
    pub reply_to: Option<ReplyReference>,
1130
    pub reactions: Vec<Reaction>,
1131
    pub read_by: Vec<OwnedUserId>,
1132
}
1133
1134
#[derive(Clone, Debug, Eq, PartialEq)]
1135
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1136
#[non_exhaustive]
1137
pub struct ThreadSummary {
1138
    pub reply_count: u32,
1139
    pub latest_event_id: Option<OwnedEventId>,
1140
    pub latest_event_timestamp_ms: Option<u64>,
1141
}
1142
1143
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1144
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1145
#[non_exhaustive]
1146
pub enum SendState {
1147
    Sent,
1148
    Queued,
1149
    Sending,
1150
    Failed { retriable: bool },
1151
}
1152
1153
#[derive(Clone, Debug, Eq, PartialEq)]
1154
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1155
pub struct ReplyReference {
1156
    pub event_id: OwnedEventId,
1157
    pub sender_id: Option<OwnedUserId>,
1158
    pub sender_name: Option<String>,
1159
}
1160
1161
#[derive(Clone, Debug, Eq, PartialEq)]
1162
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1163
#[non_exhaustive]
1164
pub struct Reaction {
1165
    pub key: String,
1166
    pub count: usize,
1167
    pub own: bool,
1168
    pub senders: Vec<OwnedUserId>,
1169
}
1170
1171
#[derive(Clone, Debug, Eq, PartialEq)]
1172
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1173
#[non_exhaustive]
1174
pub struct MediaDescriptor {
1175
    pub id: MediaId,
1176
    pub kind: MediaKind,
1177
    pub filename: String,
1178
    pub caption: Option<String>,
1179
    pub mimetype: Option<String>,
1180
    pub size_bytes: Option<u64>,
1181
    pub width: Option<u64>,
1182
    pub height: Option<u64>,
1183
}
1184
1185
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1186
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1187
pub struct MediaId(String);
1188
1189
impl MediaId {
1190
    pub(crate) fn new(value: String) -> Self {
1191
        Self(value)
1192
    }
1193
1194
    #[must_use]
1195
    pub fn as_str(&self) -> &str {
1196
        &self.0
1197
    }
1198
}
1199
1200
impl std::fmt::Display for MediaId {
1201
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1202
        self.0.fmt(formatter)
1203
    }
1204
}
1205
1206
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1207
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1208
pub enum MediaSize {
1209
    Thumbnail,
1210
    Full,
1211
}
1212
1213
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1214
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1215
#[non_exhaustive]
1216
pub enum MediaKind {
1217
    Image,
1218
    Video,
1219
    Audio,
1220
    File,
1221
    Sticker,
1222
}
1223
1224
#[derive(Clone, Debug, Eq, PartialEq)]
1225
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1226
pub struct TypingState {
1227
    pub room_id: OwnedRoomId,
1228
    pub users: Vec<OwnedUserId>,
1229
}
1230
1231
#[derive(Clone, Debug, Eq, PartialEq)]
1232
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1233
pub enum HistoryState {
1234
    Idle,
1235
    Loading,
1236
    StartReached,
1237
    Failed(String),
1238
}
1239
1240
#[derive(Clone, Debug, Eq, PartialEq)]
1241
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1242
#[non_exhaustive]
1243
pub struct EncryptionState {
1244
    pub device_id: OwnedDeviceId,
1245
    pub device_verified: bool,
1246
    pub recovery: RecoveryState,
1247
    pub key_backup: KeyBackupState,
1248
}
1249
1250
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1251
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1252
pub enum KeyBackupState {
1253
    Unknown,
1254
    Creating,
1255
    Enabling,
1256
    Resuming,
1257
    Downloading,
1258
    Enabled,
1259
    Disabling,
1260
}
1261
1262
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1263
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1264
pub enum RecoveryState {
1265
    Unknown,
1266
    Disabled,
1267
    Incomplete,
1268
    Enabled,
1269
}
1270
1271
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1272
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1273
pub enum VerificationAction {
1274
    Confirm,
1275
    Reject,
1276
    Cancel,
1277
}
1278
1279
#[derive(Clone, Debug, Eq, PartialEq)]
1280
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1281
#[non_exhaustive]
1282
pub enum VerificationTarget {
1283
    OwnIdentity,
1284
    User(OwnedUserId),
1285
    Device {
1286
        user_id: OwnedUserId,
1287
        device_id: OwnedDeviceId,
1288
    },
1289
}
1290
1291
#[derive(Clone, Debug, Eq, PartialEq)]
1292
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1293
pub enum VerificationState {
1294
    Waiting,
1295
    Comparison {
1296
        emojis: Vec<(String, String)>,
1297
        decimals: (u16, u16, u16),
1298
    },
1299
    Confirmed,
1300
    Done,
1301
    Cancelled(String),
1302
    Failed(VerificationFailure),
1303
}
1304
1305
#[derive(Clone, Debug, Eq, PartialEq)]
1306
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1307
#[non_exhaustive]
1308
pub enum VerificationFailure {
1309
    RequestAcceptance(String),
1310
    SasStartup(String),
1311
    SasAcceptance(String),
1312
    RequestStreamEnded,
1313
    SasStreamEnded,
1314
}
1315
1316
#[derive(Clone, Debug, Eq, PartialEq)]
1317
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1318
#[non_exhaustive]
1319
pub struct CommandFailure {
1320
    pub id: CommandId,
1321
    pub command: CommandKind,
1322
    pub kind: CommandErrorKind,
1323
    pub message: String,
1324
}
1325
1326
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1327
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1328
#[non_exhaustive]
1329
pub enum CommandErrorKind {
1330
    Unauthenticated,
1331
    RoomUnknown,
1332
    RoomNotOpen,
1333
    TimelineNotOpen,
1334
    RoomBusy,
1335
    QueueFull,
1336
    InteractiveAuthRequired,
1337
    EmptyBody,
1338
    MediaUnknown,
1339
    MediaTooLarge,
1340
    MediaUnavailable,
1341
    VerificationInactive,
1342
    VerificationUnsupported,
1343
    NoCrossSigningIdentity,
1344
    RecoveryRejected,
1345
    EventReceiverClosed,
1346
    Network,
1347
    Server,
1348
    Store,
1349
    Timeline,
1350
    Encryption,
1351
    Other,
1352
}
1353
1354
#[derive(Clone, Debug, Eq, PartialEq)]
1355
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1356
pub struct EngineFailure {
1357
    pub kind: EngineErrorKind,
1358
    pub message: String,
1359
}
1360
1361
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1362
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1363
#[non_exhaustive]
1364
pub enum EngineErrorKind {
1365
    EventReceiverClosed,
1366
    Watcher,
1367
    Other,
1368
}