Browse Source

account-settings: Reorganize

The main goal here is to move safety-related settings to a new tab so
we can add more in the future.

Since we do not want more tabs, we have to:

- Move the sessions to a subpage accessible with a button in the
"General" tab. While we are here, we reorder most of this tab.
- Move the settings that were in the "Privacy" section of the "Security"
tab to a new "Safety" tab.
- Rename the "Security" tab to "Encryption", to avoid confusion between
"Security" and "Safety".
merge-requests/1958/merge
Kévin Commaille 11 months ago
parent
commit
25c79797d1
No known key found for this signature in database
GPG Key ID: C971D9DBC9D678D
  1. 0
      data/resources/icons/scalable/status/encryption-symbolic.svg
  2. 4
      data/resources/icons/scalable/status/safety-symbolic.svg
  3. 3
      data/resources/resources.gresource.xml
  4. 23
      po/POTFILES.in
  5. 2
      src/session/view/account_settings/encryption_page/import_export_keys_subpage.rs
  6. 0
      src/session/view/account_settings/encryption_page/import_export_keys_subpage.ui
  7. 95
      src/session/view/account_settings/encryption_page/mod.rs
  8. 35
      src/session/view/account_settings/encryption_page/mod.ui
  9. 36
      src/session/view/account_settings/general_page/mod.rs
  10. 112
      src/session/view/account_settings/general_page/mod.ui
  11. 26
      src/session/view/account_settings/mod.rs
  12. 12
      src/session/view/account_settings/mod.ui
  13. 2
      src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.rs
  14. 0
      src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.ui
  15. 2
      src/session/view/account_settings/safety_page/ignored_users_subpage/mod.rs
  16. 0
      src/session/view/account_settings/safety_page/ignored_users_subpage/mod.ui
  17. 137
      src/session/view/account_settings/safety_page/mod.rs
  18. 39
      src/session/view/account_settings/safety_page/mod.ui
  19. 6
      src/session/view/account_settings/user_session/mod.rs
  20. 53
      src/session/view/account_settings/user_session/user_session_list_subpage.rs
  21. 109
      src/session/view/account_settings/user_session/user_session_list_subpage.ui
  22. 2
      src/session/view/account_settings/user_session/user_session_row.rs
  23. 0
      src/session/view/account_settings/user_session/user_session_row.ui
  24. 5
      src/session/view/account_settings/user_session/user_session_subpage.rs
  25. 6
      src/session/view/account_settings/user_session/user_session_subpage.ui
  26. 100
      src/session/view/account_settings/user_sessions_page/mod.ui
  27. 15
      src/ui-resources.gresource.xml

0
data/resources/icons/scalable/status/security-symbolic.svg → data/resources/icons/scalable/status/encryption-symbolic.svg

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

4
data/resources/icons/scalable/status/safety-symbolic.svg

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
<path d="m 8 0 c 0.554688 0 1 0.445312 1 1 v 6.5 s 0 0.5 0.5 0.5 s 0.5 -0.5 0.5 -0.5 v -4.5 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 v 8.5 c 0 0.5 0.5 0.5 0.5 0.5 l 1.792969 -1.707031 c 0.1875 -0.195313 0.445312 -0.300781 0.71875 -0.304688 c 1.082031 0.085938 1.144531 1.269531 0.695312 1.71875 l -3 3 c -0.707031 0.792969 -1.757812 1.289063 -2.707031 1.292969 h -6 c -3 0 -3 -3 -3 -3 v -8 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 v 3.5 s 0 0.5 0.5 0.5 s 0.5 -0.5 0.5 -0.5 v -6.5 c 0 -0.554688 0.445312 -1 1 -1 s 1 0.445312 1 1 v 5.5 s 0 0.5 0.5 0.5 s 0.5 -0.5 0.5 -0.5 v -6.5 c 0 -0.554688 0.445312 -1 1 -1 z m 0 0" fill="#2e3436"/>
</svg>

After

Width:  |  Height:  |  Size: 786 B

3
data/resources/resources.gresource.xml

@ -50,6 +50,7 @@
<file preprocess="xml-stripblanks">icons/scalable/status/document-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/done-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/empty-page-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/encryption-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/error-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/explore-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/home-symbolic.svg</file>
@ -58,7 +59,7 @@
<file preprocess="xml-stripblanks">icons/scalable/status/no-camera-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/notifications-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/person-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/security-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/safety-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/sync-off-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/sync-on-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/sync-partial-symbolic.svg</file>

23
po/POTFILES.in

@ -83,6 +83,10 @@ 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/view/account_settings/encryption_page/import_export_keys_subpage.rs
src/session/view/account_settings/encryption_page/import_export_keys_subpage.ui
src/session/view/account_settings/encryption_page/mod.rs
src/session/view/account_settings/encryption_page/mod.ui
src/session/view/account_settings/general_page/change_password_subpage.rs
src/session/view/account_settings/general_page/change_password_subpage.ui
src/session/view/account_settings/general_page/deactivate_account_subpage.rs
@ -94,17 +98,14 @@ src/session/view/account_settings/general_page/mod.ui
src/session/view/account_settings/mod.ui
src/session/view/account_settings/notifications_page.rs
src/session/view/account_settings/notifications_page.ui
src/session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.rs
src/session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.ui
src/session/view/account_settings/security_page/ignored_users_subpage/mod.ui
src/session/view/account_settings/security_page/import_export_keys_subpage.rs
src/session/view/account_settings/security_page/import_export_keys_subpage.ui
src/session/view/account_settings/security_page/mod.rs
src/session/view/account_settings/security_page/mod.ui
src/session/view/account_settings/user_sessions_page/mod.ui
src/session/view/account_settings/user_sessions_page/user_session_row.ui
src/session/view/account_settings/user_sessions_page/user_session_subpage.rs
src/session/view/account_settings/user_sessions_page/user_session_subpage.ui
src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.rs
src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.ui
src/session/view/account_settings/safety_page/ignored_users_subpage/mod.ui
src/session/view/account_settings/safety_page/mod.ui
src/session/view/account_settings/user_session/user_session_list_subpage.ui
src/session/view/account_settings/user_session/user_session_row.ui
src/session/view/account_settings/user_session/user_session_subpage.rs
src/session/view/account_settings/user_session/user_session_subpage.ui
src/session/view/content/explore/mod.ui
src/session/view/content/explore/public_room_row.rs
src/session/view/content/explore/servers_popover.ui

2
src/session/view/account_settings/security_page/import_export_keys_subpage.rs → src/session/view/account_settings/encryption_page/import_export_keys_subpage.rs

@ -27,7 +27,7 @@ mod imp {
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/account_settings/security_page/import_export_keys_subpage.ui"
resource = "/org/gnome/Fractal/ui/session/view/account_settings/encryption_page/import_export_keys_subpage.ui"
)]
#[properties(wrapper_type = super::ImportExportKeysSubpage)]
pub struct ImportExportKeysSubpage {

0
src/session/view/account_settings/security_page/import_export_keys_subpage.ui → src/session/view/account_settings/encryption_page/import_export_keys_subpage.ui

95
src/session/view/account_settings/security_page/mod.rs → src/session/view/account_settings/encryption_page/mod.rs

@ -2,16 +2,13 @@ use adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
use gtk::{glib, glib::clone, CompositeTemplate};
mod ignored_users_subpage;
mod import_export_keys_subpage;
pub use self::{
ignored_users_subpage::IgnoredUsersSubpage,
import_export_keys_subpage::{ImportExportKeysSubpage, ImportExportKeysSubpageMode},
pub(super) use self::import_export_keys_subpage::{
ImportExportKeysSubpage, ImportExportKeysSubpageMode,
};
use crate::{
components::ButtonCountRow,
session::model::{CryptoIdentityState, RecoveryState, Session, SessionVerificationState},
use crate::session::model::{
CryptoIdentityState, RecoveryState, Session, SessionVerificationState,
};
mod imp {
@ -23,16 +20,10 @@ mod imp {
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/account_settings/security_page/mod.ui"
resource = "/org/gnome/Fractal/ui/session/view/account_settings/encryption_page/mod.ui"
)]
#[properties(wrapper_type = super::SecurityPage)]
pub struct SecurityPage {
#[template_child]
public_read_receipts_row: TemplateChild<adw::SwitchRow>,
#[template_child]
typing_row: TemplateChild<adw::SwitchRow>,
#[template_child]
ignored_users_row: TemplateChild<ButtonCountRow>,
#[properties(wrapper_type = super::EncryptionPage)]
pub struct EncryptionPage {
#[template_child]
crypto_identity_row: TemplateChild<adw::PreferencesRow>,
#[template_child]
@ -52,15 +43,13 @@ mod imp {
/// The current session.
#[property(get, set = Self::set_session, nullable)]
session: glib::WeakRef<Session>,
ignored_users_count_handler: RefCell<Option<glib::SignalHandlerId>>,
security_handlers: RefCell<Vec<glib::SignalHandlerId>>,
bindings: RefCell<Vec<glib::Binding>>,
}
#[glib::object_subclass]
impl ObjectSubclass for SecurityPage {
const NAME: &'static str = "SecurityPage";
type Type = super::SecurityPage;
impl ObjectSubclass for EncryptionPage {
const NAME: &'static str = "EncryptionPage";
type Type = super::EncryptionPage;
type ParentType = adw::PreferencesPage;
fn class_init(klass: &mut Self::Class) {
@ -73,29 +62,21 @@ mod imp {
}
#[glib::derived_properties]
impl ObjectImpl for SecurityPage {
impl ObjectImpl for EncryptionPage {
fn dispose(&self) {
if let Some(session) = self.session.upgrade() {
if let Some(handler) = self.ignored_users_count_handler.take() {
session.ignored_users().disconnect(handler);
}
let security = session.security();
for handler in self.security_handlers.take() {
security.disconnect(handler);
}
}
for binding in self.bindings.take() {
binding.unbind();
}
}
}
impl WidgetImpl for SecurityPage {}
impl PreferencesPageImpl for SecurityPage {}
impl WidgetImpl for EncryptionPage {}
impl PreferencesPageImpl for EncryptionPage {}
impl SecurityPage {
impl EncryptionPage {
/// Set the current session.
fn set_session(&self, session: Option<&Session>) {
let prev_session = self.session.upgrade();
@ -105,55 +86,13 @@ mod imp {
}
if let Some(session) = prev_session {
if let Some(handler) = self.ignored_users_count_handler.take() {
session.ignored_users().disconnect(handler);
}
let security = session.security();
for handler in self.security_handlers.take() {
security.disconnect(handler);
}
}
for binding in self.bindings.take() {
binding.unbind();
}
if let Some(session) = session {
let ignored_users = session.ignored_users();
let ignored_users_count_handler = ignored_users.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |ignored_users, _, _, _| {
imp.ignored_users_row
.set_count(ignored_users.n_items().to_string());
}
));
self.ignored_users_row
.set_count(ignored_users.n_items().to_string());
self.ignored_users_count_handler
.replace(Some(ignored_users_count_handler));
let session_settings = session.settings();
let public_read_receipts_binding = session_settings
.bind_property(
"public-read-receipts-enabled",
&*self.public_read_receipts_row,
"active",
)
.bidirectional()
.sync_create()
.build();
let typing_binding = session_settings
.bind_property("typing-enabled", &*self.typing_row, "active")
.bidirectional()
.sync_create()
.build();
self.bindings
.replace(vec![public_read_receipts_binding, typing_binding]);
let security = session.security();
let crypto_identity_state_handler =
security.connect_crypto_identity_state_notify(clone!(
@ -343,12 +282,12 @@ mod imp {
}
glib::wrapper! {
/// Security settings page.
pub struct SecurityPage(ObjectSubclass<imp::SecurityPage>)
/// Encryption settings page.
pub struct EncryptionPage(ObjectSubclass<imp::EncryptionPage>)
@extends gtk::Widget, adw::PreferencesPage, @implements gtk::Accessible;
}
impl SecurityPage {
impl EncryptionPage {
pub fn new(session: &Session) -> Self {
glib::Object::builder().property("session", session).build()
}

35
src/session/view/account_settings/security_page/mod.ui → src/session/view/account_settings/encryption_page/mod.ui

@ -1,36 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="SecurityPage" parent="AdwPreferencesPage">
<property name="icon-name">security-symbolic</property>
<property name="title" translatable="yes">Security</property>
<property name="name">security</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Privacy</property>
<child>
<object class="AdwSwitchRow" id="public_read_receipts_row">
<property name="selectable">False</property>
<property name="title" translatable="yes">Send Read Receipts</property>
<property name="subtitle" translatable="yes">Allow other members of the rooms you participate in to track which messages you have seen</property>
</object>
</child>
<child>
<object class="AdwSwitchRow" id="typing_row">
<property name="selectable">False</property>
<property name="title" translatable="yes">Send Typing Notifications</property>
<property name="subtitle" translatable="yes">Allow other members of the rooms you participate in to see when you are typing a message</property>
</object>
</child>
<child>
<object class="ButtonCountRow" id="ignored_users_row">
<property name="title" translatable="yes">Ignored Users</property>
<property name="subtitle" translatable="yes">All messages or invitations sent by these users will be ignored. You will still see some of their activity, like when they join or leave a room.</property>
<property name="action-name">account-settings.show-subpage</property>
<property name="action-target">'ignored-users'</property>
</object>
</child>
</object>
</child>
<template class="EncryptionPage" parent="AdwPreferencesPage">
<property name="icon-name">encryption-symbolic</property>
<property name="title" translatable="yes">Encryption</property>
<property name="name">encryption</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Crypto Identity</property>

36
src/session/view/account_settings/general_page/mod.rs

@ -15,7 +15,7 @@ pub use self::{
};
use super::AccountSettings;
use crate::{
components::{ActionButton, ActionState, CopyableRow, EditableAvatar},
components::{ActionButton, ActionState, ButtonCountRow, CopyableRow, EditableAvatar},
prelude::*,
session::model::Session,
spawn, spawn_tokio, toast,
@ -42,13 +42,15 @@ mod imp {
#[template_child]
display_name_button: TemplateChild<ActionButton>,
#[template_child]
change_password_group: TemplateChild<adw::PreferencesGroup>,
user_id: TemplateChild<CopyableRow>,
#[template_child]
manage_account_group: TemplateChild<adw::PreferencesGroup>,
user_sessions_row: TemplateChild<ButtonCountRow>,
#[template_child]
homeserver: TemplateChild<CopyableRow>,
change_password_row: TemplateChild<adw::ButtonRow>,
#[template_child]
user_id: TemplateChild<CopyableRow>,
manage_account_row: TemplateChild<adw::ButtonRow>,
#[template_child]
homeserver: TemplateChild<CopyableRow>,
#[template_child]
session_id: TemplateChild<CopyableRow>,
#[template_child]
@ -65,6 +67,7 @@ mod imp {
changing_display_name: RefCell<Option<OngoingAsyncAction<String>>>,
avatar_uri_handler: RefCell<Option<glib::SignalHandlerId>>,
display_name_handler: RefCell<Option<glib::SignalHandlerId>>,
user_sessions_count_handler: RefCell<Option<glib::SignalHandlerId>>,
}
#[glib::object_subclass]
@ -111,6 +114,9 @@ mod imp {
if let Some(handler) = self.display_name_handler.take() {
user.disconnect(handler);
}
if let Some(handler) = self.user_sessions_count_handler.take() {
session.user_sessions().other_sessions().disconnect(handler);
}
}
self.session.set(session.as_ref());
@ -122,7 +128,7 @@ mod imp {
self.user_id.set_subtitle(session.user_id().as_str());
self.homeserver.set_subtitle(session.homeserver().as_str());
self.session_id.set_subtitle(session.device_id().as_str());
self.session_id.set_subtitle(session.session_id());
let user = session.user();
let avatar_uri_handler = user
@ -148,6 +154,20 @@ mod imp {
self.display_name_handler
.replace(Some(display_name_handler));
let other_user_sessions = session.user_sessions().other_sessions();
let user_sessions_count_handler = other_user_sessions.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |other_user_sessions, _, _, _| {
imp.user_sessions_row
.set_count((other_user_sessions.n_items() + 1).to_string());
}
));
self.user_sessions_row
.set_count((other_user_sessions.n_items() + 1).to_string());
self.user_sessions_count_handler
.replace(Some(user_sessions_count_handler));
spawn!(
glib::Priority::LOW,
clone!(
@ -220,9 +240,9 @@ mod imp {
.set_editable(capabilities.set_avatar_url.enabled);
self.display_name
.set_editable(capabilities.set_displayname.enabled);
self.change_password_group
self.change_password_row
.set_visible(!has_account_management_url && capabilities.change_password.enabled);
self.manage_account_group
self.manage_account_row
.set_visible(has_account_management_url);
self.deactivate_account_button
.set_visible(!uses_oauth_api || has_account_management_url);

112
src/session/view/account_settings/general_page/mod.ui

@ -50,36 +50,8 @@
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="change_password_group">
<property name="visible">False</property>
<child>
<object class="AdwButtonRow">
<property name="selectable">False</property>
<property name="title" translatable="yes">Change Password</property>
<property name="end-icon-name">go-next-symbolic</property>
<property name="action-name">account-settings.show-subpage</property>
<property name="action-target">'change-password'</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="manage_account_group">
<property name="visible">False</property>
<child>
<object class="AdwButtonRow">
<property name="selectable">False</property>
<property name="title" translatable="yes">Manage Account</property>
<property name="end-icon-name">external-link-symbolic</property>
<signal name="activated" handler="manage_account" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Advanced Information</property>
<child>
<object class="CopyableRow" id="user_id">
<property name="title" translatable="yes">Matrix User ID</property>
@ -89,19 +61,10 @@
</object>
</child>
<child>
<object class="CopyableRow" id="homeserver">
<property name="title" translatable="yes">Homeserver</property>
<property name="main-title">subtitle</property>
<property name="copy-button-tooltip-text" translatable="yes">Copy Homeserver Address</property>
<property name="toast-text" translatable="yes">Homeserver address copied to clipboard</property>
</object>
</child>
<child>
<object class="CopyableRow" id="session_id">
<property name="title" translatable="yes">Session ID</property>
<property name="main-title">subtitle</property>
<property name="copy-button-tooltip-text" translatable="yes">Copy Session ID</property>
<property name="toast-text" translatable="yes">Session ID copied to clipboard</property>
<object class="ButtonCountRow" id="user_sessions_row">
<property name="title" translatable="yes">Sessions</property>
<property name="action-name">account-settings.show-subpage</property>
<property name="action-target">'user-session-list'</property>
</object>
</child>
</object>
@ -110,28 +73,75 @@
<object class="AdwPreferencesGroup">
<property name="separate-rows">True</property>
<child>
<object class="AdwButtonRow">
<style>
<class name="destructive-action"/>
</style>
<object class="AdwButtonRow" id="change_password_row">
<property name="visible">False</property>
<property name="selectable">False</property>
<property name="title" translatable="yes">Log Out</property>
<property name="title" translatable="yes">Change Password</property>
<property name="end-icon-name">go-next-symbolic</property>
<property name="action-name">account-settings.show-subpage</property>
<property name="action-target">'log-out'</property>
<property name="action-target">'change-password'</property>
</object>
</child>
<child>
<object class="AdwButtonRow" id="deactivate_account_button">
<object class="AdwButtonRow" id="manage_account_row">
<property name="visible">False</property>
<property name="selectable">False</property>
<property name="title" translatable="yes">Manage Account</property>
<property name="end-icon-name">external-link-symbolic</property>
<signal name="activated" handler="manage_account" swapped="yes"/>
</object>
</child>
<child>
<object class="AdwButtonRow">
<style>
<class name="destructive-action"/>
</style>
<property name="selectable">False</property>
<!-- Translators: Deactivation is a permanent action that occurs on the server. -->
<property name="title" translatable="yes">Deactivate Account</property>
<property name="title" translatable="yes">Log Out</property>
<property name="end-icon-name">go-next-symbolic</property>
<property name="action-name">account-settings.show-subpage</property>
<property name="action-target">'deactivate-account'</property>
<property name="action-target">'log-out'</property>
</object>
</child>
<child>
<object class="AdwButtonRow" id="deactivate_account_button">
<style>
<class name="destructive-action"/>
</style>
<property name="selectable">False</property>
<!-- Translators: Deactivation is a permanent action that occurs on the server. -->
<property name="title" translatable="yes">Deactivate Account</property>
<property name="end-icon-name">go-next-symbolic</property>
<property name="action-name">account-settings.show-subpage</property>
<property name="action-target">'deactivate-account'</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwExpanderRow">
<property name="title" translatable="yes">Advanced Information</property>
<child>
<object class="CopyableRow" id="homeserver">
<property name="title" translatable="yes">Homeserver</property>
<property name="main-title">subtitle</property>
<property name="copy-button-tooltip-text" translatable="yes">Copy Homeserver Address</property>
<property name="toast-text" translatable="yes">Homeserver address copied to clipboard</property>
</object>
</child>
<child>
<object class="CopyableRow" id="session_id">
<!-- Translators: local refers to the ID, not the session. -->
<property name="title" translatable="yes">Local Session ID</property>
<property name="main-title">subtitle</property>
<!-- Translators: local refers to the ID, not the session. -->
<property name="copy-button-tooltip-text" translatable="yes">Copy Local Session ID</property>
<!-- Translators: local refers to the ID, not the session. -->
<property name="toast-text" translatable="yes">Local session ID copied to clipboard</property>
</object>
</child>
</object>
</child>
</object>

26
src/session/view/account_settings/mod.rs

@ -9,18 +9,18 @@ use matrix_sdk::authentication::oauth::{
};
use tracing::{error, warn};
mod encryption_page;
mod general_page;
mod notifications_page;
mod security_page;
mod user_sessions_page;
mod safety_page;
mod user_session;
use self::{
encryption_page::{EncryptionPage, ImportExportKeysSubpage, ImportExportKeysSubpageMode},
general_page::{ChangePasswordSubpage, DeactivateAccountSubpage, GeneralPage, LogOutSubpage},
notifications_page::NotificationsPage,
security_page::{
IgnoredUsersSubpage, ImportExportKeysSubpage, ImportExportKeysSubpageMode, SecurityPage,
},
user_sessions_page::{UserSessionSubpage, UserSessionsPage},
safety_page::{IgnoredUsersSubpage, SafetyPage},
user_session::{UserSessionListSubpage, UserSessionSubpage},
};
use crate::{
components::crypto::{CryptoIdentitySetupView, CryptoRecoverySetupView},
@ -34,6 +34,8 @@ use crate::{
pub(crate) enum AccountSettingsSubpage {
/// A form to change the account's password.
ChangePassword,
/// A page to view the list of account's sessions.
UserSessionList,
/// A page to confirm the logout.
LogOut,
/// A page to confirm the deactivation of the password.
@ -61,12 +63,6 @@ mod imp {
#[template(resource = "/org/gnome/Fractal/ui/session/view/account_settings/mod.ui")]
#[properties(wrapper_type = super::AccountSettings)]
pub struct AccountSettings {
#[template_child]
general_page: TemplateChild<GeneralPage>,
#[template_child]
sessions_page: TemplateChild<UserSessionsPage>,
#[template_child]
security_page: TemplateChild<SecurityPage>,
/// The current session.
#[property(get, set = Self::set_session, nullable)]
session: BoundObjectWeakRef<Session>,
@ -82,7 +78,10 @@ mod imp {
type ParentType = adw::PreferencesDialog;
fn class_init(klass: &mut Self::Class) {
GeneralPage::ensure_type();
NotificationsPage::ensure_type();
SafetyPage::ensure_type();
EncryptionPage::ensure_type();
Self::bind_template(klass);
@ -269,6 +268,9 @@ impl AccountSettings {
let page: adw::NavigationPage = match subpage {
AccountSettingsSubpage::ChangePassword => ChangePasswordSubpage::new(&session).upcast(),
AccountSettingsSubpage::UserSessionList => {
UserSessionListSubpage::new(&session).upcast()
}
AccountSettingsSubpage::LogOut => LogOutSubpage::new(&session).upcast(),
AccountSettingsSubpage::DeactivateAccount => {
DeactivateAccountSubpage::new(&session, self).upcast()

12
src/session/view/account_settings/mod.ui

@ -7,7 +7,7 @@
<class name="account-settings"/>
</style>
<child>
<object class="AccountSettingsGeneralPage" id="general_page">
<object class="AccountSettingsGeneralPage">
<property name="session" bind-source="AccountSettings" bind-property="session" bind-flags="sync-create"/>
<property name="account-settings">AccountSettings</property>
</object>
@ -24,16 +24,12 @@
</object>
</child>
<child>
<object class="UserSessionsPage" id="sessions_page">
<binding name="user-sessions">
<lookup name="user-sessions">
<lookup name="session">AccountSettings</lookup>
</lookup>
</binding>
<object class="SafetyPage">
<property name="session" bind-source="AccountSettings" bind-property="session" bind-flags="sync-create"/>
</object>
</child>
<child>
<object class="SecurityPage" id="security_page">
<object class="EncryptionPage">
<property name="session" bind-source="AccountSettings" bind-property="session" bind-flags="sync-create"/>
</object>
</child>

2
src/session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.rs → src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.rs

@ -13,7 +13,7 @@ mod imp {
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.ui"
resource = "/org/gnome/Fractal/ui/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.ui"
)]
#[properties(wrapper_type = super::IgnoredUserRow)]
pub struct IgnoredUserRow {

0
src/session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.ui → src/session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.ui

2
src/session/view/account_settings/security_page/ignored_users_subpage/mod.rs → src/session/view/account_settings/safety_page/ignored_users_subpage/mod.rs

@ -16,7 +16,7 @@ mod imp {
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/account_settings/security_page/ignored_users_subpage/mod.ui"
resource = "/org/gnome/Fractal/ui/session/view/account_settings/safety_page/ignored_users_subpage/mod.ui"
)]
#[properties(wrapper_type = super::IgnoredUsersSubpage)]
pub struct IgnoredUsersSubpage {

0
src/session/view/account_settings/security_page/ignored_users_subpage/mod.ui → src/session/view/account_settings/safety_page/ignored_users_subpage/mod.ui

137
src/session/view/account_settings/safety_page/mod.rs

@ -0,0 +1,137 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::{glib, glib::clone, CompositeTemplate};
mod ignored_users_subpage;
pub(super) use self::ignored_users_subpage::IgnoredUsersSubpage;
use crate::{components::ButtonCountRow, session::model::Session};
mod imp {
use std::cell::RefCell;
use glib::subclass::InitializingObject;
use super::*;
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/session/view/account_settings/safety_page/mod.ui")]
#[properties(wrapper_type = super::SafetyPage)]
pub struct SafetyPage {
#[template_child]
public_read_receipts_row: TemplateChild<adw::SwitchRow>,
#[template_child]
typing_row: TemplateChild<adw::SwitchRow>,
#[template_child]
ignored_users_row: TemplateChild<ButtonCountRow>,
/// The current session.
#[property(get, set = Self::set_session, nullable)]
session: glib::WeakRef<Session>,
ignored_users_count_handler: RefCell<Option<glib::SignalHandlerId>>,
bindings: RefCell<Vec<glib::Binding>>,
}
#[glib::object_subclass]
impl ObjectSubclass for SafetyPage {
const NAME: &'static str = "SafetyPage";
type Type = super::SafetyPage;
type ParentType = adw::PreferencesPage;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for SafetyPage {
fn dispose(&self) {
if let Some(session) = self.session.upgrade() {
if let Some(handler) = self.ignored_users_count_handler.take() {
session.ignored_users().disconnect(handler);
}
}
for binding in self.bindings.take() {
binding.unbind();
}
}
}
impl WidgetImpl for SafetyPage {}
impl PreferencesPageImpl for SafetyPage {}
impl SafetyPage {
/// Set the current session.
fn set_session(&self, session: Option<&Session>) {
let prev_session = self.session.upgrade();
if prev_session.as_ref() == session {
return;
}
if let Some(session) = prev_session {
if let Some(handler) = self.ignored_users_count_handler.take() {
session.ignored_users().disconnect(handler);
}
}
for binding in self.bindings.take() {
binding.unbind();
}
if let Some(session) = session {
let ignored_users = session.ignored_users();
let ignored_users_count_handler = ignored_users.connect_items_changed(clone!(
#[weak(rename_to = imp)]
self,
move |ignored_users, _, _, _| {
imp.ignored_users_row
.set_count(ignored_users.n_items().to_string());
}
));
self.ignored_users_row
.set_count(ignored_users.n_items().to_string());
self.ignored_users_count_handler
.replace(Some(ignored_users_count_handler));
let session_settings = session.settings();
let public_read_receipts_binding = session_settings
.bind_property(
"public-read-receipts-enabled",
&*self.public_read_receipts_row,
"active",
)
.bidirectional()
.sync_create()
.build();
let typing_binding = session_settings
.bind_property("typing-enabled", &*self.typing_row, "active")
.bidirectional()
.sync_create()
.build();
self.bindings
.replace(vec![public_read_receipts_binding, typing_binding]);
}
self.session.set(session);
self.obj().notify_session();
}
}
}
glib::wrapper! {
/// Safety settings page.
pub struct SafetyPage(ObjectSubclass<imp::SafetyPage>)
@extends gtk::Widget, adw::PreferencesPage, @implements gtk::Accessible;
}
impl SafetyPage {
pub fn new(session: &Session) -> Self {
glib::Object::builder().property("session", session).build()
}
}

39
src/session/view/account_settings/safety_page/mod.ui

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="SafetyPage" parent="AdwPreferencesPage">
<property name="icon-name">safety-symbolic</property>
<property name="title" translatable="yes">Safety</property>
<property name="name">safety</property>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="ButtonCountRow" id="ignored_users_row">
<property name="title" translatable="yes">Ignored Users</property>
<property name="subtitle" translatable="yes">All messages or invitations sent by these users will be ignored. You will still see some of their activity, like when they join or leave a room.</property>
<property name="action-name">account-settings.show-subpage</property>
<property name="action-target">'ignored-users'</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Privacy</property>
<child>
<object class="AdwSwitchRow" id="public_read_receipts_row">
<property name="selectable">False</property>
<property name="title" translatable="yes">Send Read Receipts</property>
<property name="subtitle" translatable="yes">Allow other members of the rooms you participate in to track which messages you have seen</property>
</object>
</child>
<child>
<object class="AdwSwitchRow" id="typing_row">
<property name="selectable">False</property>
<property name="title" translatable="yes">Send Typing Notifications</property>
<property name="subtitle" translatable="yes">Allow other members of the rooms you participate in to see when you are typing a message</property>
</object>
</child>
</object>
</child>
</template>
</interface>

6
src/session/view/account_settings/user_session/mod.rs

@ -0,0 +1,6 @@
mod user_session_list_subpage;
mod user_session_row;
mod user_session_subpage;
use self::user_session_row::*;
pub(super) use self::{user_session_list_subpage::*, user_session_subpage::*};

53
src/session/view/account_settings/user_sessions_page/mod.rs → src/session/view/account_settings/user_session/user_session_list_subpage.rs

@ -2,14 +2,9 @@ use adw::{prelude::*, subclass::prelude::*};
use gtk::{gio, glib, glib::clone, CompositeTemplate};
use tracing::error;
mod user_session_row;
mod user_session_subpage;
use self::user_session_row::UserSessionRow;
pub use self::user_session_subpage::UserSessionSubpage;
use super::AccountSettings;
use super::UserSessionRow;
use crate::{
session::model::{UserSession, UserSessionsList},
session::model::{Session, UserSession, UserSessionsList},
utils::{BoundObject, LoadingState},
};
@ -22,10 +17,10 @@ mod imp {
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/account_settings/user_sessions_page/mod.ui"
resource = "/org/gnome/Fractal/ui/session/view/account_settings/user_session/user_session_list_subpage.ui"
)]
#[properties(wrapper_type = super::UserSessionsPage)]
pub struct UserSessionsPage {
#[properties(wrapper_type = super::UserSessionListSubpage)]
pub struct UserSessionListSubpage {
#[template_child]
current_session_group: TemplateChild<adw::PreferencesGroup>,
#[template_child]
@ -44,10 +39,10 @@ mod imp {
}
#[glib::object_subclass]
impl ObjectSubclass for UserSessionsPage {
const NAME: &'static str = "UserSessionsPage";
type Type = super::UserSessionsPage;
type ParentType = adw::PreferencesPage;
impl ObjectSubclass for UserSessionListSubpage {
const NAME: &'static str = "UserSessionListSubpage";
type Type = super::UserSessionListSubpage;
type ParentType = adw::NavigationPage;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
@ -60,7 +55,7 @@ mod imp {
}
#[glib::derived_properties]
impl ObjectImpl for UserSessionsPage {
impl ObjectImpl for UserSessionListSubpage {
fn constructed(&self) {
self.parent_constructed();
@ -79,11 +74,11 @@ mod imp {
}
}
impl WidgetImpl for UserSessionsPage {}
impl PreferencesPageImpl for UserSessionsPage {}
impl WidgetImpl for UserSessionListSubpage {}
impl NavigationPageImpl for UserSessionListSubpage {}
#[gtk::template_callbacks]
impl UserSessionsPage {
impl UserSessionListSubpage {
/// Set the list of user sessions.
fn set_user_sessions(&self, user_sessions: Option<UserSessionsList>) {
let prev_user_sessions = self.user_sessions.obj();
@ -237,21 +232,17 @@ mod imp {
}
glib::wrapper! {
/// Page to present the sessions of a user.
pub struct UserSessionsPage(ObjectSubclass<imp::UserSessionsPage>)
@extends gtk::Widget, gtk::Window, adw::Window, adw::PreferencesPage,
/// Subpage to present the sessions of a user.
pub struct UserSessionListSubpage(ObjectSubclass<imp::UserSessionListSubpage>)
@extends gtk::Widget, adw::NavigationPage,
@implements gtk::Accessible;
}
impl UserSessionsPage {
/// Construct a new empty `UserSessionsPage`.
pub fn new() -> Self {
glib::Object::new()
}
}
impl Default for UserSessionsPage {
fn default() -> Self {
Self::new()
impl UserSessionListSubpage {
/// Construct a new `UserSessionListSubpage` for the given session.
pub fn new(session: &Session) -> Self {
glib::Object::builder()
.property("user-sessions", session.user_sessions())
.build()
}
}

109
src/session/view/account_settings/user_session/user_session_list_subpage.ui

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="UserSessionListSubpage" parent="AdwNavigationPage">
<property name="title" translatable="yes">Sessions</property>
<property name="child">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar"/>
</child>
<property name="content">
<object class="AdwPreferencesPage">
<child>
<object class="AdwPreferencesGroup" id="current_session_group">
<property name="title" translatable="yes">Current Session</property>
<child>
<object class="GtkListBox" id="current_session">
<accessibility>
<property name="label" translatable="yes">Current Session</property>
</accessibility>
<signal name="row-activated" handler="show_session_subpage" swapped="yes"/>
<style>
<class name="content"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="other_sessions_group">
<property name="title" translatable="yes">Other Active Sessions</property>
<child>
<object class="GtkStack" id="stack">
<property name="transition-type">crossfade</property>
<child>
<object class="GtkStackPage">
<property name="name">loading</property>
<property name="child">
<object class="AdwSpinner">
<property name="width-request">32</property>
<property name="height-request">32</property>
<property name="halign">center</property>
<property name="margin-top">24</property>
<property name="margin-bottom">24</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">list</property>
<property name="child">
<object class="GtkListBox" id="other_sessions">
<accessibility>
<property name="label" translatable="yes">Other Active Sessions</property>
</accessibility>
<signal name="row-activated" handler="show_session_subpage" swapped="yes"/>
<style>
<class name="content"/>
</style>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">error</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="margin-top">24</property>
<property name="margin-bottom">24</property>
<child>
<object class="GtkLabel">
<property name="wrap">true</property>
<property name="wrap-mode">word-char</property>
<property name="label" translatable="yes">Could not load the list of connected devices</property>
<style>
<class name="dimmed"/>
</style>
</object>
</child>
<child>
<object class="GtkButton">
<property name="can-shrink">true</property>
<property name="label" translatable="yes">Try Again</property>
<property name="halign">center</property>
<property name="action-name">account-settings.reload-user-sessions</property>
<style>
<class name="suggested-action"/>
<class name="standalone-button"/>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
</template>
</interface>

2
src/session/view/account_settings/user_sessions_page/user_session_row.rs → src/session/view/account_settings/user_session/user_session_row.rs

@ -12,7 +12,7 @@ mod imp {
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/account_settings/user_sessions_page/user_session_row.ui"
resource = "/org/gnome/Fractal/ui/session/view/account_settings/user_session/user_session_row.ui"
)]
#[properties(wrapper_type = super::UserSessionRow)]
pub struct UserSessionRow {

0
src/session/view/account_settings/user_sessions_page/user_session_row.ui → src/session/view/account_settings/user_session/user_session_row.ui

5
src/session/view/account_settings/user_sessions_page/user_session_subpage.rs → src/session/view/account_settings/user_session/user_session_subpage.rs

@ -4,12 +4,11 @@ use gtk::{glib, glib::clone, CompositeTemplate};
use matrix_sdk::authentication::oauth::{AccountManagementActionFull, AccountManagementUrlBuilder};
use tracing::error;
use super::AccountSettings;
use crate::{
components::{ActionButton, ActionState, AuthError, LoadingButtonRow},
gettext_f,
prelude::*,
session::model::UserSession,
session::{model::UserSession, view::AccountSettings},
toast,
utils::{BoundConstructOnlyObject, BoundObject, TemplateCallbacks},
};
@ -21,7 +20,7 @@ mod imp {
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(
resource = "/org/gnome/Fractal/ui/session/view/account_settings/user_sessions_page/user_session_subpage.ui"
resource = "/org/gnome/Fractal/ui/session/view/account_settings/user_session/user_session_subpage.ui"
)]
#[properties(wrapper_type = super::UserSessionSubpage)]
pub struct UserSessionSubpage {

6
src/session/view/account_settings/user_sessions_page/user_session_subpage.ui → src/session/view/account_settings/user_session/user_session_subpage.ui

@ -19,10 +19,10 @@
<object class="AdwPreferencesGroup">
<child>
<object class="CopyableRow">
<property name="title" translatable="yes">Session ID</property>
<property name="title" translatable="yes">Matrix Session ID</property>
<property name="main-title">subtitle</property>
<property name="copy-button-tooltip-text" translatable="yes">Copy Session ID</property>
<property name="toast-text" translatable="yes">Session ID copied to clipboard</property>
<property name="copy-button-tooltip-text" translatable="yes">Copy Matrix Session ID</property>
<property name="toast-text" translatable="yes">Matrix session ID copied to clipboard</property>
<binding name="subtitle">
<lookup name="device-id-string">
<lookup name="user-session">

100
src/session/view/account_settings/user_sessions_page/mod.ui

@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="UserSessionsPage" parent="AdwPreferencesPage">
<property name="icon-name">devices-symbolic</property>
<property name="title" translatable="yes">Sessions</property>
<property name="name">sessions</property>
<child>
<object class="AdwPreferencesGroup" id="current_session_group">
<property name="title" translatable="yes">Current Session</property>
<child>
<object class="GtkListBox" id="current_session">
<accessibility>
<property name="label" translatable="yes">Current Session</property>
</accessibility>
<signal name="row-activated" handler="show_session_subpage" swapped="yes"/>
<style>
<class name="content"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="other_sessions_group">
<property name="title" translatable="yes">Other Active Sessions</property>
<child>
<object class="GtkStack" id="stack">
<property name="transition-type">crossfade</property>
<child>
<object class="GtkStackPage">
<property name="name">loading</property>
<property name="child">
<object class="AdwSpinner">
<property name="width-request">32</property>
<property name="height-request">32</property>
<property name="halign">center</property>
<property name="margin-top">24</property>
<property name="margin-bottom">24</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">list</property>
<property name="child">
<object class="GtkListBox" id="other_sessions">
<accessibility>
<property name="label" translatable="yes">Other Active Sessions</property>
</accessibility>
<signal name="row-activated" handler="show_session_subpage" swapped="yes"/>
<style>
<class name="content"/>
</style>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">error</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="margin-top">24</property>
<property name="margin-bottom">24</property>
<child>
<object class="GtkLabel">
<property name="wrap">true</property>
<property name="wrap-mode">word-char</property>
<property name="label" translatable="yes">Could not load the list of connected devices</property>
<style>
<class name="dimmed"/>
</style>
</object>
</child>
<child>
<object class="GtkButton">
<property name="can-shrink">true</property>
<property name="label" translatable="yes">Try Again</property>
<property name="halign">center</property>
<property name="action-name">account-settings.reload-user-sessions</property>
<style>
<class name="suggested-action"/>
<class name="standalone-button"/>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

15
src/ui-resources.gresource.xml

@ -64,19 +64,20 @@
<file compressed="true" preprocess="xml-stripblanks">login/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">login/session_setup_view.ui</file>
<file compressed="true" preprocess="xml-stripblanks">login/sso_idp_button.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/encryption_page/import_export_keys_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/encryption_page/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/general_page/change_password_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/general_page/deactivate_account_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/general_page/log_out_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/general_page/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/notifications_page.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/security_page/ignored_users_subpage/ignored_user_row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/security_page/ignored_users_subpage/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/security_page/import_export_keys_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/security_page/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/user_sessions_page/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/user_sessions_page/user_session_row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/user_sessions_page/user_session_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/safety_page/ignored_users_subpage/ignored_user_row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/safety_page/ignored_users_subpage/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/safety_page/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/user_session/user_session_list_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/user_session/user_session_row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/user_session/user_session_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/content/explore/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/content/explore/public_room_row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/content/explore/server_row.ui</file>

Loading…
Cancel
Save