Browse Source

Remove dependency on strum

While it is somewhat convenient, let's stop being lazy and implement
more explicit methods manually.
merge-requests/2149/head
Kévin Commaille 2 weeks ago
parent
commit
99b4111cc0
No known key found for this signature in database
GPG Key ID: F26F4BE20A08255B
  1. 22
      Cargo.lock
  2. 1
      Cargo.toml
  3. 6
      src/account_settings/mod.rs
  4. 8
      src/account_settings/notifications_page.rs
  5. 57
      src/components/crypto/identity_setup_view.rs
  6. 72
      src/components/crypto/recovery_setup_view.rs
  7. 17
      src/error_page.rs
  8. 3
      src/login/greeter.rs
  9. 3
      src/login/homeserver_page.rs
  10. 3
      src/login/in_browser_page.rs
  11. 3
      src/login/method_page.rs
  12. 55
      src/login/mod.rs
  13. 47
      src/login/session_setup_view.rs
  14. 31
      src/session/notifications/notifications_settings.rs
  15. 17
      src/session/room/member_list.rs
  16. 14
      src/session/session_settings.rs
  17. 46
      src/session_view/content.rs
  18. 18
      src/session_view/create_direct_chat_dialog/mod.rs
  19. 2
      src/session_view/room_details/members_page/members_list_view/mod.rs
  20. 2
      src/session_view/room_details/members_page/mod.rs
  21. 30
      src/session_view/room_history/message_toolbar/mod.rs
  22. 44
      src/window.rs

22
Cargo.lock generated

@ -1167,7 +1167,6 @@ dependencies = [
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
"sourceview5", "sourceview5",
"strum",
"tempfile", "tempfile",
"thiserror 2.0.18", "thiserror 2.0.18",
"tld", "tld",
@ -4661,27 +4660,6 @@ dependencies = [
"quote", "quote",
] ]
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"

1
Cargo.toml

@ -48,7 +48,6 @@ secular = { version = "1", features = ["bmp", "normalization"] }
serde = "1" serde = "1"
serde_bytes = "0.11" serde_bytes = "0.11"
serde_json = "1" serde_json = "1"
strum = { version = "0.27.1", features = ["derive"] }
tempfile = "3" tempfile = "3"
thiserror = "2" thiserror = "2"
tld = "2" tld = "2"

6
src/account_settings/mod.rs

@ -29,7 +29,7 @@ use crate::{
}; };
/// A subpage of the account settings. /// A subpage of the account settings.
#[derive(Debug, Clone, Copy, Eq, PartialEq, glib::Variant, strum::AsRefStr)] #[derive(Debug, Clone, Copy, Eq, PartialEq, glib::Variant)]
pub(crate) enum AccountSettingsSubpage { pub(crate) enum AccountSettingsSubpage {
/// A form to change the account's password. /// A form to change the account's password.
ChangePassword, ChangePassword,
@ -298,7 +298,7 @@ impl AccountSettings {
)); ));
let page = adw::NavigationPage::builder() let page = adw::NavigationPage::builder()
.tag(AccountSettingsSubpage::CryptoIdentitySetup.as_ref()) .tag("crypto-identity-setup")
.child(&view) .child(&view)
.build(); .build();
page.connect_shown(clone!( page.connect_shown(clone!(
@ -322,7 +322,7 @@ impl AccountSettings {
)); ));
let page = adw::NavigationPage::builder() let page = adw::NavigationPage::builder()
.tag(AccountSettingsSubpage::RecoverySetup.as_ref()) .tag("crypto-recovery-setup")
.child(&view) .child(&view)
.build(); .build();
page.connect_shown(clone!( page.connect_shown(clone!(

8
src/account_settings/notifications_page.rs

@ -1,7 +1,6 @@
use adw::{prelude::*, subclass::prelude::*}; use adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext; use gettextrs::gettext;
use gtk::{gio, glib, glib::clone}; use gtk::{gio, glib, glib::clone};
use tracing::error;
use crate::{ use crate::{
components::{CheckLoadingRow, EntryAddRow, RemovableRow, SwitchLoadingRow}, components::{CheckLoadingRow, EntryAddRow, RemovableRow, SwitchLoadingRow},
@ -234,7 +233,7 @@ mod imp {
return String::new(); return String::new();
}; };
settings.global_setting().to_string() settings.global_setting().as_str().to_owned()
} }
/// Update the global section. /// Update the global section.
@ -254,10 +253,7 @@ mod imp {
/// Set the global setting, as a string. /// Set the global setting, as a string.
fn set_global_setting(&self, default: &str) { fn set_global_setting(&self, default: &str) {
let Ok(default) = default.parse::<NotificationsGlobalSetting>() else { let default = NotificationsGlobalSetting::from_str(default);
error!("Invalid value to set global default notifications setting: {default}");
return;
};
spawn!(clone!( spawn!(clone!(
#[weak(rename_to = imp)] #[weak(rename_to = imp)]

57
src/components/crypto/identity_setup_view.rs

@ -18,8 +18,7 @@ use crate::{
}; };
/// A page of the crypto identity setup navigation stack. /// A page of the crypto identity setup navigation stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr, glib::Variant)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum CryptoIdentitySetupPage { enum CryptoIdentitySetupPage {
/// Choose a verification method. /// Choose a verification method.
ChooseMethod, ChooseMethod,
@ -33,6 +32,33 @@ enum CryptoIdentitySetupPage {
Recovery, Recovery,
} }
impl CryptoIdentitySetupPage {
/// Get the tag for this page.
const fn tag(self) -> &'static str {
match self {
Self::ChooseMethod => "choose-method",
Self::Verify => "verify",
Self::Bootstrap => "bootstrap",
Self::Reset => "reset",
Self::Recovery => "recovery",
}
}
/// Get page matching the given tag.
///
/// Panics if the tag does not match any of the variants.
fn from_tag(tag: &str) -> Self {
match tag {
"choose-method" => Self::ChooseMethod,
"verify" => Self::Verify,
"bootstrap" => Self::Bootstrap,
"reset" => Self::Reset,
"recovery" => Self::Recovery,
_ => panic!("Unknown CryptoIdentitySetupPage: {tag}"),
}
}
}
/// The result of the crypto identity setup. /// The result of the crypto identity setup.
#[derive(Debug, Clone, Copy, PartialEq, Eq, glib::Enum)] #[derive(Debug, Clone, Copy, PartialEq, Eq, glib::Enum)]
#[enum_type(name = "CryptoIdentitySetupNextStep")] #[enum_type(name = "CryptoIdentitySetupNextStep")]
@ -151,11 +177,16 @@ mod imp {
impl CryptoIdentitySetupView { impl CryptoIdentitySetupView {
/// The visible page of the view. /// The visible page of the view.
fn visible_page(&self) -> CryptoIdentitySetupPage { fn visible_page(&self) -> CryptoIdentitySetupPage {
self.navigation CryptoIdentitySetupPage::from_tag(
.visible_page() &self
.and_then(|p| p.tag()) .navigation
.and_then(|t| t.as_str().try_into().ok()) .visible_page()
.unwrap() .expect(
"CryptoIdentitySetupView navigation view should always have a visible page",
)
.tag()
.expect("CryptoIdentitySetupView navigation page should always have a tag"),
)
} }
/// The recovery view. /// The recovery view.
@ -216,7 +247,7 @@ mod imp {
let verification_state = security.verification_state(); let verification_state = security.verification_state();
if verification_state == SessionVerificationState::Verified { if verification_state == SessionVerificationState::Verified {
self.navigation self.navigation
.replace_with_tags(&[CryptoIdentitySetupPage::Reset.as_ref()]); .replace_with_tags(&[CryptoIdentitySetupPage::Reset.tag()]);
return; return;
} }
@ -226,7 +257,7 @@ mod imp {
// If there is no crypto identity, we need to bootstrap it. // If there is no crypto identity, we need to bootstrap it.
if crypto_identity_state == CryptoIdentityState::Missing { if crypto_identity_state == CryptoIdentityState::Missing {
self.navigation self.navigation
.replace_with_tags(&[CryptoIdentitySetupPage::Bootstrap.as_ref()]); .replace_with_tags(&[CryptoIdentitySetupPage::Bootstrap.tag()]);
return; return;
} }
@ -331,10 +362,10 @@ mod imp {
.navigation .navigation
.visible_page() .visible_page()
.and_then(|p| p.tag()) .and_then(|p| p.tag())
.is_none_or(|t| t != CryptoIdentitySetupPage::Verify.as_ref()) .is_none_or(|t| t != CryptoIdentitySetupPage::Verify.tag())
{ {
self.navigation self.navigation
.push_by_tag(CryptoIdentitySetupPage::Verify.as_ref()); .push_by_tag(CryptoIdentitySetupPage::Verify.tag());
} }
self.obj().notify_verification(); self.obj().notify_verification();
@ -349,7 +380,7 @@ mod imp {
recovery_view.set_initial_page(initial_page); recovery_view.set_initial_page(initial_page);
let page = adw::NavigationPage::builder() let page = adw::NavigationPage::builder()
.tag(CryptoIdentitySetupPage::Recovery.as_ref()) .tag(CryptoIdentitySetupPage::Recovery.tag())
.child(recovery_view) .child(recovery_view)
.build(); .build();
page.connect_shown(clone!( page.connect_shown(clone!(
@ -404,7 +435,7 @@ mod imp {
self.navigation.push(&recovery_view); self.navigation.push(&recovery_view);
} else { } else {
self.navigation self.navigation
.push_by_tag(CryptoIdentitySetupPage::Bootstrap.as_ref()); .push_by_tag(CryptoIdentitySetupPage::Bootstrap.tag());
} }
} }

72
src/components/crypto/recovery_setup_view.rs

@ -14,8 +14,7 @@ use crate::{
}; };
/// A page of the [`CryptoRecoverySetupView`] navigation stack. /// A page of the [`CryptoRecoverySetupView`] navigation stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum CryptoRecoverySetupPage { enum CryptoRecoverySetupPage {
/// Use account recovery. /// Use account recovery.
Recover, Recover,
@ -29,10 +28,35 @@ enum CryptoRecoverySetupPage {
Incomplete, Incomplete,
} }
impl CryptoRecoverySetupPage {
/// Get the tag for this page.
const fn tag(self) -> &'static str {
match self {
Self::Recover => "recover",
Self::Reset => "reset",
Self::Enable => "enable",
Self::Success => "success",
Self::Incomplete => "incomplete",
}
}
/// Get the page matching the given tag.
///
/// Panics if the tag does not match any variant.
fn from_tag(tag: &str) -> Self {
match tag {
"recover" => Self::Recover,
"reset" => Self::Reset,
"enable" => Self::Enable,
"success" => Self::Success,
"incomplete" => Self::Incomplete,
_ => panic!("Unknown CryptoRecoverySetupPage: {tag}"),
}
}
}
/// The initial page of the [`CryptoRecoverySetupView`]. /// The initial page of the [`CryptoRecoverySetupView`].
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, glib::Enum, strum::AsRefStr)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[enum_type(name = "CryptoRecoverySetupInitialPage")]
#[strum(serialize_all = "kebab-case")]
pub enum CryptoRecoverySetupInitialPage { pub enum CryptoRecoverySetupInitialPage {
/// Use account recovery. /// Use account recovery.
#[default] #[default]
@ -43,6 +67,16 @@ pub enum CryptoRecoverySetupInitialPage {
Enable, Enable,
} }
impl From<CryptoRecoverySetupInitialPage> for CryptoRecoverySetupPage {
fn from(value: CryptoRecoverySetupInitialPage) -> Self {
match value {
CryptoRecoverySetupInitialPage::Recover => Self::Recover,
CryptoRecoverySetupInitialPage::Reset => Self::Reset,
CryptoRecoverySetupInitialPage::Enable => Self::Enable,
}
}
}
mod imp { mod imp {
use std::sync::LazyLock; use std::sync::LazyLock;
@ -140,11 +174,16 @@ mod imp {
impl CryptoRecoverySetupView { impl CryptoRecoverySetupView {
/// The visible page of the view. /// The visible page of the view.
fn visible_page(&self) -> CryptoRecoverySetupPage { fn visible_page(&self) -> CryptoRecoverySetupPage {
self.navigation CryptoRecoverySetupPage::from_tag(
.visible_page() &self
.and_then(|p| p.tag()) .navigation
.and_then(|t| t.as_str().try_into().ok()) .visible_page()
.unwrap() .expect(
"CryptoRecoverySetupView navigation view should always have a visible page",
)
.tag()
.expect("CryptoRecoverySetupView navigation page should always have a tag"),
)
} }
/// Set the current session. /// Set the current session.
@ -213,7 +252,8 @@ mod imp {
/// Set the initial page of this view. /// Set the initial page of this view.
pub(super) fn set_initial_page(&self, initial_page: CryptoRecoverySetupInitialPage) { pub(super) fn set_initial_page(&self, initial_page: CryptoRecoverySetupInitialPage) {
self.navigation.replace_with_tags(&[initial_page.as_ref()]); self.navigation
.replace_with_tags(&[CryptoRecoverySetupPage::from(initial_page).tag()]);
} }
/// Update the success page for the given recovery key. /// Update the success page for the given recovery key.
@ -276,7 +316,7 @@ mod imp {
// sure of the SDK's recovery state at this point, not the Session's. // sure of the SDK's recovery state at this point, not the Session's.
if encryption.recovery().state() == SdkRecoveryState::Incomplete { if encryption.recovery().state() == SdkRecoveryState::Incomplete {
self.navigation self.navigation
.push_by_tag(CryptoRecoverySetupPage::Incomplete.as_ref()); .push_by_tag(CryptoRecoverySetupPage::Incomplete.tag());
} else { } else {
self.emit_completed(); self.emit_completed();
} }
@ -424,7 +464,7 @@ mod imp {
self.update_success(key); self.update_success(key);
self.navigation self.navigation
.push_by_tag(CryptoRecoverySetupPage::Success.as_ref()); .push_by_tag(CryptoRecoverySetupPage::Success.tag());
} }
Err(error) => { Err(error) => {
error!("Could not re-enable account recovery: {error}"); error!("Could not re-enable account recovery: {error}");
@ -458,7 +498,7 @@ mod imp {
self.update_success(key); self.update_success(key);
self.navigation self.navigation
.push_by_tag(CryptoRecoverySetupPage::Success.as_ref()); .push_by_tag(CryptoRecoverySetupPage::Success.tag());
} }
Err(error) => { Err(error) => {
error!("Could not reset account recovery key: {error}"); error!("Could not reset account recovery key: {error}");
@ -496,7 +536,7 @@ mod imp {
self.update_success(key); self.update_success(key);
self.navigation self.navigation
.push_by_tag(CryptoRecoverySetupPage::Success.as_ref()); .push_by_tag(CryptoRecoverySetupPage::Success.tag());
} }
Err(error) => { Err(error) => {
error!("Could not enable account recovery: {error}"); error!("Could not enable account recovery: {error}");
@ -531,7 +571,7 @@ mod imp {
fn show_reset(&self) { fn show_reset(&self) {
self.update_reset(); self.update_reset();
self.navigation self.navigation
.push_by_tag(CryptoRecoverySetupPage::Reset.as_ref()); .push_by_tag(CryptoRecoverySetupPage::Reset.tag());
} }
} }
} }

17
src/error_page.rs

@ -5,8 +5,7 @@ use gtk::glib;
use crate::{APP_ID, toast}; use crate::{APP_ID, toast};
/// The possible error subpages. /// The possible error subpages.
#[derive(Debug, Clone, Copy, strum::AsRefStr)] #[derive(Debug, Clone, Copy)]
#[strum(serialize_all = "kebab-case")]
pub enum ErrorSubpage { pub enum ErrorSubpage {
/// The page to present when there was an error with the secret API. /// The page to present when there was an error with the secret API.
Secret, Secret,
@ -14,6 +13,16 @@ pub enum ErrorSubpage {
Session, Session,
} }
impl ErrorSubpage {
/// The name of this page.
const fn name(self) -> &'static str {
match self {
Self::Secret => "secret",
Self::Session => "name",
}
}
}
mod imp { mod imp {
use glib::subclass::InitializingObject; use glib::subclass::InitializingObject;
@ -74,14 +83,14 @@ mod imp {
self.secret_error_page.set_description(Some(message)); self.secret_error_page.set_description(Some(message));
self.stack self.stack
.set_visible_child_name(ErrorSubpage::Secret.as_ref()); .set_visible_child_name(ErrorSubpage::Secret.name());
} }
/// Display the given session error. /// Display the given session error.
pub(super) fn display_session_error(&self, message: &str) { pub(super) fn display_session_error(&self, message: &str) {
self.session_error_page.set_description(Some(message)); self.session_error_page.set_description(Some(message));
self.stack self.stack
.set_visible_child_name(ErrorSubpage::Session.as_ref()); .set_visible_child_name(ErrorSubpage::Session.name());
} }
/// Copy the secret service override command to the clipboard. /// Copy the secret service override command to the clipboard.

3
src/login/greeter.rs

@ -51,6 +51,9 @@ glib::wrapper! {
} }
impl Greeter { impl Greeter {
/// The tag for this page.
pub(super) const TAG: &str = "greeter";
pub fn new() -> Self { pub fn new() -> Self {
glib::Object::new() glib::Object::new()
} }

3
src/login/homeserver_page.rs

@ -282,6 +282,9 @@ glib::wrapper! {
} }
impl LoginHomeserverPage { impl LoginHomeserverPage {
/// The tag for this page.
pub(super) const TAG: &str = "homeserver";
pub fn new() -> Self { pub fn new() -> Self {
glib::Object::new() glib::Object::new()
} }

3
src/login/in_browser_page.rs

@ -247,6 +247,9 @@ glib::wrapper! {
} }
impl LoginInBrowserPage { impl LoginInBrowserPage {
/// The tag for this page.
pub(super) const TAG: &str = "in-browser";
pub fn new() -> Self { pub fn new() -> Self {
glib::Object::new() glib::Object::new()
} }

3
src/login/method_page.rs

@ -180,6 +180,9 @@ glib::wrapper! {
} }
impl LoginMethodPage { impl LoginMethodPage {
/// The tag for this page.
pub(super) const TAG: &str = "method";
pub fn new() -> Self { pub fn new() -> Self {
glib::Object::new() glib::Object::new()
} }

55
src/login/mod.rs

@ -40,8 +40,7 @@ use crate::{
}; };
/// A page of the login stack. /// A page of the login stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum LoginPage { enum LoginPage {
/// The greeter page. /// The greeter page.
Greeter, Greeter,
@ -57,6 +56,35 @@ enum LoginPage {
Completed, Completed,
} }
impl LoginPage {
/// Get the tag for this page.
const fn tag(self) -> &'static str {
match self {
Self::Greeter => Greeter::TAG,
Self::Homeserver => LoginHomeserverPage::TAG,
Self::Method => LoginMethodPage::TAG,
Self::InBrowser => LoginInBrowserPage::TAG,
Self::SessionSetup => SessionSetupView::TAG,
Self::Completed => "completed",
}
}
/// Get the page matching the given tag.
///
/// Panics if the tag does not match any of the variants.
fn from_tag(tag: &str) -> Self {
match tag {
Greeter::TAG => Self::Greeter,
LoginHomeserverPage::TAG => Self::Homeserver,
LoginMethodPage::TAG => Self::Method,
LoginInBrowserPage::TAG => Self::InBrowser,
SessionSetupView::TAG => Self::SessionSetup,
"completed" => Self::Completed,
_ => panic!("Unknown LoginPage: {tag}"),
}
}
}
mod imp { mod imp {
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
@ -175,11 +203,14 @@ mod imp {
impl Login { impl Login {
/// The visible page of the view. /// The visible page of the view.
pub(super) fn visible_page(&self) -> LoginPage { pub(super) fn visible_page(&self) -> LoginPage {
self.navigation LoginPage::from_tag(
.visible_page() &self
.and_then(|p| p.tag()) .navigation
.and_then(|s| s.as_str().try_into().ok()) .visible_page()
.unwrap() .expect("Login navigation view should always have a visible page")
.tag()
.expect("Login navigation page should always have a tag"),
)
} }
/// Set whether auto-discovery is enabled. /// Set whether auto-discovery is enabled.
@ -195,7 +226,7 @@ mod imp {
/// Get the session setup view, if any. /// Get the session setup view, if any.
pub(super) fn session_setup(&self) -> Option<SessionSetupView> { pub(super) fn session_setup(&self) -> Option<SessionSetupView> {
self.navigation self.navigation
.find_page(LoginPage::SessionSetup.as_ref()) .find_page(LoginPage::SessionSetup.tag())
.and_downcast() .and_downcast()
} }
@ -377,7 +408,7 @@ mod imp {
) { ) {
self.method_page self.method_page
.update(homeserver, server_name, supports_sso); .update(homeserver, server_name, supports_sso);
self.navigation.push_by_tag(LoginPage::Method.as_ref()); self.navigation.push_by_tag(LoginPage::Method.tag());
} }
/// Show the page to log in with the browser with the given data. /// Show the page to log in with the browser with the given data.
@ -387,7 +418,7 @@ mod imp {
data: LoginInBrowserData, data: LoginInBrowserData,
) { ) {
self.in_browser_page.set_up(local_server_handle, data); self.in_browser_page.set_up(local_server_handle, data);
self.navigation.push_by_tag(LoginPage::InBrowser.as_ref()); self.navigation.push_by_tag(LoginPage::InBrowser.tag());
} }
/// Create the session after a successful login. /// Create the session after a successful login.
@ -414,7 +445,7 @@ mod imp {
#[weak(rename_to = imp)] #[weak(rename_to = imp)]
self, self,
move |_| { move |_| {
imp.navigation.push_by_tag(LoginPage::Completed.as_ref()); imp.navigation.push_by_tag(LoginPage::Completed.tag());
} }
)); ));
self.navigation.push(&setup_view); self.navigation.push(&setup_view);
@ -465,7 +496,7 @@ mod imp {
self.drop_session(); self.drop_session();
// Reinitialize UI. // Reinitialize UI.
self.navigation.pop_to_tag(LoginPage::Greeter.as_ref()); self.navigation.pop_to_tag(LoginPage::Greeter.tag());
self.unfreeze(); self.unfreeze();
} }

47
src/login/session_setup_view.rs

@ -13,8 +13,7 @@ use crate::{
}; };
/// A page of the session setup stack. /// A page of the session setup stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum SessionSetupPage { enum SessionSetupPage {
/// The loading page. /// The loading page.
Loading, Loading,
@ -24,6 +23,29 @@ enum SessionSetupPage {
Recovery, Recovery,
} }
impl SessionSetupPage {
/// Get the name of this page.
const fn name(self) -> &'static str {
match self {
Self::Loading => "loading",
Self::CryptoIdentity => "crypto-identity",
Self::Recovery => "recovery",
}
}
/// Get the page matching the given name.
///
/// Panics if the name does not match any of the variants.
fn from_name(name: &str) -> Self {
match name {
"loading" => Self::Loading,
"crypto-identity" => Self::CryptoIdentity,
"recovery" => Self::Recovery,
_ => panic!("Unknown SessionSetupPage: {name}"),
}
}
}
mod imp { mod imp {
use std::{ use std::{
cell::{OnceCell, RefCell}, cell::{OnceCell, RefCell},
@ -113,10 +135,12 @@ mod imp {
impl SessionSetupView { impl SessionSetupView {
/// The visible page of the stack. /// The visible page of the stack.
fn visible_stack_page(&self) -> SessionSetupPage { fn visible_stack_page(&self) -> SessionSetupPage {
self.stack SessionSetupPage::from_name(
.visible_child_name() &self
.and_then(|n| n.as_str().try_into().ok()) .stack
.unwrap() .visible_child_name()
.expect("SessionSetupView stack should always have a visible child name"),
)
} }
/// The crypto identity view. /// The crypto identity view.
@ -280,10 +304,10 @@ mod imp {
self.stack.add_named( self.stack.add_named(
crypto_identity_view, crypto_identity_view,
Some(SessionSetupPage::CryptoIdentity.as_ref()), Some(SessionSetupPage::CryptoIdentity.name()),
); );
self.stack self.stack
.set_visible_child_name(SessionSetupPage::CryptoIdentity.as_ref()); .set_visible_child_name(SessionSetupPage::CryptoIdentity.name());
} else { } else {
self.switch_to_recovery(); self.switch_to_recovery();
} }
@ -313,9 +337,9 @@ mod imp {
let recovery_view = self.recovery_view(); let recovery_view = self.recovery_view();
self.stack self.stack
.add_named(recovery_view, Some(SessionSetupPage::Recovery.as_ref())); .add_named(recovery_view, Some(SessionSetupPage::Recovery.name()));
self.stack self.stack
.set_visible_child_name(SessionSetupPage::Recovery.as_ref()); .set_visible_child_name(SessionSetupPage::Recovery.name());
} }
/// Focus the proper widget for the current page. /// Focus the proper widget for the current page.
@ -343,6 +367,9 @@ glib::wrapper! {
} }
impl SessionSetupView { impl SessionSetupView {
/// The tag for this page.
pub(super) const TAG: &str = "session-setup";
pub fn new(session: &Session) -> Self { pub fn new(session: &Session) -> Self {
glib::Object::builder().property("session", session).build() glib::Object::builder().property("session", session).build()
} }

31
src/session/notifications/notifications_settings.rs

@ -22,11 +22,8 @@ use crate::{
}; };
/// The possible values for the global notifications setting. /// The possible values for the global notifications setting.
#[derive( #[derive(Debug, Default, Eq, PartialEq, Clone, Copy, glib::Enum)]
Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, strum::Display, strum::EnumString,
)]
#[enum_type(name = "NotificationsGlobalSetting")] #[enum_type(name = "NotificationsGlobalSetting")]
#[strum(serialize_all = "kebab-case")]
pub enum NotificationsGlobalSetting { pub enum NotificationsGlobalSetting {
/// Every message in every room. /// Every message in every room.
#[default] #[default]
@ -37,10 +34,32 @@ pub enum NotificationsGlobalSetting {
MentionsOnly, MentionsOnly,
} }
impl NotificationsGlobalSetting {
/// Get the string representation of this value.
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::All => "all",
Self::DirectAndMentions => "direct-and-mentions",
Self::MentionsOnly => "mentions-only",
}
}
/// Construct a `NotificationsGlobalSetting` from its string representation.
///
/// Panics if the string does not match a variant of this enum.
pub(crate) fn from_str(s: &str) -> Self {
match s {
"all" => Self::All,
"direct-and-mentions" => Self::DirectAndMentions,
"mentions-only" => Self::MentionsOnly,
_ => panic!("Unknown NotificationsGlobalSetting: {s}"),
}
}
}
/// The possible values for a room notifications setting. /// The possible values for a room notifications setting.
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, strum::EnumString)] #[derive(Debug, Default, Eq, PartialEq, Clone, Copy, glib::Enum)]
#[enum_type(name = "NotificationsRoomSetting")] #[enum_type(name = "NotificationsRoomSetting")]
#[strum(serialize_all = "kebab-case")]
pub enum NotificationsRoomSetting { pub enum NotificationsRoomSetting {
/// Use the global setting. /// Use the global setting.
#[default] #[default]

17
src/session/room/member_list.rs

@ -322,11 +322,8 @@ impl MemberList {
/// The kind of membership used to filter a list of room members. /// The kind of membership used to filter a list of room members.
/// ///
/// This is a subset of [`Membership`]. /// This is a subset of [`Membership`].
#[derive( #[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, glib::Variant)]
Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, glib::Variant, strum::AsRefStr,
)]
#[enum_type(name = "MembershipListKind")] #[enum_type(name = "MembershipListKind")]
#[strum(serialize_all = "lowercase")]
pub enum MembershipListKind { pub enum MembershipListKind {
/// The user is currently in the room. /// The user is currently in the room.
#[default] #[default]
@ -358,8 +355,18 @@ impl MembershipListKind {
.upcast() .upcast()
} }
/// The tag to use for pages that present this kind.
pub(crate) const fn tag(self) -> &'static str {
match self {
Self::Join => "join",
Self::Invite => "invite",
Self::Ban => "ban",
Self::Knock => "knock",
}
}
/// The name of the icon that represents this kind. /// The name of the icon that represents this kind.
pub(crate) fn icon_name(self) -> &'static str { pub(crate) const fn icon_name(self) -> &'static str {
match self { match self {
Self::Join | Self::Knock => "users-symbolic", Self::Join | Self::Knock => "users-symbolic",
Self::Invite => "user-add-symbolic", Self::Invite => "user-add-symbolic",

14
src/session/session_settings.rs

@ -317,8 +317,8 @@ pub(super) struct MediaPreviewsSetting {
/// previews. /// previews.
/// ///
/// Legacy setting from version 0 of the stored settings. /// Legacy setting from version 0 of the stored settings.
#[derive(Debug, Clone, Default, strum::EnumString)] #[derive(Debug, Clone, Default, Deserialize)]
#[strum(serialize_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub(super) enum MediaPreviewsGlobalSetting { pub(super) enum MediaPreviewsGlobalSetting {
/// All rooms show media previews. /// All rooms show media previews.
All, All,
@ -329,16 +329,6 @@ pub(super) enum MediaPreviewsGlobalSetting {
None, None,
} }
impl<'de> Deserialize<'de> for MediaPreviewsGlobalSetting {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let cow = ruma::serde::deserialize_cow_str(deserializer)?;
cow.parse().map_err(serde::de::Error::custom)
}
}
impl From<MediaPreviewsGlobalSetting> for MediaPreviews { impl From<MediaPreviewsGlobalSetting> for MediaPreviews {
fn from(value: MediaPreviewsGlobalSetting) -> Self { fn from(value: MediaPreviewsGlobalSetting) -> Self {
match value { match value {

46
src/session_view/content.rs

@ -11,8 +11,7 @@ use crate::{
}; };
/// A page of the content stack. /// A page of the content stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum ContentPage { enum ContentPage {
/// The placeholder page when no content is presented. /// The placeholder page when no content is presented.
Empty, Empty,
@ -28,6 +27,35 @@ enum ContentPage {
Verification, Verification,
} }
impl ContentPage {
/// The name of this page.
const fn name(self) -> &'static str {
match self {
Self::Empty => "empty",
Self::RoomHistory => "room-history",
Self::InviteRequest => "invite-request",
Self::Invite => "invite",
Self::Explore => "explore",
Self::Verification => "verification",
}
}
/// Get the page matching the given name.
///
/// Panics if the name does not match any of the variants.
fn from_name(name: &str) -> Self {
match name {
"empty" => Self::Empty,
"room-history" => Self::RoomHistory,
"invite-request" => Self::InviteRequest,
"invite" => Self::Invite,
"explore" => Self::Explore,
"verification" => Self::Verification,
_ => panic!("Unknown ContentPage: {name}"),
}
}
}
mod imp { mod imp {
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
@ -123,12 +151,12 @@ mod imp {
impl Content { impl Content {
/// The visible page of the content. /// The visible page of the content.
pub(super) fn visible_page(&self) -> ContentPage { pub(super) fn visible_page(&self) -> ContentPage {
self.stack ContentPage::from_name(
.visible_child_name() &self
.expect("stack should always have a visible child name") .stack
.as_str() .visible_child_name()
.try_into() .expect("Content stack should always have a visible child name"),
.expect("stack child name should be convertible to a ContentPage") )
} }
/// Set the visible page of the content. /// Set the visible page of the content.
@ -137,7 +165,7 @@ mod imp {
return; return;
} }
self.stack.set_visible_child_name(page.as_ref()); self.stack.set_visible_child_name(page.name());
} }
/// Set the current session. /// Set the current session.

18
src/session_view/create_direct_chat_dialog/mod.rs

@ -13,8 +13,7 @@ use crate::{
}; };
/// A page of the [`CreateDirectChatDialog`]. /// A page of the [`CreateDirectChatDialog`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::AsRefStr)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum CreateDirectChatDialogPage { enum CreateDirectChatDialogPage {
/// The page when there is no search term. /// The page when there is no search term.
NoSearchTerm, NoSearchTerm,
@ -28,6 +27,19 @@ enum CreateDirectChatDialogPage {
Error, Error,
} }
impl CreateDirectChatDialogPage {
/// Get the name of this page.
const fn name(self) -> &'static str {
match self {
Self::NoSearchTerm => "no-search-term",
Self::Loading => "loading",
Self::Results => "results",
Self::Empty => "empty",
Self::Error => "error",
}
}
}
mod imp { mod imp {
use glib::subclass::InitializingObject; use glib::subclass::InitializingObject;
@ -129,7 +141,7 @@ mod imp {
/// Set the visible page of the dialog. /// Set the visible page of the dialog.
fn set_visible_page(&self, page: CreateDirectChatDialogPage) { fn set_visible_page(&self, page: CreateDirectChatDialogPage) {
self.stack.set_visible_child_name(page.as_ref()); self.stack.set_visible_child_name(page.name());
} }
/// Update the view for the current state of the user list. /// Update the view for the current state of the user list.

2
src/session_view/room_details/members_page/members_list_view/mod.rs

@ -177,7 +177,7 @@ mod imp {
/// Set the kind of the membership list. /// Set the kind of the membership list.
fn set_kind(&self, kind: MembershipListKind) { fn set_kind(&self, kind: MembershipListKind) {
self.kind.set(kind); self.kind.set(kind);
self.obj().set_tag(Some(kind.as_ref())); self.obj().set_tag(Some(kind.tag()));
self.update_empty_page(); self.update_empty_page();
} }

2
src/session_view/room_details/members_page/mod.rs

@ -69,7 +69,7 @@ mod imp {
impl MembersPage { impl MembersPage {
/// Show the subpage for the list with the given membership. /// Show the subpage for the list with the given membership.
pub(super) fn show_membership_list(&self, kind: MembershipListKind) { pub(super) fn show_membership_list(&self, kind: MembershipListKind) {
let tag = kind.as_ref(); let tag = kind.tag();
if self.navigation_view.find_page(tag).is_some() { if self.navigation_view.find_page(tag).is_some() {
self.navigation_view.push_by_tag(tag); self.navigation_view.push_by_tag(tag);

30
src/session_view/room_history/message_toolbar/mod.rs

@ -54,8 +54,7 @@ use crate::{
type ComposerStatesMap = HashMap<Option<String>, HashMap<Option<OwnedRoomId>, ComposerState>>; type ComposerStatesMap = HashMap<Option<String>, HashMap<Option<OwnedRoomId>, ComposerState>>;
/// The available stack pages of the [`MessageToolbar`]. /// The available stack pages of the [`MessageToolbar`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::AsRefStr, strum::EnumString)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum MessageToolbarPage { enum MessageToolbarPage {
/// The composer and other buttons to send messages. /// The composer and other buttons to send messages.
Composer, Composer,
@ -65,6 +64,29 @@ enum MessageToolbarPage {
Tombstoned, Tombstoned,
} }
impl MessageToolbarPage {
/// The name of this page.
const fn name(self) -> &'static str {
match self {
Self::Composer => "composer",
Self::NoPermission => "no-permission",
Self::Tombstoned => "tombstoned",
}
}
/// Get the page matching the given name.
///
/// Panics if the name does not match any variant.
fn from_name(name: &str) -> Self {
match name {
"composer" => Self::Composer,
"no-permission" => Self::NoPermission,
"tombstoned" => Self::Tombstoned,
_ => panic!("Unknown MessageToolbarPage: {name}"),
}
}
}
mod imp { mod imp {
use std::{ use std::{
cell::{Cell, RefCell}, cell::{Cell, RefCell},
@ -195,7 +217,7 @@ mod imp {
let Some(visible_page) = self let Some(visible_page) = self
.main_stack .main_stack
.visible_child_name() .visible_child_name()
.and_then(|name| MessageToolbarPage::try_from(name.as_str()).ok()) .map(|name| MessageToolbarPage::from_name(&name))
else { else {
return false; return false;
}; };
@ -315,7 +337,7 @@ mod imp {
/// Update the visible stack page. /// Update the visible stack page.
fn update_visible_page(&self) { fn update_visible_page(&self) {
self.main_stack self.main_stack
.set_visible_child_name(self.visible_page().as_ref()); .set_visible_child_name(self.visible_page().name());
} }
/// Update the identifier to watch for the successor of the current /// Update the identifier to watch for the successor of the current

44
src/window.rs

@ -23,8 +23,7 @@ use crate::{
}; };
/// A page of the main window stack. /// A page of the main window stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[strum(serialize_all = "kebab-case")]
enum WindowPage { enum WindowPage {
/// The loading page. /// The loading page.
Loading, Loading,
@ -36,6 +35,31 @@ enum WindowPage {
Error, Error,
} }
impl WindowPage {
/// Get the name of this page.
const fn name(self) -> &'static str {
match self {
Self::Loading => "loading",
Self::Login => "login",
Self::Session => "session",
Self::Error => "error",
}
}
/// Get the page matching the given name.
///
/// Panics if the name does not match any of the variants.
fn from_name(name: &str) -> Self {
match name {
"loading" => Self::Loading,
"login" => Self::Login,
"session" => Self::Session,
"error" => Self::Error,
_ => panic!("Unknown WindowPage: {name}"),
}
}
}
mod imp { mod imp {
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
@ -339,12 +363,12 @@ mod imp {
/// The visible page of the window. /// The visible page of the window.
pub(super) fn visible_page(&self) -> WindowPage { pub(super) fn visible_page(&self) -> WindowPage {
self.main_stack WindowPage::from_name(
.visible_child_name() &self
.expect("stack should always have a visible child name") .main_stack
.as_str() .visible_child_name()
.try_into() .expect("stack should always have a visible child name"),
.expect("stack child name should be convertible to a WindowPage") )
} }
/// The ID of the currently visible session, if any. /// The ID of the currently visible session, if any.
@ -447,8 +471,8 @@ mod imp {
} }
/// Set the visible page of the window. /// Set the visible page of the window.
fn set_visible_page(&self, name: WindowPage) { fn set_visible_page(&self, page: WindowPage) {
self.main_stack.set_visible_child_name(name.as_ref()); self.main_stack.set_visible_child_name(page.name());
} }
/// Open the error page and display the given secret error message. /// Open the error page and display the given secret error message.

Loading…
Cancel
Save