diff options
author | fiaxh <git@lightrise.org> | 2024-09-16 22:47:49 +0200 |
---|---|---|
committer | fiaxh <git@lightrise.org> | 2024-09-21 17:06:20 +0100 |
commit | f8c004630f56914438fa1b114530f639748e41c1 (patch) | |
tree | 4a9d2ae1de436c00e9d1ac75e05426291be52c4b | |
parent | b09556f03349b51d95c14d1046add355a4754e01 (diff) | |
download | dino-f8c004630f56914438fa1b114530f639748e41c1.tar.gz dino-f8c004630f56914438fa1b114530f639748e41c1.zip |
Add change password functionality
Co-authored-by: Stanislav Malishevskiy <stanislav.malishevskiy@gmail.com>
-rw-r--r-- | libdino/src/service/registration.vala | 6 | ||||
-rw-r--r-- | main/CMakeLists.txt | 2 | ||||
-rw-r--r-- | main/data/gresource.xml | 1 | ||||
-rw-r--r-- | main/data/preferences_window/account_preferences_subpage.ui | 22 | ||||
-rw-r--r-- | main/data/preferences_window/change_password_dialog.ui | 85 | ||||
-rw-r--r-- | main/data/preferences_window/preferences_window.ui | 4 | ||||
-rw-r--r-- | main/meson.build | 1 | ||||
-rw-r--r-- | main/src/view_model/preferences_window.vala | 20 | ||||
-rw-r--r-- | main/src/windows/preferences_window/account_preferences_subpage.vala | 16 | ||||
-rw-r--r-- | main/src/windows/preferences_window/change_password_dialog.vala | 66 | ||||
-rw-r--r-- | main/src/windows/preferences_window/preferences_window.vala | 2 | ||||
-rw-r--r-- | xmpp-vala/src/module/xep/0077_in_band_registration.vala | 18 |
12 files changed, 235 insertions, 8 deletions
diff --git a/libdino/src/service/registration.vala b/libdino/src/service/registration.vala index 4255f322..8a8e75b1 100644 --- a/libdino/src/service/registration.vala +++ b/libdino/src/service/registration.vala @@ -71,6 +71,12 @@ public class Register : StreamInteractionModule, Object{ return ret; } + public async string? change_password(Account account, string new_pw){ + XmppStream stream = stream_interactor.get_stream(account); + if (stream == null) return null; + return (yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).change_password(stream, account.full_jid, new_pw)).condition; + } + public class ServerAvailabilityReturn { public bool available { get; set; } public TlsCertificateFlags? error_flags { get; set; } diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 13111de8..fe7528cf 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -62,6 +62,7 @@ set(RESOURCE_LIST preferences_window/account_preferences_subpage.ui preferences_window/add_account_dialog.ui + preferences_window/change_password_dialog.ui preferences_window/general_preferences_page.ui preferences_window/preferences_window.ui @@ -241,6 +242,7 @@ SOURCES src/windows/preferences_window/account_preferences_subpage.vala src/windows/preferences_window/accounts_preferences_page.vala src/windows/preferences_window/add_account_dialog.vala + src/windows/preferences_window/change_password_dialog.vala src/windows/preferences_window/encryption_preferences_page.vala src/windows/preferences_window/general_preferences_page.vala src/windows/preferences_window/preferences_window.vala diff --git a/main/data/gresource.xml b/main/data/gresource.xml index 304fa7a4..edceea3e 100644 --- a/main/data/gresource.xml +++ b/main/data/gresource.xml @@ -57,6 +57,7 @@ <file>preferences_window/account_preferences_subpage.ui</file> <file>preferences_window/add_account_dialog.ui</file> + <file>preferences_window/change_password_dialog.ui</file> <file>preferences_window/general_preferences_page.ui</file> <file>preferences_window/preferences_window.ui</file> diff --git a/main/data/preferences_window/account_preferences_subpage.ui b/main/data/preferences_window/account_preferences_subpage.ui index fa273053..a4e0b300 100644 --- a/main/data/preferences_window/account_preferences_subpage.ui +++ b/main/data/preferences_window/account_preferences_subpage.ui @@ -80,6 +80,28 @@ </object> </child> <child> + <object class="AdwActionRow" id="password_change"> + <property name="title" translatable="yes">Password</property> + <child type="suffix"> + <object class="GtkBox"> + <property name="opacity">0.7</property> + <property name="spacing">6</property> + <property name="orientation">horizontal</property> + <child> + <object class="GtkLabel"> + <property name="label">•••••</property> + </object> + </child> + <child> + <object class="GtkImage"> + <property name="icon-name">go-next-symbolic</property> + </object> + </child> + </object> + </child> + </object> + </child> + <child> <object class="AdwActionRow" id="connection_status"> <property name="title" translatable="yes">Connection status</property> <style> diff --git a/main/data/preferences_window/change_password_dialog.ui b/main/data/preferences_window/change_password_dialog.ui new file mode 100644 index 00000000..350d2a2d --- /dev/null +++ b/main/data/preferences_window/change_password_dialog.ui @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <requires lib="gtk" version="4.0"/> + <template class="DinoUiChangePasswordDialog"> + <property name="title" translatable="1">Change password</property> + <property name="default-width">430</property> + <property name="default-height">270</property> + <property name="modal">True</property> + <child type="titlebar"> + <object class="GtkHeaderBar"> + <property name="show-title-buttons">False</property> + <child> + <object class="GtkButton" id="cancel_button"> + <property name="label" translatable="yes">Cancel</property> + </object> + </child> + <child type="end"> + <object class="GtkButton" id="change_password_button"> + <property name="sensitive">0</property> + <style> + <class name="suggested-action"/> + </style> + <child> + <object class="GtkStack" id="change_password_stack"> + <child> + <object class="GtkStackPage"> + <property name="name">label</property> + <property name="child"> + <object class="GtkLabel" > + <property name="label" translatable="1">Change</property> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">spinner</property> + <property name="child"> + <object class="GtkSpinner"> + <property name="spinning">True</property> + </object> + </property> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="AdwPreferencesPage"> + <child> + <object class="AdwPreferencesGroup"> + <child> + <object class="AdwPasswordEntryRow" id="current_password_entry"> + <property name="title" translatable="yes">Current password</property> + </object> + </child> + <child> + <object class="AdwPasswordEntryRow" id="new_password_entry"> + <property name="title" translatable="yes">New password</property> + </object> + </child> + <child> + <object class="AdwPasswordEntryRow" id="confirm_new_password_entry"> + <property name="title" translatable="yes">Confirm password</property> + </object> + </child> + <child> + <object class="GtkLabel" id="change_password_error_label"> + <property name="halign">center</property> + <property name="hexpand">True</property> + <property name="margin-top">7</property> + <attributes> + <attribute name="scale" value="0.9"></attribute> + </attributes> + </object> + </child> + </object> + </child> + </object> + </child> + </template> +</interface>
\ No newline at end of file diff --git a/main/data/preferences_window/preferences_window.ui b/main/data/preferences_window/preferences_window.ui index d262dd76..3cf08f77 100644 --- a/main/data/preferences_window/preferences_window.ui +++ b/main/data/preferences_window/preferences_window.ui @@ -3,8 +3,8 @@ <requires lib="gtk" version="4.0"/> <object class="DinoUiViewModelPreferencesWindow" id="model" /> <template class="DinoUiPreferencesWindow"> - <property name="default-width">500</property> - <property name="default-height">600</property> + <property name="default-width">700</property> + <property name="default-height">550</property> <property name="modal">True</property> <child> <object class="DinoUiPreferencesWindowAccounts" id="accounts_page"> diff --git a/main/meson.build b/main/meson.build index a2902dbd..30a28032 100644 --- a/main/meson.build +++ b/main/meson.build @@ -93,6 +93,7 @@ sources = files( 'src/windows/preferences_window/account_preferences_subpage.vala', 'src/windows/preferences_window/accounts_preferences_page.vala', 'src/windows/preferences_window/add_account_dialog.vala', + 'src/windows/preferences_window/change_password_dialog.vala', 'src/windows/preferences_window/encryption_preferences_page.vala', 'src/windows/preferences_window/general_preferences_page.vala', 'src/windows/preferences_window/preferences_window.vala', diff --git a/main/src/view_model/preferences_window.vala b/main/src/view_model/preferences_window.vala index 9cc5a80e..7d967605 100644 --- a/main/src/view_model/preferences_window.vala +++ b/main/src/view_model/preferences_window.vala @@ -98,6 +98,13 @@ public class Dino.Ui.ViewModel.PreferencesWindow : Object { update_data(); } + public ChangePasswordDialog get_change_password_dialog_model() { + return new ChangePasswordDialog() { + account = selected_account.account, + stream_interactor = stream_interactor + }; + } + private void bind_general_page() { var settings = Dino.Application.get_default().settings; settings.bind_property("send-typing", general_page, "send-typing", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); @@ -107,3 +114,16 @@ public class Dino.Ui.ViewModel.PreferencesWindow : Object { } } +public class Dino.Ui.ViewModel.ChangePasswordDialog : Object { + public Entities.Account account { get; set; } + public StreamInteractor stream_interactor { get; set; } + + public async string? change_password(string new_password) { + var res = yield stream_interactor.get_module(Register.IDENTITY).change_password(account, new_password); + if (res == null) { + account.password = new_password; + } + return res; + } +} + diff --git a/main/src/windows/preferences_window/account_preferences_subpage.vala b/main/src/windows/preferences_window/account_preferences_subpage.vala index d6ddb2b4..1a7d5696 100644 --- a/main/src/windows/preferences_window/account_preferences_subpage.vala +++ b/main/src/windows/preferences_window/account_preferences_subpage.vala @@ -13,6 +13,7 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box { [GtkChild] public unowned AvatarPicture avatar; [GtkChild] public unowned Adw.ActionRow xmpp_address; [GtkChild] public unowned Adw.EntryRow local_alias; + [GtkChild] public unowned Adw.ActionRow password_change; [GtkChild] public unowned Adw.ActionRow connection_status; [GtkChild] public unowned Button enter_password_button; [GtkChild] public unowned Box avatar_menu_box; @@ -50,6 +51,12 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box { remove_account_button.clicked.connect(() => { show_remove_account_dialog(); }); + password_change.activatable_widget = new Label(""); + password_change.activated.connect(() => { + var dialog = new ChangePasswordDialog(model.get_change_password_dialog_model()); + dialog.set_transient_for((Gtk.Window)this.get_root()); + dialog.present(); + }); enter_password_button.clicked.connect(() => { var dialog = new Adw.MessageDialog((Window)this.get_root(), "Enter password for %s".printf(account.bare_jid.to_string()), null); var password = new PasswordEntry() { show_peek_icon=true }; @@ -76,10 +83,10 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box { avatar.model = model.selected_account.avatar_model; xmpp_address.subtitle = account.bare_jid.to_string(); - if (alias_entry_changed != 0) local_alias_entry.disconnect(alias_entry_changed); - local_alias_entry.text = account.alias ?? ""; - alias_entry_changed = local_alias_entry.changed.connect(() => { - account.alias = local_alias_entry.text; + if (alias_entry_changed != 0) local_alias.disconnect(alias_entry_changed); + local_alias.text = account.alias ?? ""; + alias_entry_changed = local_alias.changed.connect(() => { + account.alias = local_alias.text; }); bindings += account.bind_property("enabled", disable_account_button, "label", BindingFlags.SYNC_CREATE, (binding, from, ref to) => { @@ -88,6 +95,7 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box { return true; }); bindings += account.bind_property("enabled", avatar_menu_box, "visible", BindingFlags.SYNC_CREATE); + bindings += account.bind_property("enabled", password_change, "visible", BindingFlags.SYNC_CREATE); bindings += account.bind_property("enabled", connection_status, "visible", BindingFlags.SYNC_CREATE); bindings += model.selected_account.bind_property("connection-state", connection_status, "subtitle", BindingFlags.SYNC_CREATE, (binding, from, ref to) => { to = get_status_label(); diff --git a/main/src/windows/preferences_window/change_password_dialog.vala b/main/src/windows/preferences_window/change_password_dialog.vala new file mode 100644 index 00000000..f0c7dcf9 --- /dev/null +++ b/main/src/windows/preferences_window/change_password_dialog.vala @@ -0,0 +1,66 @@ +using Gee; +using Gtk; + +using Dino.Entities; +using Xmpp; + +namespace Dino.Ui{ + + [GtkTemplate (ui = "/im/dino/Dino/preferences_window/change_password_dialog.ui")] + public class ChangePasswordDialog : Gtk.Dialog { + + [GtkChild] private unowned Button change_password_button; + [GtkChild] private unowned Stack change_password_stack; + [GtkChild] private unowned Button cancel_button; + [GtkChild] private unowned Adw.PasswordEntryRow current_password_entry; + [GtkChild] private unowned Adw.PasswordEntryRow new_password_entry; + [GtkChild] private unowned Adw.PasswordEntryRow confirm_new_password_entry; + [GtkChild] private unowned Label change_password_error_label; + + private ViewModel.ChangePasswordDialog model; + + public ChangePasswordDialog(ViewModel.ChangePasswordDialog model) { + Object(use_header_bar : 1); + this.model = model; + + Util.force_error_color(change_password_error_label); + cancel_button.clicked.connect(() => { close(); }); + current_password_entry.changed.connect(is_form_filled); + new_password_entry.changed.connect(is_form_filled); + confirm_new_password_entry.changed.connect(is_form_filled); + change_password_button.clicked.connect(on_change_password_button_clicked); + } + + private void is_form_filled(){ + if (current_password_entry.get_text().length > 0 + && new_password_entry.get_text().length > 0 + && confirm_new_password_entry.get_text().length > 0 + && new_password_entry.get_text() == confirm_new_password_entry.get_text()){ + change_password_button.sensitive = true; + } else { + change_password_button.sensitive = false; + } + } + + private async void on_change_password_button_clicked(){ + string? pw_input = current_password_entry.get_text(); + string? new_pw_input = new_password_entry.get_text(); + + if (pw_input != null && model.account.password == pw_input){ + change_password_button.sensitive = false; + change_password_stack.visible_child_name = "spinner"; + string? ret = yield model.change_password(new_pw_input); + change_password_button.sensitive = true; + change_password_stack.visible_child_name = "label"; + if (ret == null) { + close(); + } + + change_password_error_label.label = "Error: %s".printf(ret); + + } else { + change_password_error_label.label = "Wrong current password"; + } + } + } +}
\ No newline at end of file diff --git a/main/src/windows/preferences_window/preferences_window.vala b/main/src/windows/preferences_window/preferences_window.vala index 1a75aa0a..662a6924 100644 --- a/main/src/windows/preferences_window/preferences_window.vala +++ b/main/src/windows/preferences_window/preferences_window.vala @@ -15,8 +15,6 @@ public class Dino.Ui.PreferencesWindow : Adw.PreferencesWindow { [GtkChild] public unowned ViewModel.PreferencesWindow model { get; } construct { - this.default_height = 500; - this.default_width = 700; this.can_navigate_back = true; // remove once we require Adw > 1.4 this.bind_property("model", accounts_page, "model", BindingFlags.SYNC_CREATE); this.bind_property("model", account_page, "model", BindingFlags.SYNC_CREATE); diff --git a/xmpp-vala/src/module/xep/0077_in_band_registration.vala b/xmpp-vala/src/module/xep/0077_in_band_registration.vala index baaa4ee0..87b654a8 100644 --- a/xmpp-vala/src/module/xep/0077_in_band_registration.vala +++ b/xmpp-vala/src/module/xep/0077_in_band_registration.vala @@ -30,6 +30,24 @@ public class Module : XmppStreamNegotiationModule { return null; } + public async ErrorStanza? change_password(XmppStream stream, Jid jid, string new_pw) { + StanzaNode pw_change_node = new StanzaNode.build("query", NS_URI).add_self_xmlns(); + StanzaNode username_node = new StanzaNode.build("username", NS_URI); + StanzaNode pw_node = new StanzaNode.build("password", NS_URI); + username_node.put_node(new StanzaNode.text(jid.localpart)); + pw_node.put_node(new StanzaNode.text(new_pw)); + pw_change_node.put_node(username_node); + pw_change_node.put_node(pw_node); + Iq.Stanza set_password_iq = new Iq.Stanza.set(pw_change_node) { to=jid.bare_jid.domain_jid }; + + Iq.Stanza chpw_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, set_password_iq); + if (chpw_result.is_error()) { + return chpw_result.get_error(); + } + + return null; + } + public override bool mandatory_outstanding(XmppStream stream) { return false; } public override bool negotiation_active(XmppStream stream) { return false; } |