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. 55
      src/components/crypto/identity_setup_view.rs
  6. 70
      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. 53
      src/login/mod.rs
  13. 45
      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. 44
      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. 42
      src/window.rs

22
Cargo.lock generated

@ -1167,7 +1167,6 @@ dependencies = [
"serde_bytes",
"serde_json",
"sourceview5",
"strum",
"tempfile",
"thiserror 2.0.18",
"tld",
@ -4661,27 +4660,6 @@ dependencies = [
"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]]
name = "subtle"
version = "2.6.1"

1
Cargo.toml

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

6
src/account_settings/mod.rs

@ -29,7 +29,7 @@ use crate::{
};
/// 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 {
/// A form to change the account's password.
ChangePassword,
@ -298,7 +298,7 @@ impl AccountSettings {
));
let page = adw::NavigationPage::builder()
.tag(AccountSettingsSubpage::CryptoIdentitySetup.as_ref())
.tag("crypto-identity-setup")
.child(&view)
.build();
page.connect_shown(clone!(
@ -322,7 +322,7 @@ impl AccountSettings {
));
let page = adw::NavigationPage::builder()
.tag(AccountSettingsSubpage::RecoverySetup.as_ref())
.tag("crypto-recovery-setup")
.child(&view)
.build();
page.connect_shown(clone!(

8
src/account_settings/notifications_page.rs

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

55
src/components/crypto/identity_setup_view.rs

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

70
src/components/crypto/recovery_setup_view.rs

@ -14,8 +14,7 @@ use crate::{
};
/// A page of the [`CryptoRecoverySetupView`] navigation stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CryptoRecoverySetupPage {
/// Use account recovery.
Recover,
@ -29,10 +28,35 @@ enum CryptoRecoverySetupPage {
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`].
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, glib::Enum, strum::AsRefStr)]
#[enum_type(name = "CryptoRecoverySetupInitialPage")]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum CryptoRecoverySetupInitialPage {
/// Use account recovery.
#[default]
@ -43,6 +67,16 @@ pub enum CryptoRecoverySetupInitialPage {
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 {
use std::sync::LazyLock;
@ -140,11 +174,16 @@ mod imp {
impl CryptoRecoverySetupView {
/// The visible page of the view.
fn visible_page(&self) -> CryptoRecoverySetupPage {
self.navigation
CryptoRecoverySetupPage::from_tag(
&self
.navigation
.visible_page()
.and_then(|p| p.tag())
.and_then(|t| t.as_str().try_into().ok())
.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.
@ -213,7 +252,8 @@ mod imp {
/// Set the initial page of this view.
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.
@ -276,7 +316,7 @@ mod imp {
// sure of the SDK's recovery state at this point, not the Session's.
if encryption.recovery().state() == SdkRecoveryState::Incomplete {
self.navigation
.push_by_tag(CryptoRecoverySetupPage::Incomplete.as_ref());
.push_by_tag(CryptoRecoverySetupPage::Incomplete.tag());
} else {
self.emit_completed();
}
@ -424,7 +464,7 @@ mod imp {
self.update_success(key);
self.navigation
.push_by_tag(CryptoRecoverySetupPage::Success.as_ref());
.push_by_tag(CryptoRecoverySetupPage::Success.tag());
}
Err(error) => {
error!("Could not re-enable account recovery: {error}");
@ -458,7 +498,7 @@ mod imp {
self.update_success(key);
self.navigation
.push_by_tag(CryptoRecoverySetupPage::Success.as_ref());
.push_by_tag(CryptoRecoverySetupPage::Success.tag());
}
Err(error) => {
error!("Could not reset account recovery key: {error}");
@ -496,7 +536,7 @@ mod imp {
self.update_success(key);
self.navigation
.push_by_tag(CryptoRecoverySetupPage::Success.as_ref());
.push_by_tag(CryptoRecoverySetupPage::Success.tag());
}
Err(error) => {
error!("Could not enable account recovery: {error}");
@ -531,7 +571,7 @@ mod imp {
fn show_reset(&self) {
self.update_reset();
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};
/// The possible error subpages.
#[derive(Debug, Clone, Copy, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy)]
pub enum ErrorSubpage {
/// The page to present when there was an error with the secret API.
Secret,
@ -14,6 +13,16 @@ pub enum ErrorSubpage {
Session,
}
impl ErrorSubpage {
/// The name of this page.
const fn name(self) -> &'static str {
match self {
Self::Secret => "secret",
Self::Session => "name",
}
}
}
mod imp {
use glib::subclass::InitializingObject;
@ -74,14 +83,14 @@ mod imp {
self.secret_error_page.set_description(Some(message));
self.stack
.set_visible_child_name(ErrorSubpage::Secret.as_ref());
.set_visible_child_name(ErrorSubpage::Secret.name());
}
/// Display the given session error.
pub(super) fn display_session_error(&self, message: &str) {
self.session_error_page.set_description(Some(message));
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.

3
src/login/greeter.rs

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

3
src/login/homeserver_page.rs

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

3
src/login/in_browser_page.rs

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

3
src/login/method_page.rs

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

53
src/login/mod.rs

@ -40,8 +40,7 @@ use crate::{
};
/// A page of the login stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum LoginPage {
/// The greeter page.
Greeter,
@ -57,6 +56,35 @@ enum LoginPage {
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 {
use std::cell::{Cell, RefCell};
@ -175,11 +203,14 @@ mod imp {
impl Login {
/// The visible page of the view.
pub(super) fn visible_page(&self) -> LoginPage {
self.navigation
LoginPage::from_tag(
&self
.navigation
.visible_page()
.and_then(|p| p.tag())
.and_then(|s| s.as_str().try_into().ok())
.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.
@ -195,7 +226,7 @@ mod imp {
/// Get the session setup view, if any.
pub(super) fn session_setup(&self) -> Option<SessionSetupView> {
self.navigation
.find_page(LoginPage::SessionSetup.as_ref())
.find_page(LoginPage::SessionSetup.tag())
.and_downcast()
}
@ -377,7 +408,7 @@ mod imp {
) {
self.method_page
.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.
@ -387,7 +418,7 @@ mod imp {
data: LoginInBrowserData,
) {
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.
@ -414,7 +445,7 @@ mod imp {
#[weak(rename_to = imp)]
self,
move |_| {
imp.navigation.push_by_tag(LoginPage::Completed.as_ref());
imp.navigation.push_by_tag(LoginPage::Completed.tag());
}
));
self.navigation.push(&setup_view);
@ -465,7 +496,7 @@ mod imp {
self.drop_session();
// Reinitialize UI.
self.navigation.pop_to_tag(LoginPage::Greeter.as_ref());
self.navigation.pop_to_tag(LoginPage::Greeter.tag());
self.unfreeze();
}

45
src/login/session_setup_view.rs

@ -13,8 +13,7 @@ use crate::{
};
/// A page of the session setup stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SessionSetupPage {
/// The loading page.
Loading,
@ -24,6 +23,29 @@ enum SessionSetupPage {
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 {
use std::{
cell::{OnceCell, RefCell},
@ -113,10 +135,12 @@ mod imp {
impl SessionSetupView {
/// The visible page of the stack.
fn visible_stack_page(&self) -> SessionSetupPage {
self.stack
SessionSetupPage::from_name(
&self
.stack
.visible_child_name()
.and_then(|n| n.as_str().try_into().ok())
.unwrap()
.expect("SessionSetupView stack should always have a visible child name"),
)
}
/// The crypto identity view.
@ -280,10 +304,10 @@ mod imp {
self.stack.add_named(
crypto_identity_view,
Some(SessionSetupPage::CryptoIdentity.as_ref()),
Some(SessionSetupPage::CryptoIdentity.name()),
);
self.stack
.set_visible_child_name(SessionSetupPage::CryptoIdentity.as_ref());
.set_visible_child_name(SessionSetupPage::CryptoIdentity.name());
} else {
self.switch_to_recovery();
}
@ -313,9 +337,9 @@ mod imp {
let recovery_view = self.recovery_view();
self.stack
.add_named(recovery_view, Some(SessionSetupPage::Recovery.as_ref()));
.add_named(recovery_view, Some(SessionSetupPage::Recovery.name()));
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.
@ -343,6 +367,9 @@ glib::wrapper! {
}
impl SessionSetupView {
/// The tag for this page.
pub(super) const TAG: &str = "session-setup";
pub fn new(session: &Session) -> Self {
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.
#[derive(
Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, strum::Display, strum::EnumString,
)]
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy, glib::Enum)]
#[enum_type(name = "NotificationsGlobalSetting")]
#[strum(serialize_all = "kebab-case")]
pub enum NotificationsGlobalSetting {
/// Every message in every room.
#[default]
@ -37,10 +34,32 @@ pub enum NotificationsGlobalSetting {
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.
#[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")]
#[strum(serialize_all = "kebab-case")]
pub enum NotificationsRoomSetting {
/// Use the global setting.
#[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.
///
/// This is a subset of [`Membership`].
#[derive(
Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, glib::Variant, strum::AsRefStr,
)]
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, glib::Variant)]
#[enum_type(name = "MembershipListKind")]
#[strum(serialize_all = "lowercase")]
pub enum MembershipListKind {
/// The user is currently in the room.
#[default]
@ -358,8 +355,18 @@ impl MembershipListKind {
.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.
pub(crate) fn icon_name(self) -> &'static str {
pub(crate) const fn icon_name(self) -> &'static str {
match self {
Self::Join | Self::Knock => "users-symbolic",
Self::Invite => "user-add-symbolic",

14
src/session/session_settings.rs

@ -317,8 +317,8 @@ pub(super) struct MediaPreviewsSetting {
/// previews.
///
/// Legacy setting from version 0 of the stored settings.
#[derive(Debug, Clone, Default, strum::EnumString)]
#[strum(serialize_all = "lowercase")]
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "lowercase")]
pub(super) enum MediaPreviewsGlobalSetting {
/// All rooms show media previews.
All,
@ -329,16 +329,6 @@ pub(super) enum MediaPreviewsGlobalSetting {
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 {
fn from(value: MediaPreviewsGlobalSetting) -> Self {
match value {

44
src/session_view/content.rs

@ -11,8 +11,7 @@ use crate::{
};
/// A page of the content stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ContentPage {
/// The placeholder page when no content is presented.
Empty,
@ -28,6 +27,35 @@ enum ContentPage {
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 {
use std::cell::{Cell, RefCell};
@ -123,12 +151,12 @@ mod imp {
impl Content {
/// The visible page of the content.
pub(super) fn visible_page(&self) -> ContentPage {
self.stack
ContentPage::from_name(
&self
.stack
.visible_child_name()
.expect("stack should always have a visible child name")
.as_str()
.try_into()
.expect("stack child name should be convertible to a ContentPage")
.expect("Content stack should always have a visible child name"),
)
}
/// Set the visible page of the content.
@ -137,7 +165,7 @@ mod imp {
return;
}
self.stack.set_visible_child_name(page.as_ref());
self.stack.set_visible_child_name(page.name());
}
/// 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`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CreateDirectChatDialogPage {
/// The page when there is no search term.
NoSearchTerm,
@ -28,6 +27,19 @@ enum CreateDirectChatDialogPage {
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 {
use glib::subclass::InitializingObject;
@ -129,7 +141,7 @@ mod imp {
/// Set the visible page of the dialog.
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.

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.
fn set_kind(&self, kind: MembershipListKind) {
self.kind.set(kind);
self.obj().set_tag(Some(kind.as_ref()));
self.obj().set_tag(Some(kind.tag()));
self.update_empty_page();
}

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

@ -69,7 +69,7 @@ mod imp {
impl MembersPage {
/// Show the subpage for the list with the given membership.
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() {
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>>;
/// The available stack pages of the [`MessageToolbar`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::AsRefStr, strum::EnumString)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum MessageToolbarPage {
/// The composer and other buttons to send messages.
Composer,
@ -65,6 +64,29 @@ enum MessageToolbarPage {
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 {
use std::{
cell::{Cell, RefCell},
@ -195,7 +217,7 @@ mod imp {
let Some(visible_page) = self
.main_stack
.visible_child_name()
.and_then(|name| MessageToolbarPage::try_from(name.as_str()).ok())
.map(|name| MessageToolbarPage::from_name(&name))
else {
return false;
};
@ -315,7 +337,7 @@ mod imp {
/// Update the visible stack page.
fn update_visible_page(&self) {
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

42
src/window.rs

@ -23,8 +23,7 @@ use crate::{
};
/// A page of the main window stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString, strum::AsRefStr)]
#[strum(serialize_all = "kebab-case")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum WindowPage {
/// The loading page.
Loading,
@ -36,6 +35,31 @@ enum WindowPage {
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 {
use std::{cell::RefCell, rc::Rc};
@ -339,12 +363,12 @@ mod imp {
/// The visible page of the window.
pub(super) fn visible_page(&self) -> WindowPage {
self.main_stack
WindowPage::from_name(
&self
.main_stack
.visible_child_name()
.expect("stack should always have a visible child name")
.as_str()
.try_into()
.expect("stack child name should be convertible to a WindowPage")
.expect("stack should always have a visible child name"),
)
}
/// The ID of the currently visible session, if any.
@ -447,8 +471,8 @@ mod imp {
}
/// Set the visible page of the window.
fn set_visible_page(&self, name: WindowPage) {
self.main_stack.set_visible_child_name(name.as_ref());
fn set_visible_page(&self, page: WindowPage) {
self.main_stack.set_visible_child_name(page.name());
}
/// Open the error page and display the given secret error message.

Loading…
Cancel
Save