Browse Source

session: Remove model module

And move submodules directly under session, to reduce nesting.
fractal-13
Kévin Commaille 5 months ago
parent
commit
e3ced48c9e
No known key found for this signature in database
GPG Key ID: F26F4BE20A08255B
  1. 18
      po/POTFILES.in
  2. 2
      src/account_chooser_dialog/account_row.rs
  3. 2
      src/account_settings/encryption_page/import_export_keys_subpage.rs
  4. 4
      src/account_settings/encryption_page/mod.rs
  5. 2
      src/account_settings/general_page/change_password_subpage.rs
  6. 2
      src/account_settings/general_page/deactivate_account_subpage.rs
  7. 2
      src/account_settings/general_page/log_out_subpage.rs
  8. 2
      src/account_settings/general_page/mod.rs
  9. 2
      src/account_settings/mod.rs
  10. 2
      src/account_settings/notifications_page.rs
  11. 2
      src/account_settings/safety_page/ignored_users_subpage/ignored_user_row.rs
  12. 2
      src/account_settings/safety_page/ignored_users_subpage/mod.rs
  13. 2
      src/account_settings/safety_page/mod.rs
  14. 2
      src/account_settings/user_session/user_session_list_subpage.rs
  15. 2
      src/account_settings/user_session/user_session_row.rs
  16. 2
      src/account_settings/user_session/user_session_subpage.rs
  17. 2
      src/account_switcher/session_item.rs
  18. 2
      src/application.rs
  19. 2
      src/components/avatar/image.rs
  20. 2
      src/components/avatar/mod.rs
  21. 2
      src/components/crypto/identity_setup_view.rs
  22. 2
      src/components/crypto/recovery_setup_view.rs
  23. 2
      src/components/dialogs/auth/mod.rs
  24. 2
      src/components/dialogs/message_dialogs.rs
  25. 2
      src/components/dialogs/room_preview.rs
  26. 2
      src/components/dialogs/user_profile.rs
  27. 2
      src/components/media/audio_player/mod.rs
  28. 2
      src/components/offline_banner.rs
  29. 2
      src/components/pill/at_room.rs
  30. 2
      src/components/pill/mod.rs
  31. 2
      src/components/pill/source.rs
  32. 2
      src/components/pill/source_row.rs
  33. 2
      src/components/power_level_selection/combo_box.rs
  34. 2
      src/components/power_level_selection/popover.rs
  35. 2
      src/components/power_level_selection/row.rs
  36. 2
      src/components/role_badge.rs
  37. 2
      src/components/user_page.rs
  38. 2
      src/identity_verification_view/accept_request_page.rs
  39. 2
      src/identity_verification_view/cancelled_page.rs
  40. 2
      src/identity_verification_view/choose_method_page.rs
  41. 2
      src/identity_verification_view/completed_page.rs
  42. 2
      src/identity_verification_view/confirm_qr_code_page.rs
  43. 2
      src/identity_verification_view/mod.rs
  44. 2
      src/identity_verification_view/no_supported_methods_page.rs
  45. 2
      src/identity_verification_view/qr_code_scanned_page.rs
  46. 2
      src/identity_verification_view/room_left_page.rs
  47. 2
      src/identity_verification_view/sas_page.rs
  48. 2
      src/identity_verification_view/scan_qr_code_page.rs
  49. 2
      src/identity_verification_view/wait_for_other_page.rs
  50. 2
      src/intent.rs
  51. 4
      src/login/mod.rs
  52. 2
      src/login/session_setup_view.rs
  53. 2
      src/prelude.rs
  54. 2
      src/session/global_account_data.rs
  55. 0
      src/session/ignored_users.rs
  56. 868
      src/session/mod.rs
  57. 34
      src/session/model/mod.rs
  58. 853
      src/session/model/session.rs
  59. 0
      src/session/notifications/mod.rs
  60. 2
      src/session/notifications/notifications_settings.rs
  61. 2
      src/session/remote/cache.rs
  62. 0
      src/session/remote/mod.rs
  63. 2
      src/session/remote/room.rs
  64. 2
      src/session/remote/user.rs
  65. 0
      src/session/room/aliases.rs
  66. 2
      src/session/room/category.rs
  67. 0
      src/session/room/highlight_flags.rs
  68. 0
      src/session/room/join_rule.rs
  69. 2
      src/session/room/member.rs
  70. 0
      src/session/room/member_list.rs
  71. 0
      src/session/room/mod.rs
  72. 0
      src/session/room/permissions.rs
  73. 2
      src/session/room/timeline/event/mod.rs
  74. 2
      src/session/room/timeline/event/reaction_group.rs
  75. 2
      src/session/room/timeline/event/reaction_list.rs
  76. 0
      src/session/room/timeline/mod.rs
  77. 0
      src/session/room/timeline/timeline_diff_minimizer/mod.rs
  78. 0
      src/session/room/timeline/timeline_diff_minimizer/tests.rs
  79. 2
      src/session/room/timeline/timeline_item.rs
  80. 0
      src/session/room/timeline/virtual_item.rs
  81. 0
      src/session/room/typing_list.rs
  82. 2
      src/session/room_list/metainfo.rs
  83. 2
      src/session/room_list/mod.rs
  84. 2
      src/session/room_list/room_info.rs
  85. 0
      src/session/security.rs
  86. 0
      src/session/session_settings.rs
  87. 2
      src/session/sidebar_data/icon_item.rs
  88. 2
      src/session/sidebar_data/item.rs
  89. 2
      src/session/sidebar_data/item_list.rs
  90. 2
      src/session/sidebar_data/list_model.rs
  91. 0
      src/session/sidebar_data/mod.rs
  92. 2
      src/session/sidebar_data/section/mod.rs
  93. 2
      src/session/sidebar_data/section/name.rs
  94. 2
      src/session/sidebar_data/section/room_category_filter.rs
  95. 0
      src/session/user.rs
  96. 0
      src/session/user_sessions_list/mod.rs
  97. 2
      src/session/user_sessions_list/other_sessions_list.rs
  98. 2
      src/session/user_sessions_list/user_session.rs
  99. 2
      src/session/verification/identity_verification.rs
  100. 0
      src/session/verification/mod.rs
  101. Some files were not shown because too many files have changed in this diff Show More

18
po/POTFILES.in

@ -100,15 +100,15 @@ src/login/mod.blp
src/login/session_setup_view.blp
src/login/sso_idp_button.rs
src/secret/linux.rs
src/session/model/session.rs
src/session/model/notifications/mod.rs
src/session/model/room/join_rule.rs
src/session/model/room/mod.rs
src/session/model/room/permissions.rs
src/session/model/room_list/mod.rs
src/session/model/sidebar_data/section/name.rs
src/session/model/sidebar_data/icon_item.rs
src/session/model/user_sessions_list/user_session.rs
src/session/mod.rs
src/session/notifications/mod.rs
src/session/room/join_rule.rs
src/session/room/mod.rs
src/session/room/permissions.rs
src/session/room_list/mod.rs
src/session/sidebar_data/section/name.rs
src/session/sidebar_data/icon_item.rs
src/session/user_sessions_list/user_session.rs
src/session_view/content/explore/mod.blp
src/session_view/content/explore/public_room_row.rs
src/session_view/content/explore/servers_popover.blp

2
src/account_chooser_dialog/account_row.rs

@ -3,7 +3,7 @@ use gtk::{glib, prelude::*, subclass::prelude::*};
use crate::{
components::{Avatar, AvatarData},
prelude::*,
session::model::Session,
session::Session,
session_list::{FailedSession, SessionInfo},
};

2
src/account_settings/encryption_page/import_export_keys_subpage.rs

@ -4,7 +4,7 @@ use gtk::{gio, glib};
use matrix_sdk::encryption::{KeyExportError, RoomKeyImportError};
use tracing::{debug, error};
use crate::{components::LoadingButtonRow, session::model::Session, spawn_tokio, toast};
use crate::{components::LoadingButtonRow, session::Session, spawn_tokio, toast};
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
#[repr(u32)]

4
src/account_settings/encryption_page/mod.rs

@ -7,9 +7,7 @@ mod import_export_keys_subpage;
pub(super) use self::import_export_keys_subpage::{
ImportExportKeysSubpage, ImportExportKeysSubpageMode,
};
use crate::session::model::{
CryptoIdentityState, RecoveryState, Session, SessionVerificationState,
};
use crate::session::{CryptoIdentityState, RecoveryState, Session, SessionVerificationState};
mod imp {
use std::cell::RefCell;

2
src/account_settings/general_page/change_password_subpage.rs

@ -6,7 +6,7 @@ use tracing::error;
use crate::{
components::{AuthDialog, AuthError, LoadingButtonRow},
session::model::Session,
session::Session,
toast,
utils::matrix::validate_password,
};

2
src/account_settings/general_page/deactivate_account_subpage.rs

@ -8,7 +8,7 @@ use super::AccountSettings;
use crate::{
components::{AuthDialog, LoadingButtonRow},
prelude::*,
session::model::Session,
session::Session,
toast,
};

2
src/account_settings/general_page/log_out_subpage.rs

@ -5,7 +5,7 @@ use gtk::glib;
use crate::{
account_settings::AccountSettings,
components::LoadingButtonRow,
session::model::{CryptoIdentityState, RecoveryState, Session, SessionVerificationState},
session::{CryptoIdentityState, RecoveryState, Session, SessionVerificationState},
toast,
};

2
src/account_settings/general_page/mod.rs

@ -17,7 +17,7 @@ use super::AccountSettings;
use crate::{
components::{ActionButton, ActionState, ButtonCountRow, CopyableRow, EditableAvatar},
prelude::*,
session::model::Session,
session::Session,
spawn, spawn_tokio, toast,
utils::{OngoingAsyncAction, TemplateCallbacks, media::FileInfo},
};

2
src/account_settings/mod.rs

@ -23,7 +23,7 @@ use self::{
};
use crate::{
components::crypto::{CryptoIdentitySetupView, CryptoRecoverySetupView},
session::model::Session,
session::Session,
spawn, spawn_tokio,
utils::BoundObjectWeakRef,
};

2
src/account_settings/notifications_page.rs

@ -6,7 +6,7 @@ use tracing::error;
use crate::{
components::{CheckLoadingRow, EntryAddRow, RemovableRow, SwitchLoadingRow},
i18n::gettext_f,
session::model::{NotificationsGlobalSetting, NotificationsSettings},
session::{NotificationsGlobalSetting, NotificationsSettings},
spawn, toast,
utils::{BoundObjectWeakRef, PlaceholderObject, SingleItemListModel},
};

2
src/account_settings/safety_page/ignored_users_subpage/ignored_user_row.rs

@ -2,7 +2,7 @@ use gettextrs::gettext;
use gtk::{glib, prelude::*, subclass::prelude::*};
use ruma::UserId;
use crate::{components::LoadingButton, session::model::IgnoredUsers, toast};
use crate::{components::LoadingButton, session::IgnoredUsers, toast};
mod imp {
use std::cell::RefCell;

2
src/account_settings/safety_page/ignored_users_subpage/mod.rs

@ -5,7 +5,7 @@ use tracing::error;
mod ignored_user_row;
use self::ignored_user_row::IgnoredUserRow;
use crate::session::model::Session;
use crate::session::Session;
mod imp {
use std::cell::RefCell;

2
src/account_settings/safety_page/mod.rs

@ -9,7 +9,7 @@ mod ignored_users_subpage;
pub(super) use self::ignored_users_subpage::IgnoredUsersSubpage;
use crate::{
components::{ButtonCountRow, CheckLoadingRow, SwitchLoadingRow},
session::model::Session,
session::Session,
spawn, toast,
};

2
src/account_settings/user_session/user_session_list_subpage.rs

@ -4,7 +4,7 @@ use tracing::error;
use super::UserSessionRow;
use crate::{
session::model::{Session, UserSession, UserSessionsList},
session::{Session, UserSession, UserSessionsList},
utils::{BoundObject, LoadingState},
};

2
src/account_settings/user_session/user_session_row.rs

@ -1,7 +1,7 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::glib;
use crate::{session::model::UserSession, utils::TemplateCallbacks};
use crate::{session::UserSession, utils::TemplateCallbacks};
mod imp {
use std::cell::RefCell;

2
src/account_settings/user_session/user_session_subpage.rs

@ -9,7 +9,7 @@ use crate::{
components::{ActionButton, ActionState, AuthError, LoadingButtonRow},
gettext_f,
prelude::*,
session::model::UserSession,
session::UserSession,
toast,
utils::{BoundConstructOnlyObject, BoundObject, TemplateCallbacks},
};

2
src/account_switcher/session_item.rs

@ -4,7 +4,7 @@ use super::avatar_with_selection::AvatarWithSelection;
use crate::{
components::AvatarData,
prelude::*,
session::model::Session,
session::Session,
session_list::{FailedSession, SessionInfo},
};

2
src/application.rs

@ -9,7 +9,7 @@ use crate::{
GETTEXT_PACKAGE, Window, config,
intent::SessionIntent,
prelude::*,
session::model::{Session, SessionState},
session::{Session, SessionState},
session_list::{FailedSession, SessionInfo, SessionList},
spawn,
system_settings::SystemSettings,

2
src/components/avatar/image.rs

@ -10,7 +10,7 @@ use ruma::{
};
use crate::{
session::model::Session,
session::Session,
spawn,
utils::{
CountedRef,

2
src/components/avatar/mod.rs

@ -16,7 +16,7 @@ pub use self::{
};
use crate::{
components::AnimatedImagePaintable,
session::model::Room,
session::Room,
utils::{BoundObject, BoundObjectWeakRef, CountedRef},
};

2
src/components/crypto/identity_setup_view.rs

@ -10,7 +10,7 @@ use super::{CryptoRecoverySetupInitialPage, CryptoRecoverySetupView};
use crate::{
components::{AuthDialog, AuthError, LoadingButton},
identity_verification_view::IdentityVerificationView,
session::model::{
session::{
CryptoIdentityState, IdentityVerification, RecoveryState, Session, SessionVerificationState,
},
spawn, toast,

2
src/components/crypto/recovery_setup_view.rs

@ -9,7 +9,7 @@ use tracing::{debug, error, warn};
use crate::{
components::{AuthDialog, AuthError, LoadingButton, SwitchLoadingRow},
session::model::{RecoveryState, Session},
session::{RecoveryState, Session},
spawn_tokio, toast,
};

2
src/components/dialogs/auth/mod.rs

@ -22,7 +22,7 @@ mod password_page;
use self::{in_browser_page::AuthDialogInBrowserPage, password_page::AuthDialogPasswordPage};
use crate::{
components::ToastableDialog, prelude::*, session::model::Session, spawn_tokio, toast,
components::ToastableDialog, prelude::*, session::Session, spawn_tokio, toast,
utils::OneshotNotifier,
};

2
src/components/dialogs/message_dialogs.rs

@ -7,7 +7,7 @@ use crate::{
i18n::gettext_f,
ngettext_f,
prelude::*,
session::model::{Member, Membership, Room, RoomCategory, User},
session::{Member, Membership, Room, RoomCategory, User},
};
/// Show a dialog to confirm leaving a room.

2
src/components/dialogs/room_preview.rs

@ -8,7 +8,7 @@ use crate::{
components::{Avatar, LoadingButton},
i18n::ngettext_f,
prelude::*,
session::model::{RemoteRoom, Session},
session::{RemoteRoom, Session},
toast,
utils::{
LoadingState,

2
src/components/dialogs/user_profile.rs

@ -6,7 +6,7 @@ use super::ToastableDialog;
use crate::{
components::UserPage,
prelude::*,
session::model::{Member, Session, User},
session::{Member, Session, User},
utils::LoadingState,
};

2
src/components/media/audio_player/mod.rs

@ -11,7 +11,7 @@ mod waveform_paintable;
use self::waveform::Waveform;
use crate::{
MEDIA_FILE_NOTIFIER,
session::model::Session,
session::Session,
spawn,
utils::{
File, LoadingState, OneshotNotifier,

2
src/components/offline_banner.rs

@ -1,7 +1,7 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::{gio, glib, glib::clone};
use crate::{session::model::Session, utils::BoundObjectWeakRef};
use crate::{session::Session, utils::BoundObjectWeakRef};
mod imp {
use std::cell::RefCell;

2
src/components/pill/at_room.rs

@ -2,7 +2,7 @@ use gettextrs::gettext;
use gtk::{glib, prelude::*, subclass::prelude::*};
use ruma::RoomId;
use crate::{components::PillSource, prelude::*, session::model::Room};
use crate::{components::PillSource, prelude::*, session::Room};
mod imp {
use std::cell::OnceCell;

2
src/components/pill/mod.rs

@ -15,7 +15,7 @@ pub use self::{
use super::{Avatar, AvatarImageSafetySetting, RoomPreviewDialog, UserProfileDialog};
use crate::{
prelude::*,
session::model::{Member, RemoteRoom, Room},
session::{Member, RemoteRoom, Room},
session_view::SessionView,
utils::{BoundObject, key_bindings},
};

2
src/components/pill/source.rs

@ -3,7 +3,7 @@ use gtk::{glib, prelude::*, subclass::prelude::*};
use super::Pill;
use crate::{
components::{AvatarData, AvatarImageSafetySetting},
session::model::Room,
session::Room,
};
mod imp {

2
src/components/pill/source_row.rs

@ -1,7 +1,7 @@
use gtk::{glib, prelude::*, subclass::prelude::*};
use super::{AtRoom, Avatar, AvatarImageSafetySetting, PillSource};
use crate::session::model::Room;
use crate::session::Room;
mod imp {
use std::cell::RefCell;

2
src/components/power_level_selection/combo_box.rs

@ -3,7 +3,7 @@ use gtk::{gdk, glib};
use ruma::{Int, events::room::power_levels::UserPowerLevel};
use super::PowerLevelSelectionPopover;
use crate::{components::RoleBadge, session::model::Permissions};
use crate::{components::RoleBadge, session::Permissions};
mod imp {
use std::cell::{Cell, RefCell};

2
src/components/power_level_selection/popover.rs

@ -3,7 +3,7 @@ use gtk::{glib, glib::clone};
use ruma::events::room::power_levels::UserPowerLevel;
use crate::{
session::model::{POWER_LEVEL_ADMIN, POWER_LEVEL_MAX, POWER_LEVEL_MOD, Permissions},
session::{POWER_LEVEL_ADMIN, POWER_LEVEL_MAX, POWER_LEVEL_MOD, Permissions},
utils::BoundObject,
};

2
src/components/power_level_selection/row.rs

@ -5,7 +5,7 @@ use ruma::{Int, events::room::power_levels::UserPowerLevel, int};
use super::PowerLevelSelectionPopover;
use crate::{
components::{LoadingBin, RoleBadge},
session::model::Permissions,
session::Permissions,
};
mod imp {

2
src/components/role_badge.rs

@ -1,7 +1,7 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::glib;
use crate::session::model::MemberRole;
use crate::session::MemberRole;
mod imp {
use std::cell::Cell;

2
src/components/user_page.rs

@ -21,7 +21,7 @@ use crate::{
},
gettext_f,
prelude::*,
session::model::{Member, Membership, Permissions, Room, User},
session::{Member, Membership, Permissions, Room, User},
toast,
utils::BoundObject,
};

2
src/identity_verification_view/accept_request_page.rs

@ -3,7 +3,7 @@ use gettextrs::gettext;
use gtk::{glib, glib::clone, prelude::*};
use crate::{
components::LoadingButton, gettext_f, prelude::*, session::model::IdentityVerification, toast,
components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
};
mod imp {

2
src/identity_verification_view/cancelled_page.rs

@ -5,7 +5,7 @@ use matrix_sdk::crypto::CancelInfo;
use ruma::events::key::verification::cancel::CancelCode;
use crate::{
components::LoadingButton, gettext_f, prelude::*, session::model::IdentityVerification, toast,
components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
utils::BoundObjectWeakRef,
};

2
src/identity_verification_view/choose_method_page.rs

@ -7,7 +7,7 @@ use crate::{
contrib::QRCode,
gettext_f,
prelude::*,
session::model::{IdentityVerification, VerificationSupportedMethods},
session::{IdentityVerification, VerificationSupportedMethods},
toast,
utils::BoundObjectWeakRef,
};

2
src/identity_verification_view/completed_page.rs

@ -2,7 +2,7 @@ use adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
use gtk::{glib, glib::clone};
use crate::{gettext_f, prelude::*, session::model::IdentityVerification};
use crate::{gettext_f, prelude::*, session::IdentityVerification};
mod imp {
use std::cell::RefCell;

2
src/identity_verification_view/confirm_qr_code_page.rs

@ -3,7 +3,7 @@ use gettextrs::gettext;
use gtk::{glib, glib::clone};
use crate::{
components::LoadingButton, gettext_f, prelude::*, session::model::IdentityVerification, toast,
components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
};
mod imp {

2
src/identity_verification_view/mod.rs

@ -23,7 +23,7 @@ use self::{
scan_qr_code_page::ScanQrCodePage, wait_for_other_page::WaitForOtherPage,
};
use crate::{
session::model::{IdentityVerification, VerificationState},
session::{IdentityVerification, VerificationState},
utils::BoundObject,
};

2
src/identity_verification_view/no_supported_methods_page.rs

@ -3,7 +3,7 @@ use gettextrs::gettext;
use gtk::{glib, glib::clone};
use crate::{
components::LoadingButton, gettext_f, prelude::*, session::model::IdentityVerification, toast,
components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
utils::BoundObjectWeakRef,
};

2
src/identity_verification_view/qr_code_scanned_page.rs

@ -3,7 +3,7 @@ use gettextrs::gettext;
use gtk::{glib, glib::clone};
use crate::{
components::LoadingButton, gettext_f, prelude::*, session::model::IdentityVerification, toast,
components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
};
mod imp {

2
src/identity_verification_view/room_left_page.rs

@ -1,7 +1,7 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::glib;
use crate::session::model::IdentityVerification;
use crate::session::IdentityVerification;
mod imp {
use glib::subclass::InitializingObject;

2
src/identity_verification_view/sas_page.rs

@ -6,7 +6,7 @@ use gtk::{gio, glib, glib::clone};
use super::sas_emoji::SasEmoji;
use crate::{
components::LoadingButton, gettext_f, prelude::*, session::model::IdentityVerification, toast,
components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
utils::BoundObjectWeakRef,
};

2
src/identity_verification_view/scan_qr_code_page.rs

@ -8,7 +8,7 @@ use crate::{
components::{LoadingButton, QrCodeScanner},
gettext_f,
prelude::*,
session::model::{IdentityVerification, VerificationSupportedMethods},
session::{IdentityVerification, VerificationSupportedMethods},
spawn, toast,
utils::BoundConstructOnlyObject,
};

2
src/identity_verification_view/wait_for_other_page.rs

@ -3,7 +3,7 @@ use gettextrs::gettext;
use gtk::{glib, glib::clone};
use crate::{
components::LoadingButton, gettext_f, prelude::*, session::model::IdentityVerification, toast,
components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
};
mod imp {

2
src/intent.rs

@ -2,7 +2,7 @@ use std::borrow::Cow;
use gtk::{glib, prelude::*};
use crate::{session::model::VerificationKey, utils::matrix::MatrixIdUri};
use crate::{session::VerificationKey, utils::matrix::MatrixIdUri};
/// Intents that can be handled by a session.
///

4
src/login/mod.rs

@ -34,8 +34,8 @@ use self::{
};
use crate::{
APP_HOMEPAGE_URL, APP_NAME, Application, RUNTIME, SETTINGS_KEY_CURRENT_SESSION, Window,
components::OfflineBanner, prelude::*, secret::Secret, session::model::Session, spawn,
spawn_tokio, toast,
components::OfflineBanner, prelude::*, secret::Secret, session::Session, spawn, spawn_tokio,
toast,
};
/// A page of the login stack.

2
src/login/session_setup_view.rs

@ -8,7 +8,7 @@ use crate::{
components::crypto::{
CryptoIdentitySetupNextStep, CryptoIdentitySetupView, CryptoRecoverySetupView,
},
session::model::{CryptoIdentityState, RecoveryState, Session, SessionVerificationState},
session::{CryptoIdentityState, RecoveryState, Session, SessionVerificationState},
spawn, spawn_tokio,
};

2
src/prelude.rs

@ -4,7 +4,7 @@ pub(crate) use crate::{
ToastableDialogExt, ToastableDialogImpl,
},
secret::SecretExt,
session::model::{TimelineItemExt, UserExt},
session::{TimelineItemExt, UserExt},
session_list::SessionInfoExt,
user_facing_error::UserFacingError,
utils::{

2
src/session/model/global_account_data.rs → src/session/global_account_data.rs

@ -12,7 +12,7 @@ use tokio::task::AbortHandle;
use tracing::error;
use super::{Room, Session};
use crate::{session::model::JoinRuleValue, spawn, spawn_tokio};
use crate::{session::JoinRuleValue, spawn, spawn_tokio};
/// We default the media previews setting to private.
const DEFAULT_MEDIA_PREVIEWS: MediaPreviews = MediaPreviews::Private;

0
src/session/model/ignored_users.rs → src/session/ignored_users.rs

868
src/session/mod.rs

@ -1 +1,867 @@
pub mod model;
use std::time::Duration;
use futures_util::{StreamExt, lock::Mutex};
use gettextrs::gettext;
use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use matrix_sdk::{
Client, SessionChange, config::SyncSettings, media::MediaRetentionPolicy, sync::SyncResponse,
};
use ruma::{
api::client::{
filter::{FilterDefinition, RoomFilter},
profile::{AvatarUrl, DisplayName},
search::search_events::v3::UserProfile,
},
assign,
};
use tokio::{task::AbortHandle, time::sleep};
use tokio_stream::wrappers::BroadcastStream;
use tracing::{debug, error, info};
mod global_account_data;
mod ignored_users;
mod notifications;
mod remote;
mod room;
mod room_list;
mod security;
mod session_settings;
mod sidebar_data;
mod user;
mod user_sessions_list;
mod verification;
pub(crate) use self::{
global_account_data::*, ignored_users::*, notifications::*, remote::*, room::*, room_list::*,
security::*, session_settings::*, sidebar_data::*, user::*, user_sessions_list::*,
verification::*,
};
use crate::{
Application,
components::AvatarData,
prelude::*,
secret::StoredSession,
session_list::{SessionInfo, SessionInfoImpl},
spawn, spawn_tokio,
utils::{
TokioDrop,
matrix::{self, ClientSetupError},
},
};
/// The database key for persisting the session's profile.
const SESSION_PROFILE_KEY: &str = "session_profile";
/// The number of consecutive missed synchronizations before the session is
/// marked as offline.
///
/// Note that this is set to `2`, but the count begins at `0` so this would
/// match the third missed synchronization.
const MISSED_SYNC_OFFLINE_COUNT: usize = 2;
/// The delays in seconds to wait for when a sync fails, depending on the number
/// of missed attempts.
const MISSED_SYNC_DELAYS: &[u64] = &[1, 5, 10, 20, 30];
/// The state of the session.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, glib::Enum)]
#[repr(i32)]
#[enum_type(name = "SessionState")]
pub enum SessionState {
LoggedOut = -1,
#[default]
Init = 0,
InitialSync = 1,
Ready = 2,
}
mod imp {
use std::cell::{Cell, OnceCell, RefCell};
use super::*;
#[derive(Debug, Default, glib::Properties)]
#[properties(wrapper_type = super::Session)]
pub struct Session {
/// The Matrix client for this session.
client: OnceCell<TokioDrop<Client>>,
/// The list model of the sidebar.
#[property(get = Self::sidebar_list_model)]
sidebar_list_model: OnceCell<SidebarListModel>,
/// The user of this session.
#[property(get = Self::user)]
user: OnceCell<User>,
/// The current state of the session.
#[property(get, builder(SessionState::default()))]
state: Cell<SessionState>,
/// Whether this session has a connection to the homeserver.
#[property(get)]
is_homeserver_reachable: Cell<bool>,
/// Whether this session is synchronized with the homeserver.
#[property(get)]
is_offline: Cell<bool>,
/// The current settings for this session.
#[property(get, construct_only)]
settings: OnceCell<SessionSettings>,
/// The settings in the global account data for this session.
#[property(get = Self::global_account_data_owned)]
global_account_data: OnceCell<GlobalAccountData>,
/// The notifications API for this session.
#[property(get)]
notifications: Notifications,
/// The ignored users API for this session.
#[property(get)]
ignored_users: IgnoredUsers,
/// The list of sessions for this session's user.
#[property(get)]
user_sessions: UserSessionsList,
/// Information about security for this session.
#[property(get)]
security: SessionSecurity,
/// The cache for remote data.
remote_cache: OnceCell<RemoteCache>,
session_changes_handle: RefCell<Option<AbortHandle>>,
sync_handle: RefCell<Option<AbortHandle>>,
network_monitor_handler_id: RefCell<Option<glib::SignalHandlerId>>,
homeserver_reachable_lock: Mutex<()>,
homeserver_reachable_source: RefCell<Option<glib::SourceId>>,
/// The number of missed synchronizations in a row.
///
/// Capped at `MISSED_SYNC_DELAYS.len() - 1`.
missed_sync_count: Cell<usize>,
}
#[glib::object_subclass]
impl ObjectSubclass for Session {
const NAME: &'static str = "Session";
type Type = super::Session;
type ParentType = SessionInfo;
}
#[glib::derived_properties]
impl ObjectImpl for Session {
fn dispose(&self) {
// Needs to be disconnected or else it may restart the sync
if let Some(handler_id) = self.network_monitor_handler_id.take() {
gio::NetworkMonitor::default().disconnect(handler_id);
}
if let Some(source) = self.homeserver_reachable_source.take() {
source.remove();
}
if let Some(handle) = self.session_changes_handle.take() {
handle.abort();
}
if let Some(handle) = self.sync_handle.take() {
handle.abort();
}
}
}
impl SessionInfoImpl for Session {
fn avatar_data(&self) -> AvatarData {
self.user().avatar_data().clone()
}
}
impl Session {
/// Set the Matrix client for this session.
pub(super) fn set_client(&self, client: Client) {
self.client
.set(TokioDrop::new(client))
.expect("client should be uninitialized");
let obj = self.obj();
self.ignored_users.set_session(Some(obj.clone()));
self.notifications.set_session(Some(obj.clone()));
self.user_sessions.init(&obj, obj.user_id().clone());
let monitor = gio::NetworkMonitor::default();
let handler_id = monitor.connect_network_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _| {
spawn!(async move {
imp.update_homeserver_reachable().await;
});
}
));
self.network_monitor_handler_id.replace(Some(handler_id));
}
/// The Matrix client for this session.
pub(super) fn client(&self) -> &Client {
self.client.get().expect("client should be initialized")
}
/// The list model of the sidebar.
fn sidebar_list_model(&self) -> SidebarListModel {
self.sidebar_list_model
.get_or_init(|| {
let obj = self.obj();
let item_list =
SidebarItemList::new(&RoomList::new(&obj), &VerificationList::new(&obj));
SidebarListModel::new(&item_list)
})
.clone()
}
/// The room list of this session.
pub(super) fn room_list(&self) -> RoomList {
self.sidebar_list_model().item_list().room_list()
}
/// The verification list of this session.
pub(super) fn verification_list(&self) -> VerificationList {
self.sidebar_list_model().item_list().verification_list()
}
/// The user of the session.
fn user(&self) -> User {
self.user
.get_or_init(|| {
let obj = self.obj();
User::new(&obj, obj.info().user_id.clone())
})
.clone()
}
/// Set the current state of the session.
fn set_state(&self, state: SessionState) {
let old_state = self.state.get();
if old_state == SessionState::LoggedOut || old_state == state {
// The session should be dismissed when it has been logged out, so
// we do not accept anymore state changes.
return;
}
self.state.set(state);
self.obj().notify_state();
}
/// The homeserver URL as a `GNetworkAddress`.
fn homeserver_address(&self) -> gio::NetworkAddress {
let obj = self.obj();
let homeserver = obj.homeserver();
let default_port = if homeserver.scheme() == "http" {
80
} else {
443
};
gio::NetworkAddress::parse_uri(homeserver.as_str(), default_port)
.expect("url is parsed successfully")
}
/// Check whether the homeserver is reachable.
pub(super) async fn update_homeserver_reachable(&self) {
// If there is a timeout, remove it, we will add a new one later if needed.
if let Some(source) = self.homeserver_reachable_source.take() {
source.remove();
}
let Some(_guard) = self.homeserver_reachable_lock.try_lock() else {
// There is an ongoing check.
return;
};
let monitor = gio::NetworkMonitor::default();
let is_network_available = monitor.is_network_available();
let is_homeserver_reachable = if is_network_available {
// Check if we can reach the homeserver.
let address = self.homeserver_address();
match monitor.can_reach_future(&address).await {
Ok(()) => true,
Err(error) => {
error!(
session = self.obj().session_id(),
"Homeserver is not reachable: {error}"
);
false
}
}
} else {
false
};
self.set_is_homeserver_reachable(is_homeserver_reachable);
if is_network_available && !is_homeserver_reachable {
// Check again later if the homeserver is reachable.
let source = glib::timeout_add_seconds_local_once(
10,
clone!(
#[weak(rename_to = imp)]
self,
move || {
imp.homeserver_reachable_source.take();
spawn!(async move {
imp.update_homeserver_reachable().await;
});
}
),
);
self.homeserver_reachable_source.replace(Some(source));
}
}
/// Set whether the homeserver is reachable.
fn set_is_homeserver_reachable(&self, is_reachable: bool) {
if self.is_homeserver_reachable.get() == is_reachable {
return;
}
let obj = self.obj();
self.is_homeserver_reachable.set(is_reachable);
if let Some(handle) = self.sync_handle.take() {
handle.abort();
}
if is_reachable {
info!(session = obj.session_id(), "Homeserver is reachable");
// Restart the sync loop.
self.sync();
} else {
self.set_offline(true);
}
obj.notify_is_homeserver_reachable();
}
/// Set whether this session is synchronized with the homeserver.
pub(super) fn set_offline(&self, is_offline: bool) {
if self.is_offline.get() == is_offline {
return;
}
if !is_offline {
// Restart the send queues, in case they were stopped.
let client = self.client().clone();
spawn_tokio!(async move {
client.send_queue().set_enabled(true).await;
});
}
self.is_offline.set(is_offline);
self.obj().notify_is_offline();
}
/// The settings stored in the global account data for this session.
fn global_account_data(&self) -> &GlobalAccountData {
self.global_account_data
.get_or_init(|| GlobalAccountData::new(&self.obj()))
}
/// The owned settings stored in the global account data for this
/// session.
fn global_account_data_owned(&self) -> GlobalAccountData {
self.global_account_data().clone()
}
/// The cache for remote data.
pub(super) fn remote_cache(&self) -> &RemoteCache {
self.remote_cache
.get_or_init(|| RemoteCache::new(self.obj().clone()))
}
/// Finish initialization of this session.
pub(super) async fn prepare(&self) {
spawn!(
glib::Priority::LOW,
clone!(
#[weak(rename_to = imp)]
self,
async move {
// First, load the profile from the cache, it will be quicker.
imp.init_user_profile().await;
// Then, check if the profile changed.
imp.update_user_profile().await;
}
)
);
self.global_account_data();
self.watch_session_changes();
self.update_homeserver_reachable().await;
self.room_list().load().await;
self.verification_list().init();
self.security.set_session(Some(&*self.obj()));
let client = self.client().clone();
spawn_tokio!(async move {
client
.send_queue()
.respawn_tasks_for_rooms_with_unsent_requests()
.await;
});
self.set_state(SessionState::InitialSync);
self.sync();
debug!(
session = self.obj().session_id(),
"A new session was prepared"
);
}
/// Watch the changes of the session, like being logged out or the
/// tokens being refreshed.
fn watch_session_changes(&self) {
let receiver = self.client().subscribe_to_session_changes();
let stream = BroadcastStream::new(receiver);
let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
let fut = stream.for_each(move |change| {
let obj_weak = obj_weak.clone();
async move {
let Ok(change) = change else {
return;
};
let ctx = glib::MainContext::default();
ctx.spawn(async move {
spawn!(async move {
if let Some(obj) = obj_weak.upgrade() {
match change {
SessionChange::UnknownToken { .. } => {
info!(
session = obj.session_id(),
"The access token is invalid, cleaning up the session…"
);
obj.imp().clean_up().await;
}
SessionChange::TokensRefreshed => {
obj.imp().store_tokens().await;
}
}
}
});
});
}
});
let handle = spawn_tokio!(fut).abort_handle();
self.session_changes_handle.replace(Some(handle));
}
/// Start syncing the Matrix client.
fn sync(&self) {
if self.state.get() < SessionState::InitialSync || !self.is_homeserver_reachable.get() {
return;
}
let client = self.client().clone();
let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
let handle = spawn_tokio!(async move {
// Make sure that the event cache is subscribed to sync responses to benefit
// from it.
if let Err(error) = client.event_cache().subscribe() {
error!("Could not subscribe event cache to sync responses: {error}");
}
// TODO: only create the filter once and reuse it in the future
let filter = assign!(FilterDefinition::default(), {
room: assign!(RoomFilter::with_lazy_loading(), {
include_leave: true,
}),
});
let sync_settings = SyncSettings::new()
.timeout(Duration::from_secs(30))
.ignore_timeout_on_first_sync(true)
.filter(filter.into());
let mut sync_stream = Box::pin(client.sync_stream(sync_settings).await);
while let Some(response) = sync_stream.next().await {
let obj_weak = obj_weak.clone();
let ctx = glib::MainContext::default();
let delay = ctx
.spawn(async move {
spawn!(async move {
if let Some(obj) = obj_weak.upgrade() {
obj.imp().handle_sync_response(response)
} else {
None
}
})
.await
.expect("task was not aborted")
})
.await
.expect("task was not aborted");
if let Some(delay) = delay {
sleep(delay).await;
}
}
})
.abort_handle();
self.sync_handle.replace(Some(handle));
}
/// Handle the response received via sync.
///
/// Returns the delay to wait for before making the next sync, if
/// necessary.
fn handle_sync_response(
&self,
response: Result<SyncResponse, matrix_sdk::Error>,
) -> Option<Duration> {
let obj = self.obj();
let session_id = obj.session_id();
debug!(session = session_id, "Received sync response");
match response {
Ok(response) => {
self.room_list().handle_room_updates(response.rooms);
if self.state.get() < SessionState::Ready {
self.set_state(SessionState::Ready);
self.init_notifications();
}
self.set_offline(false);
self.missed_sync_count.set(0);
None
}
Err(error) => {
let missed_sync_count = self.missed_sync_count.get();
// If there are too many failed attempts, mark the session as offline.
if missed_sync_count == MISSED_SYNC_OFFLINE_COUNT {
self.set_offline(true);
}
// Increase the count of missed syncs, if we have not reached the maximum value.
if missed_sync_count < 4 {
self.missed_sync_count.set(missed_sync_count + 1);
}
error!(session = session_id, "Could not perform sync: {error}");
// Sleep a little between attempts.
let delay = MISSED_SYNC_DELAYS[missed_sync_count];
Some(Duration::from_secs(delay))
}
}
}
/// Load the cached profile of the user of this session.
async fn init_user_profile(&self) {
let client = self.client().clone();
let handle = spawn_tokio!(async move {
client
.state_store()
.get_custom_value(SESSION_PROFILE_KEY.as_bytes())
.await
});
let profile = match handle.await.expect("task was not aborted") {
Ok(Some(bytes)) => match serde_json::from_slice::<UserProfile>(&bytes) {
Ok(profile) => profile,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not deserialize session profile: {error}"
);
return;
}
},
Ok(None) => return,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not load cached session profile: {error}"
);
return;
}
};
let user = self.user();
user.set_name(profile.displayname);
user.set_avatar_url(profile.avatar_url);
}
/// Update the profile of this session’s user.
///
/// Fetches the updated profile and updates the local data.
async fn update_user_profile(&self) {
let client = self.client().clone();
let client_clone = client.clone();
let handle =
spawn_tokio!(async move { client_clone.account().fetch_user_profile().await });
let profile = match handle
.await
.expect("task was not aborted")
.and_then(|response| {
let mut profile = UserProfile::new();
profile.displayname = response.get_static::<DisplayName>()?;
profile.avatar_url = response.get_static::<AvatarUrl>()?;
Ok(profile)
}) {
Ok(profile) => profile,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not fetch session profile: {error}"
);
return;
}
};
let user = self.user();
if Some(user.display_name()) == profile.displayname
&& user
.avatar_data()
.image()
.is_some_and(|i| i.uri() == profile.avatar_url)
{
// Nothing to update.
return;
}
// Serialize first for caching to avoid a clone.
let value = serde_json::to_vec(&profile);
// Update the profile for the UI.
user.set_name(profile.displayname);
user.set_avatar_url(profile.avatar_url);
// Update the cache.
let value = match value {
Ok(value) => value,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not serialize session profile: {error}"
);
return;
}
};
let handle = spawn_tokio!(async move {
client
.state_store()
.set_custom_value(SESSION_PROFILE_KEY.as_bytes(), value)
.await
});
if let Err(error) = handle.await.expect("task was not aborted") {
error!(
session = self.obj().session_id(),
"Could not cache session profile: {error}"
);
}
}
/// Start listening to notifications.
fn init_notifications(&self) {
let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
let client = self.client().clone();
spawn_tokio!(async move {
client
.register_notification_handler(move |notification, room, _| {
let obj_weak = obj_weak.clone();
async move {
let ctx = glib::MainContext::default();
ctx.spawn(async move {
spawn!(async move {
if let Some(obj) = obj_weak.upgrade() {
obj.notifications().show_push(notification, room).await;
}
});
});
}
})
.await;
});
}
/// Update the stored session tokens.
async fn store_tokens(&self) {
let Some(session_tokens) = self.client().session_tokens() else {
return;
};
debug!(
session = self.obj().session_id(),
"Storing updated session tokens…"
);
self.obj().info().store_tokens(session_tokens).await;
}
/// Clean up this session after it was logged out.
///
/// This should only be called if the session has been logged out
/// without calling `Session::log_out`.
pub(super) async fn clean_up(&self) {
let obj = self.obj();
self.set_state(SessionState::LoggedOut);
if let Some(handle) = self.sync_handle.take() {
handle.abort();
}
if let Some(settings) = self.settings.get() {
settings.delete();
}
obj.info().clone().delete().await;
self.notifications.clear();
debug!(
session = obj.session_id(),
"The logged out session was cleaned up"
);
}
}
}
glib::wrapper! {
/// A Matrix user session.
pub struct Session(ObjectSubclass<imp::Session>)
@extends SessionInfo;
}
impl Session {
/// Construct an existing session.
pub(crate) async fn new(
stored_session: StoredSession,
settings: SessionSettings,
) -> Result<Self, ClientSetupError> {
let tokens = stored_session
.load_tokens()
.await
.ok_or(ClientSetupError::NoSessionTokens)?;
let stored_session_clone = stored_session.clone();
let client = spawn_tokio!(async move {
let client = matrix::client_with_stored_session(stored_session_clone, tokens).await?;
// Make sure that we use the proper retention policy.
let media = client.media();
let used_media_retention_policy = media.media_retention_policy().await?;
let wanted_media_retention_policy = MediaRetentionPolicy::default();
if used_media_retention_policy != wanted_media_retention_policy {
media
.set_media_retention_policy(wanted_media_retention_policy)
.await?;
}
Ok::<_, ClientSetupError>(client)
})
.await
.expect("task was not aborted")?;
let obj = glib::Object::builder::<Self>()
.property("info", stored_session)
.property("settings", settings)
.build();
obj.imp().set_client(client);
Ok(obj)
}
/// Create a new session from the session of the given Matrix client.
pub(crate) async fn create(client: &Client) -> Result<Self, ClientSetupError> {
let stored_session = StoredSession::new(client).await?;
let settings = Application::default()
.session_list()
.settings()
.get_or_create(&stored_session.id);
Self::new(stored_session, settings).await
}
/// Finish initialization of this session.
pub(crate) async fn prepare(&self) {
self.imp().prepare().await;
}
/// The room list of this session.
pub(crate) fn room_list(&self) -> RoomList {
self.imp().room_list()
}
/// The verification list of this session.
pub(crate) fn verification_list(&self) -> VerificationList {
self.imp().verification_list()
}
/// The Matrix client.
pub(crate) fn client(&self) -> Client {
self.imp().client().clone()
}
/// The cache for remote data.
pub(crate) fn remote_cache(&self) -> &RemoteCache {
self.imp().remote_cache()
}
/// Log out of this session.
pub(crate) async fn log_out(&self) -> Result<(), String> {
debug!(
session = self.session_id(),
"The session is about to be logged out"
);
let client = self.client();
let handle = spawn_tokio!(async move { client.logout().await });
match handle.await.expect("task was not aborted") {
Ok(()) => {
self.imp().clean_up().await;
Ok(())
}
Err(error) => {
error!(
session = self.session_id(),
"Could not log the session out: {error}"
);
Err(gettext("Could not log the session out"))
}
}
}
/// Clean up this session after it was logged out.
///
/// This should only be called if the session has been logged out without
/// calling `Session::log_out`.
pub(crate) async fn clean_up(&self) {
self.imp().clean_up().await;
}
/// Connect to the signal emitted when this session is logged out.
pub(crate) fn connect_logged_out<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_state_notify(move |obj| {
if obj.state() == SessionState::LoggedOut {
f(obj);
}
})
}
/// Connect to the signal emitted when this session is ready.
pub(crate) fn connect_ready<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_state_notify(move |obj| {
if obj.state() == SessionState::Ready {
f(obj);
}
})
}
}

34
src/session/model/mod.rs

@ -1,34 +0,0 @@
mod global_account_data;
mod ignored_users;
mod notifications;
mod remote;
mod room;
mod room_list;
mod security;
mod session;
mod session_settings;
mod sidebar_data;
mod user;
mod user_sessions_list;
mod verification;
pub(crate) use self::{
global_account_data::*,
ignored_users::IgnoredUsers,
notifications::{
Notifications, NotificationsGlobalSetting, NotificationsRoomSetting, NotificationsSettings,
},
remote::*,
room::*,
room_list::*,
security::*,
session::*,
session_settings::*,
sidebar_data::{
SidebarIconItem, SidebarIconItemType, SidebarItemList, SidebarListModel, SidebarSection,
SidebarSectionName,
},
user::{User, UserExt},
user_sessions_list::{UserSession, UserSessionsList},
verification::*,
};

853
src/session/model/session.rs

@ -1,853 +0,0 @@
use std::time::Duration;
use futures_util::{StreamExt, lock::Mutex};
use gettextrs::gettext;
use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use matrix_sdk::{
Client, SessionChange, config::SyncSettings, media::MediaRetentionPolicy, sync::SyncResponse,
};
use ruma::{
api::client::{
filter::{FilterDefinition, RoomFilter},
profile::{AvatarUrl, DisplayName},
search::search_events::v3::UserProfile,
},
assign,
};
use tokio::{task::AbortHandle, time::sleep};
use tokio_stream::wrappers::BroadcastStream;
use tracing::{debug, error, info};
use super::{
GlobalAccountData, IgnoredUsers, Notifications, RemoteCache, RoomList, SessionSecurity,
SessionSettings, SidebarItemList, SidebarListModel, User, UserSessionsList, VerificationList,
};
use crate::{
Application,
components::AvatarData,
prelude::*,
secret::StoredSession,
session_list::{SessionInfo, SessionInfoImpl},
spawn, spawn_tokio,
utils::{
TokioDrop,
matrix::{self, ClientSetupError},
},
};
/// The database key for persisting the session's profile.
const SESSION_PROFILE_KEY: &str = "session_profile";
/// The number of consecutive missed synchronizations before the session is
/// marked as offline.
///
/// Note that this is set to `2`, but the count begins at `0` so this would
/// match the third missed synchronization.
const MISSED_SYNC_OFFLINE_COUNT: usize = 2;
/// The delays in seconds to wait for when a sync fails, depending on the number
/// of missed attempts.
const MISSED_SYNC_DELAYS: &[u64] = &[1, 5, 10, 20, 30];
/// The state of the session.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, glib::Enum)]
#[repr(i32)]
#[enum_type(name = "SessionState")]
pub enum SessionState {
LoggedOut = -1,
#[default]
Init = 0,
InitialSync = 1,
Ready = 2,
}
mod imp {
use std::cell::{Cell, OnceCell, RefCell};
use super::*;
#[derive(Debug, Default, glib::Properties)]
#[properties(wrapper_type = super::Session)]
pub struct Session {
/// The Matrix client for this session.
client: OnceCell<TokioDrop<Client>>,
/// The list model of the sidebar.
#[property(get = Self::sidebar_list_model)]
sidebar_list_model: OnceCell<SidebarListModel>,
/// The user of this session.
#[property(get = Self::user)]
user: OnceCell<User>,
/// The current state of the session.
#[property(get, builder(SessionState::default()))]
state: Cell<SessionState>,
/// Whether this session has a connection to the homeserver.
#[property(get)]
is_homeserver_reachable: Cell<bool>,
/// Whether this session is synchronized with the homeserver.
#[property(get)]
is_offline: Cell<bool>,
/// The current settings for this session.
#[property(get, construct_only)]
settings: OnceCell<SessionSettings>,
/// The settings in the global account data for this session.
#[property(get = Self::global_account_data_owned)]
global_account_data: OnceCell<GlobalAccountData>,
/// The notifications API for this session.
#[property(get)]
notifications: Notifications,
/// The ignored users API for this session.
#[property(get)]
ignored_users: IgnoredUsers,
/// The list of sessions for this session's user.
#[property(get)]
user_sessions: UserSessionsList,
/// Information about security for this session.
#[property(get)]
security: SessionSecurity,
/// The cache for remote data.
remote_cache: OnceCell<RemoteCache>,
session_changes_handle: RefCell<Option<AbortHandle>>,
sync_handle: RefCell<Option<AbortHandle>>,
network_monitor_handler_id: RefCell<Option<glib::SignalHandlerId>>,
homeserver_reachable_lock: Mutex<()>,
homeserver_reachable_source: RefCell<Option<glib::SourceId>>,
/// The number of missed synchonizations in a row.
///
/// Capped at `MISSED_SYNC_DELAYS.len() - 1`.
missed_sync_count: Cell<usize>,
}
#[glib::object_subclass]
impl ObjectSubclass for Session {
const NAME: &'static str = "Session";
type Type = super::Session;
type ParentType = SessionInfo;
}
#[glib::derived_properties]
impl ObjectImpl for Session {
fn dispose(&self) {
// Needs to be disconnected or else it may restart the sync
if let Some(handler_id) = self.network_monitor_handler_id.take() {
gio::NetworkMonitor::default().disconnect(handler_id);
}
if let Some(source) = self.homeserver_reachable_source.take() {
source.remove();
}
if let Some(handle) = self.session_changes_handle.take() {
handle.abort();
}
if let Some(handle) = self.sync_handle.take() {
handle.abort();
}
}
}
impl SessionInfoImpl for Session {
fn avatar_data(&self) -> AvatarData {
self.user().avatar_data().clone()
}
}
impl Session {
/// Set the Matrix client for this session.
pub(super) fn set_client(&self, client: Client) {
self.client
.set(TokioDrop::new(client))
.expect("client should be uninitialized");
let obj = self.obj();
self.ignored_users.set_session(Some(obj.clone()));
self.notifications.set_session(Some(obj.clone()));
self.user_sessions.init(&obj, obj.user_id().clone());
let monitor = gio::NetworkMonitor::default();
let handler_id = monitor.connect_network_changed(clone!(
#[weak(rename_to = imp)]
self,
move |_, _| {
spawn!(async move {
imp.update_homeserver_reachable().await;
});
}
));
self.network_monitor_handler_id.replace(Some(handler_id));
}
/// The Matrix client for this session.
pub(super) fn client(&self) -> &Client {
self.client.get().expect("client should be initialized")
}
/// The list model of the sidebar.
fn sidebar_list_model(&self) -> SidebarListModel {
self.sidebar_list_model
.get_or_init(|| {
let obj = self.obj();
let item_list =
SidebarItemList::new(&RoomList::new(&obj), &VerificationList::new(&obj));
SidebarListModel::new(&item_list)
})
.clone()
}
/// The room list of this session.
pub(super) fn room_list(&self) -> RoomList {
self.sidebar_list_model().item_list().room_list()
}
/// The verification list of this session.
pub(super) fn verification_list(&self) -> VerificationList {
self.sidebar_list_model().item_list().verification_list()
}
/// The user of the session.
fn user(&self) -> User {
self.user
.get_or_init(|| {
let obj = self.obj();
User::new(&obj, obj.info().user_id.clone())
})
.clone()
}
/// Set the current state of the session.
fn set_state(&self, state: SessionState) {
let old_state = self.state.get();
if old_state == SessionState::LoggedOut || old_state == state {
// The session should be dismissed when it has been logged out, so
// we do not accept anymore state changes.
return;
}
self.state.set(state);
self.obj().notify_state();
}
/// The homeserver URL as a `GNetworkAddress`.
fn homeserver_address(&self) -> gio::NetworkAddress {
let obj = self.obj();
let homeserver = obj.homeserver();
let default_port = if homeserver.scheme() == "http" {
80
} else {
443
};
gio::NetworkAddress::parse_uri(homeserver.as_str(), default_port)
.expect("url is parsed successfully")
}
/// Check whether the homeserver is reachable.
pub(super) async fn update_homeserver_reachable(&self) {
// If there is a timeout, remove it, we will add a new one later if needed.
if let Some(source) = self.homeserver_reachable_source.take() {
source.remove();
}
let Some(_guard) = self.homeserver_reachable_lock.try_lock() else {
// There is an ongoing check.
return;
};
let monitor = gio::NetworkMonitor::default();
let is_network_available = monitor.is_network_available();
let is_homeserver_reachable = if is_network_available {
// Check if we can reach the homeserver.
let address = self.homeserver_address();
match monitor.can_reach_future(&address).await {
Ok(()) => true,
Err(error) => {
error!(
session = self.obj().session_id(),
"Homeserver is not reachable: {error}"
);
false
}
}
} else {
false
};
self.set_is_homeserver_reachable(is_homeserver_reachable);
if is_network_available && !is_homeserver_reachable {
// Check again later if the homeserver is reachable.
let source = glib::timeout_add_seconds_local_once(
10,
clone!(
#[weak(rename_to = imp)]
self,
move || {
imp.homeserver_reachable_source.take();
spawn!(async move {
imp.update_homeserver_reachable().await;
});
}
),
);
self.homeserver_reachable_source.replace(Some(source));
}
}
/// Set whether the homeserver is reachable.
fn set_is_homeserver_reachable(&self, is_reachable: bool) {
if self.is_homeserver_reachable.get() == is_reachable {
return;
}
let obj = self.obj();
self.is_homeserver_reachable.set(is_reachable);
if let Some(handle) = self.sync_handle.take() {
handle.abort();
}
if is_reachable {
info!(session = obj.session_id(), "Homeserver is reachable");
// Restart the sync loop.
self.sync();
} else {
self.set_offline(true);
}
obj.notify_is_homeserver_reachable();
}
/// Set whether this session is synchronized with the homeserver.
pub(super) fn set_offline(&self, is_offline: bool) {
if self.is_offline.get() == is_offline {
return;
}
if !is_offline {
// Restart the send queues, in case they were stopped.
let client = self.client().clone();
spawn_tokio!(async move {
client.send_queue().set_enabled(true).await;
});
}
self.is_offline.set(is_offline);
self.obj().notify_is_offline();
}
/// The settings stored in the global account data for this session.
fn global_account_data(&self) -> &GlobalAccountData {
self.global_account_data
.get_or_init(|| GlobalAccountData::new(&self.obj()))
}
/// The owned settings stored in the global account data for this
/// session.
fn global_account_data_owned(&self) -> GlobalAccountData {
self.global_account_data().clone()
}
/// The cache for remote data.
pub(super) fn remote_cache(&self) -> &RemoteCache {
self.remote_cache
.get_or_init(|| RemoteCache::new(self.obj().clone()))
}
/// Finish initialization of this session.
pub(super) async fn prepare(&self) {
spawn!(
glib::Priority::LOW,
clone!(
#[weak(rename_to = imp)]
self,
async move {
// First, load the profile from the cache, it will be quicker.
imp.init_user_profile().await;
// Then, check if the profile changed.
imp.update_user_profile().await;
}
)
);
self.global_account_data();
self.watch_session_changes();
self.update_homeserver_reachable().await;
self.room_list().load().await;
self.verification_list().init();
self.security.set_session(Some(&*self.obj()));
let client = self.client().clone();
spawn_tokio!(async move {
client
.send_queue()
.respawn_tasks_for_rooms_with_unsent_requests()
.await;
});
self.set_state(SessionState::InitialSync);
self.sync();
debug!(
session = self.obj().session_id(),
"A new session was prepared"
);
}
/// Watch the changes of the session, like being logged out or the
/// tokens being refreshed.
fn watch_session_changes(&self) {
let receiver = self.client().subscribe_to_session_changes();
let stream = BroadcastStream::new(receiver);
let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
let fut = stream.for_each(move |change| {
let obj_weak = obj_weak.clone();
async move {
let Ok(change) = change else {
return;
};
let ctx = glib::MainContext::default();
ctx.spawn(async move {
spawn!(async move {
if let Some(obj) = obj_weak.upgrade() {
match change {
SessionChange::UnknownToken { .. } => {
info!(
session = obj.session_id(),
"The access token is invalid, cleaning up the session…"
);
obj.imp().clean_up().await;
}
SessionChange::TokensRefreshed => {
obj.imp().store_tokens().await;
}
}
}
});
});
}
});
let handle = spawn_tokio!(fut).abort_handle();
self.session_changes_handle.replace(Some(handle));
}
/// Start syncing the Matrix client.
fn sync(&self) {
if self.state.get() < SessionState::InitialSync || !self.is_homeserver_reachable.get() {
return;
}
let client = self.client().clone();
let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
let handle = spawn_tokio!(async move {
// Make sure that the event cache is subscribed to sync responses to benefit
// from it.
if let Err(error) = client.event_cache().subscribe() {
error!("Could not subscribe event cache to sync responses: {error}");
}
// TODO: only create the filter once and reuse it in the future
let filter = assign!(FilterDefinition::default(), {
room: assign!(RoomFilter::with_lazy_loading(), {
include_leave: true,
}),
});
let sync_settings = SyncSettings::new()
.timeout(Duration::from_secs(30))
.ignore_timeout_on_first_sync(true)
.filter(filter.into());
let mut sync_stream = Box::pin(client.sync_stream(sync_settings).await);
while let Some(response) = sync_stream.next().await {
let obj_weak = obj_weak.clone();
let ctx = glib::MainContext::default();
let delay = ctx
.spawn(async move {
spawn!(async move {
if let Some(obj) = obj_weak.upgrade() {
obj.imp().handle_sync_response(response)
} else {
None
}
})
.await
.expect("task was not aborted")
})
.await
.expect("task was not aborted");
if let Some(delay) = delay {
sleep(delay).await;
}
}
})
.abort_handle();
self.sync_handle.replace(Some(handle));
}
/// Handle the response received via sync.
///
/// Returns the delay to wait for before making the next sync, if
/// necessary.
fn handle_sync_response(
&self,
response: Result<SyncResponse, matrix_sdk::Error>,
) -> Option<Duration> {
let obj = self.obj();
let session_id = obj.session_id();
debug!(session = session_id, "Received sync response");
match response {
Ok(response) => {
self.room_list().handle_room_updates(response.rooms);
if self.state.get() < SessionState::Ready {
self.set_state(SessionState::Ready);
self.init_notifications();
}
self.set_offline(false);
self.missed_sync_count.set(0);
None
}
Err(error) => {
let missed_sync_count = self.missed_sync_count.get();
// If there are too many failed attempts, mark the session as offline.
if missed_sync_count == MISSED_SYNC_OFFLINE_COUNT {
self.set_offline(true);
}
// Increase the count of missed syncs, if we have not reached the maximum value.
if missed_sync_count < 4 {
self.missed_sync_count.set(missed_sync_count + 1);
}
error!(session = session_id, "Could not perform sync: {error}");
// Sleep a little between attempts.
let delay = MISSED_SYNC_DELAYS[missed_sync_count];
Some(Duration::from_secs(delay))
}
}
}
/// Load the cached profile of the user of this session.
async fn init_user_profile(&self) {
let client = self.client().clone();
let handle = spawn_tokio!(async move {
client
.state_store()
.get_custom_value(SESSION_PROFILE_KEY.as_bytes())
.await
});
let profile = match handle.await.expect("task was not aborted") {
Ok(Some(bytes)) => match serde_json::from_slice::<UserProfile>(&bytes) {
Ok(profile) => profile,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not deserialize session profile: {error}"
);
return;
}
},
Ok(None) => return,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not load cached session profile: {error}"
);
return;
}
};
let user = self.user();
user.set_name(profile.displayname);
user.set_avatar_url(profile.avatar_url);
}
/// Update the profile of this session’s user.
///
/// Fetches the updated profile and updates the local data.
async fn update_user_profile(&self) {
let client = self.client().clone();
let client_clone = client.clone();
let handle =
spawn_tokio!(async move { client_clone.account().fetch_user_profile().await });
let profile = match handle
.await
.expect("task was not aborted")
.and_then(|response| {
let mut profile = UserProfile::new();
profile.displayname = response.get_static::<DisplayName>()?;
profile.avatar_url = response.get_static::<AvatarUrl>()?;
Ok(profile)
}) {
Ok(profile) => profile,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not fetch session profile: {error}"
);
return;
}
};
let user = self.user();
if Some(user.display_name()) == profile.displayname
&& user
.avatar_data()
.image()
.is_some_and(|i| i.uri() == profile.avatar_url)
{
// Nothing to update.
return;
}
// Serialize first for caching to avoid a clone.
let value = serde_json::to_vec(&profile);
// Update the profile for the UI.
user.set_name(profile.displayname);
user.set_avatar_url(profile.avatar_url);
// Update the cache.
let value = match value {
Ok(value) => value,
Err(error) => {
error!(
session = self.obj().session_id(),
"Could not serialize session profile: {error}"
);
return;
}
};
let handle = spawn_tokio!(async move {
client
.state_store()
.set_custom_value(SESSION_PROFILE_KEY.as_bytes(), value)
.await
});
if let Err(error) = handle.await.expect("task was not aborted") {
error!(
session = self.obj().session_id(),
"Could not cache session profile: {error}"
);
}
}
/// Start listening to notifications.
fn init_notifications(&self) {
let obj_weak = glib::SendWeakRef::from(self.obj().downgrade());
let client = self.client().clone();
spawn_tokio!(async move {
client
.register_notification_handler(move |notification, room, _| {
let obj_weak = obj_weak.clone();
async move {
let ctx = glib::MainContext::default();
ctx.spawn(async move {
spawn!(async move {
if let Some(obj) = obj_weak.upgrade() {
obj.notifications().show_push(notification, room).await;
}
});
});
}
})
.await;
});
}
/// Update the stored session tokens.
async fn store_tokens(&self) {
let Some(session_tokens) = self.client().session_tokens() else {
return;
};
debug!(
session = self.obj().session_id(),
"Storing updated session tokens…"
);
self.obj().info().store_tokens(session_tokens).await;
}
/// Clean up this session after it was logged out.
///
/// This should only be called if the session has been logged out
/// without calling `Session::log_out`.
pub(super) async fn clean_up(&self) {
let obj = self.obj();
self.set_state(SessionState::LoggedOut);
if let Some(handle) = self.sync_handle.take() {
handle.abort();
}
if let Some(settings) = self.settings.get() {
settings.delete();
}
obj.info().clone().delete().await;
self.notifications.clear();
debug!(
session = obj.session_id(),
"The logged out session was cleaned up"
);
}
}
}
glib::wrapper! {
/// A Matrix user session.
pub struct Session(ObjectSubclass<imp::Session>)
@extends SessionInfo;
}
impl Session {
/// Construct an existing session.
pub(crate) async fn new(
stored_session: StoredSession,
settings: SessionSettings,
) -> Result<Self, ClientSetupError> {
let tokens = stored_session
.load_tokens()
.await
.ok_or(ClientSetupError::NoSessionTokens)?;
let stored_session_clone = stored_session.clone();
let client = spawn_tokio!(async move {
let client = matrix::client_with_stored_session(stored_session_clone, tokens).await?;
// Make sure that we use the proper retention policy.
let media = client.media();
let used_media_retention_policy = media.media_retention_policy().await?;
let wanted_media_retention_policy = MediaRetentionPolicy::default();
if used_media_retention_policy != wanted_media_retention_policy {
media
.set_media_retention_policy(wanted_media_retention_policy)
.await?;
}
Ok::<_, ClientSetupError>(client)
})
.await
.expect("task was not aborted")?;
let obj = glib::Object::builder::<Self>()
.property("info", stored_session)
.property("settings", settings)
.build();
obj.imp().set_client(client);
Ok(obj)
}
/// Create a new session from the session of the given Matrix client.
pub(crate) async fn create(client: &Client) -> Result<Self, ClientSetupError> {
let stored_session = StoredSession::new(client).await?;
let settings = Application::default()
.session_list()
.settings()
.get_or_create(&stored_session.id);
Self::new(stored_session, settings).await
}
/// Finish initialization of this session.
pub(crate) async fn prepare(&self) {
self.imp().prepare().await;
}
/// The room list of this session.
pub(crate) fn room_list(&self) -> RoomList {
self.imp().room_list()
}
/// The verification list of this session.
pub(crate) fn verification_list(&self) -> VerificationList {
self.imp().verification_list()
}
/// The Matrix client.
pub(crate) fn client(&self) -> Client {
self.imp().client().clone()
}
/// The cache for remote data.
pub(crate) fn remote_cache(&self) -> &RemoteCache {
self.imp().remote_cache()
}
/// Log out of this session.
pub(crate) async fn log_out(&self) -> Result<(), String> {
debug!(
session = self.session_id(),
"The session is about to be logged out"
);
let client = self.client();
let handle = spawn_tokio!(async move { client.logout().await });
match handle.await.expect("task was not aborted") {
Ok(()) => {
self.imp().clean_up().await;
Ok(())
}
Err(error) => {
error!(
session = self.session_id(),
"Could not log the session out: {error}"
);
Err(gettext("Could not log the session out"))
}
}
}
/// Clean up this session after it was logged out.
///
/// This should only be called if the session has been logged out without
/// calling `Session::log_out`.
pub(crate) async fn clean_up(&self) {
self.imp().clean_up().await;
}
/// Connect to the signal emitted when this session is logged out.
pub(crate) fn connect_logged_out<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_state_notify(move |obj| {
if obj.state() == SessionState::LoggedOut {
f(obj);
}
})
}
/// Connect to the signal emitted when this session is ready.
pub(crate) fn connect_ready<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_state_notify(move |obj| {
if obj.state() == SessionState::Ready {
f(obj);
}
})
}
}

0
src/session/model/notifications/mod.rs → src/session/notifications/mod.rs

2
src/session/model/notifications/notifications_settings.rs → src/session/notifications/notifications_settings.rs

@ -17,7 +17,7 @@ use tokio_stream::wrappers::BroadcastStream;
use tracing::error;
use crate::{
session::model::{Room, Session, SessionState},
session::{Room, Session, SessionState},
spawn, spawn_tokio,
};

2
src/session/model/remote/cache.rs → src/session/remote/cache.rs

@ -4,7 +4,7 @@ use ruma::{OwnedRoomOrAliasId, OwnedUserId, RoomId};
use wtinylfu::WTinyLfuCache;
use super::{RemoteRoom, RemoteUser};
use crate::{session::model::Session, utils::matrix::MatrixRoomIdUri};
use crate::{session::Session, utils::matrix::MatrixRoomIdUri};
/// The data of the [`RemoteCache`].
struct RemoteCacheData {

0
src/session/model/remote/mod.rs → src/session/remote/mod.rs

2
src/session/model/remote/room.rs → src/session/remote/room.rs

@ -14,7 +14,7 @@ use tracing::{debug, warn};
use crate::{
components::{AvatarImage, AvatarUriSource, PillSource},
prelude::*,
session::model::{RoomListRoomInfo, Session},
session::{RoomListRoomInfo, Session},
spawn, spawn_tokio,
utils::{AbortableHandle, LoadingState, matrix::MatrixRoomIdUri, string::linkify},
};

2
src/session/model/remote/user.rs → src/session/remote/user.rs

@ -6,7 +6,7 @@ use matrix_sdk::ruma::OwnedUserId;
use crate::{
components::PillSource,
prelude::*,
session::model::{Session, User},
session::{Session, User},
spawn,
utils::LoadingState,
};

0
src/session/model/room/aliases.rs → src/session/room/aliases.rs

2
src/session/model/room/category.rs → src/session/room/category.rs

@ -3,7 +3,7 @@ use std::fmt;
use gtk::glib;
use matrix_sdk::RoomState;
use crate::session::model::SidebarSectionName;
use crate::session::SidebarSectionName;
/// The category of a room.
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]

0
src/session/model/room/highlight_flags.rs → src/session/room/highlight_flags.rs

0
src/session/model/room/join_rule.rs → src/session/room/join_rule.rs

2
src/session/model/room/member.rs → src/session/room/member.rs

@ -16,7 +16,7 @@ use ruma::{
use tracing::{debug, error};
use super::{MemberRole, Room};
use crate::{components::PillSource, prelude::*, session::model::User, spawn, spawn_tokio};
use crate::{components::PillSource, prelude::*, session::User, spawn, spawn_tokio};
/// The possible states of membership of a user in a room.
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy, glib::Enum)]

0
src/session/model/room/member_list.rs → src/session/room/member_list.rs

0
src/session/model/room/mod.rs → src/session/room/mod.rs

0
src/session/model/room/permissions.rs → src/session/room/permissions.rs

2
src/session/model/room/timeline/event/mod.rs → src/session/room/timeline/event/mod.rs

@ -25,7 +25,7 @@ pub(crate) use self::{
use super::{Timeline, TimelineItem, TimelineItemImpl};
use crate::{
prelude::*,
session::model::Member,
session::Member,
spawn_tokio,
utils::matrix::{MediaMessage, raw_eq, timestamp_to_date},
};

2
src/session/model/room/timeline/event/reaction_group.rs → src/session/room/timeline/event/reaction_group.rs

@ -3,7 +3,7 @@ use indexmap::IndexMap;
use matrix_sdk_ui::timeline::ReactionInfo;
use ruma::{MilliSecondsSinceUnixEpoch, OwnedUserId};
use crate::{prelude::*, session::model::User};
use crate::{prelude::*, session::User};
/// A map of user ID to reaction info.
type ReactionsMap = IndexMap<OwnedUserId, ReactionInfo>;

2
src/session/model/room/timeline/event/reaction_list.rs → src/session/room/timeline/event/reaction_list.rs

@ -2,7 +2,7 @@ use gtk::{gio, glib, prelude::*, subclass::prelude::*};
use matrix_sdk_ui::timeline::ReactionsByKeyBySender;
use super::ReactionGroup;
use crate::session::model::User;
use crate::session::User;
mod imp {
use std::cell::{OnceCell, RefCell};

0
src/session/model/room/timeline/mod.rs → src/session/room/timeline/mod.rs

0
src/session/model/room/timeline/timeline_diff_minimizer/mod.rs → src/session/room/timeline/timeline_diff_minimizer/mod.rs

0
src/session/model/room/timeline/timeline_diff_minimizer/tests.rs → src/session/room/timeline/timeline_diff_minimizer/tests.rs

2
src/session/model/room/timeline/timeline_item.rs → src/session/room/timeline/timeline_item.rs

@ -3,7 +3,7 @@ use matrix_sdk_ui::timeline::{TimelineItem as SdkTimelineItem, TimelineItemKind}
use tracing::error;
use super::{Event, Timeline, VirtualItem};
use crate::session::model::Room;
use crate::session::Room;
mod imp {
use std::cell::{OnceCell, RefCell};

0
src/session/model/room/timeline/virtual_item.rs → src/session/room/timeline/virtual_item.rs

0
src/session/model/room/typing_list.rs → src/session/room/typing_list.rs

2
src/session/model/room_list/metainfo.rs → src/session/room_list/metainfo.rs

@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
use tracing::error;
use super::RoomList;
use crate::{session::model::Room, spawn, spawn_tokio};
use crate::{session::Room, spawn, spawn_tokio};
const ROOMS_METAINFO_KEY: &str = "rooms_metainfo";

2
src/session/model/room_list/mod.rs → src/session/room_list/mod.rs

@ -24,7 +24,7 @@ pub use self::{metainfo::RoomMetainfo, room_info::RoomListRoomInfo};
use crate::{
gettext_f,
prelude::*,
session::model::{Room, Session},
session::{Room, Session},
spawn_tokio,
};

2
src/session/model/room_list/room_info.rs → src/session/room_list/room_info.rs

@ -2,7 +2,7 @@ use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
use ruma::OwnedRoomOrAliasId;
use super::RoomList;
use crate::{session::model::Room, utils::BoundObject};
use crate::{session::Room, utils::BoundObject};
mod imp {
use std::cell::{Cell, RefCell};

0
src/session/model/security.rs → src/session/security.rs

0
src/session/model/session_settings.rs → src/session/session_settings.rs

2
src/session/model/sidebar_data/icon_item.rs → src/session/sidebar_data/icon_item.rs

@ -3,7 +3,7 @@ use std::fmt;
use gettextrs::gettext;
use gtk::{glib, prelude::*, subclass::prelude::*};
use crate::session::model::RoomCategory;
use crate::session::RoomCategory;
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
#[enum_type(name = "SidebarIconItemType")]

2
src/session/model/sidebar_data/item.rs → src/session/sidebar_data/item.rs

@ -2,7 +2,7 @@ use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use super::{SidebarIconItem, SidebarSection};
use crate::{
session::model::RoomCategory,
session::RoomCategory,
utils::{BoundConstructOnlyObject, SingleItemListModel},
};

2
src/session/model/sidebar_data/item_list.rs → src/session/sidebar_data/item_list.rs

@ -5,7 +5,7 @@ use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
use super::{
SidebarIconItem, SidebarIconItemType, SidebarItem, SidebarSection, SidebarSectionName,
};
use crate::session::model::{RoomCategory, RoomList, VerificationList};
use crate::session::{RoomCategory, RoomList, VerificationList};
/// The number of top-level items in the sidebar.
const TOP_LEVEL_ITEMS_COUNT: usize = 9;

2
src/session/model/sidebar_data/list_model.rs → src/session/sidebar_data/list_model.rs

@ -2,7 +2,7 @@ use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
use super::SidebarItemList;
use crate::{
session::model::{IdentityVerification, Room},
session::{IdentityVerification, Room},
utils::{BoundObjectWeakRef, FixedSelection, expression},
};

0
src/session/model/sidebar_data/mod.rs → src/session/sidebar_data/mod.rs

2
src/session/model/sidebar_data/section/mod.rs → src/session/sidebar_data/section/mod.rs

@ -6,7 +6,7 @@ mod room_category_filter;
pub use self::name::SidebarSectionName;
use self::room_category_filter::RoomCategoryFilter;
use crate::{
session::model::{
session::{
Room, RoomCategory, RoomList, SessionSettings, VerificationList, room::HighlightFlags,
},
utils::ExpressionListModel,

2
src/session/model/sidebar_data/section/name.rs → src/session/sidebar_data/section/name.rs

@ -4,7 +4,7 @@ use gettextrs::gettext;
use gtk::glib;
use serde::{Deserialize, Serialize};
use crate::session::model::{RoomCategory, TargetRoomCategory};
use crate::session::{RoomCategory, TargetRoomCategory};
/// The possible names of the sections in the sidebar.
#[derive(

2
src/session/model/sidebar_data/section/room_category_filter.rs → src/session/sidebar_data/section/room_category_filter.rs

@ -1,6 +1,6 @@
use gtk::{glib, prelude::*, subclass::prelude::*};
use crate::session::model::RoomCategory;
use crate::session::RoomCategory;
mod imp {
use std::cell::{Cell, RefCell};

0
src/session/model/user.rs → src/session/user.rs

0
src/session/model/user_sessions_list/mod.rs → src/session/user_sessions_list/mod.rs

2
src/session/model/user_sessions_list/other_sessions_list.rs → src/session/user_sessions_list/other_sessions_list.rs

@ -2,7 +2,7 @@ use gtk::{gio, glib, prelude::*, subclass::prelude::*};
use ruma::OwnedDeviceId;
use super::{UserSession, UserSessionData};
use crate::session::model::Session;
use crate::session::Session;
mod imp {
use std::{cell::RefCell, collections::HashSet};

2
src/session/model/user_sessions_list/user_session.rs → src/session/user_sessions_list/user_session.rs

@ -13,7 +13,7 @@ use crate::{
Application,
components::{AuthDialog, AuthError},
prelude::*,
session::model::Session,
session::Session,
spawn_tokio,
system_settings::ClockFormat,
utils::matrix::timestamp_to_date,

2
src/session/model/verification/identity_verification.rs → src/session/verification/identity_verification.rs

@ -20,7 +20,7 @@ use super::{VerificationKey, load_supported_verification_methods};
use crate::{
components::QrCodeScanner,
prelude::*,
session::model::{Member, Membership, Room, User},
session::{Member, Membership, Room, User},
spawn, spawn_tokio,
utils::BoundConstructOnlyObject,
};

0
src/session/model/verification/mod.rs → src/session/verification/mod.rs

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save