Daniel Brötzmann pushed to branch newcontrol at gajim / gajim
Commits: 557079f5 by wurstsalat at 2022-07-16T17:52:48+02:00 MUCData: Store error and error text
- - - - - 94c9a1eb by wurstsalat at 2022-07-16T17:54:00+02:00 Move captcha and captcha-error pages to ChatFunctionPage
- - - - -
7 changed files:
- gajim/common/helpers.py - gajim/common/modules/muc.py - gajim/common/structs.py - gajim/data/gui/groupchat_control.ui - gajim/gtk/chat_function_page.py - gajim/gtk/chat_stack.py - gajim/gtk/controls/groupchat.py
Changes:
===================================== gajim/common/helpers.py ===================================== @@ -68,6 +68,7 @@ from nbxmpp.const import ConnectionType from nbxmpp.const import Affiliation from nbxmpp.errors import StanzaError +from nbxmpp.structs import CommonError from nbxmpp.structs import ProxyData from nbxmpp.protocol import JID from nbxmpp.protocol import InvalidJid @@ -1145,7 +1146,7 @@ def validate_jid(jid: Union[str, JID], type_: Optional[str] = None) -> JID: raise ValueError(f'Not a {type_} JID')
-def to_user_string(error: StanzaError) -> str: +def to_user_string(error: Union[CommonError, StanzaError]) -> str: text = error.get_text(get_rfc5646_lang()) if text: return text
===================================== gajim/common/modules/muc.py ===================================== @@ -34,6 +34,7 @@ from nbxmpp.protocol import JID from nbxmpp.protocol import Message from nbxmpp.protocol import Presence +from nbxmpp.structs import CommonError from nbxmpp.structs import DiscoInfo from nbxmpp.structs import MessageProperties from nbxmpp.structs import PresenceProperties @@ -511,6 +512,9 @@ def _on_error_presence(self, elif muc_data.state == MUCJoinedState.CAPTCHA_REQUEST: self._set_muc_state(room_jid, MUCJoinedState.CAPTCHA_FAILED) self._set_muc_state(room_jid, MUCJoinedState.NOT_JOINED) + muc_data.error = 'captcha-failed' + assert isinstance(properties.error, CommonError) + muc_data.error_text = helpers.to_user_string(properties.error) room.notify('room-captcha-error', properties.error)
elif muc_data.state == MUCJoinedState.CAPTCHA_FAILED:
===================================== gajim/common/structs.py ===================================== @@ -70,6 +70,8 @@ def __init__(self, # Message id of the captcha challenge self.captcha_id: Optional[str] = None self.captcha_form: Optional[SimpleDataForm] = None + self.error: Optional[str] = None + self.error_text: Optional[str] = None self.subject: Optional[MucSubject] = None
@property
===================================== gajim/data/gui/groupchat_control.ui ===================================== @@ -221,96 +221,7 @@ </packing> </child> <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="orientation">vertical</property> - <child> - <!-- n-columns=1 n-rows=2 --> - <object class="GtkGrid"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="halign">center</property> - <property name="valign">center</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="row-spacing">12</property> - <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="homogeneous">True</property> - <child> - <object class="GtkButton"> - <property name="label" translatable="yes">_Cancel</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="halign">start</property> - <property name="valign">start</property> - <property name="use-underline">True</property> - <signal name="clicked" handler="_on_captcha_cancel_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="captcha_set_button"> - <property name="label" translatable="yes">_Join</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can-focus">True</property> - <property name="can-default">True</property> - <property name="receives-default">True</property> - <property name="halign">end</property> - <property name="valign">start</property> - <property name="use-underline">True</property> - <signal name="clicked" handler="_on_captcha_set_clicked" swapped="no"/> - <style> - <class name="suggested-action"/> - </style> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">1</property> - </packing> - </child> - <child> - <object class="GtkBox" id="captcha_box"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="orientation">vertical</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="name">captcha</property> - <property name="position">2</property> - </packing> + <placeholder/> </child> <child> <object class="GtkBox"> @@ -451,116 +362,6 @@ <property name="position">3</property> </packing> </child> - <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="orientation">vertical</property> - <child> - <!-- n-columns=1 n-rows=3 --> - <object class="GtkGrid"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="halign">center</property> - <property name="valign">center</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="row-spacing">12</property> - <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="homogeneous">True</property> - <child> - <object class="GtkButton" id="captcha_close_button"> - <property name="label" translatable="yes">_Close</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="can-default">True</property> - <property name="receives-default">True</property> - <property name="halign">center</property> - <property name="valign">start</property> - <property name="use-underline">True</property> - <signal name="clicked" handler="_on_page_close_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="captcha_try_again_button"> - <property name="label" translatable="yes">_Try Again</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="can-default">True</property> - <property name="receives-default">True</property> - <property name="use-underline">True</property> - <signal name="clicked" handler="_on_captcha_try_again_clicked" swapped="no"/> - <style> - <class name="suggested-action"/> - </style> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">2</property> - </packing> - </child> - <child> - <object class="GtkImage"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="icon-name">dialog-error-symbolic</property> - <property name="icon_size">6</property> - <style> - <class name="error-color"/> - </style> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="captcha_error_label"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="justify">center</property> - <property name="wrap">True</property> - <property name="max-width-chars">40</property> - <style> - <class name="bold16"/> - </style> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="name">captcha-error</property> - <property name="position">4</property> - </packing> - </child> - <child> - <placeholder/> - </child> <child> <placeholder/> </child>
===================================== gajim/gtk/chat_function_page.py ===================================== @@ -38,7 +38,7 @@ class ChatFunctionPage(Gtk.Box):
__gsignals__ = { - 'finish': (GObject.SignalFlags.RUN_LAST, None, ()), + 'finish': (GObject.SignalFlags.RUN_LAST, None, (bool,)), 'message': (GObject.SignalFlags.RUN_LAST, None, (str,)), }
@@ -63,6 +63,7 @@ def __init__(self) -> None: self.add(self._heading)
self._content_box = Gtk.Box() + self._content_box.set_halign(Gtk.Align.CENTER) self.add(self._content_box)
cancel_button = Gtk.Button(label=_('Cancel')) @@ -72,7 +73,7 @@ def __init__(self) -> None: self._confirm_button.set_can_default(True) self._confirm_button.connect('clicked', self._on_confirm_clicked)
- button_box = Gtk.Box() + button_box = Gtk.Box(spacing=18) button_box.pack_start(cancel_button, False, True, 0) button_box.pack_end(self._confirm_button, False, True, 0)
@@ -81,7 +82,7 @@ def __init__(self) -> None:
def _on_key_press(self, _widget: Gtk.Widget, event: Gdk.EventKey) -> None: if event.keyval == Gdk.KEY_Escape: - self.emit('finish') + self.emit('finish', self._mode in ('error', 'captcha-error'))
def _clear(self) -> None: for child in self._content_box.get_children(): @@ -159,6 +160,14 @@ def set_mode(self, self._widget.show_all() self._widget.connect('is-valid', self._on_ready)
+ elif mode == 'captcha-error': + self._confirm_button.set_label(_('Try Again')) + self._confirm_button.set_sensitive(True) + self._confirm_button.get_style_context().add_class( + 'suggested-action') + assert data is not None + self._widget = ErrorWidget(data) + assert self._widget is not None self._content_box.add(self._widget) if isinstance(self._widget, InputWidget): @@ -221,7 +230,12 @@ def _on_confirm_clicked(self, _button: Gtk.Button) -> None: self._client.get_module('MUC').send_captcha( self._contact.jid, form_node)
- self.emit('finish') + elif self._mode == 'captcha-error': + assert self._client is not None + assert self._contact is not None + self._client.get_module('MUC').join(self._contact.jid) + + self.emit('finish', False)
def _on_cancel_clicked(self, _button: Gtk.Button) -> None: if self._mode == 'captcha-request': @@ -229,8 +243,14 @@ def _on_cancel_clicked(self, _button: Gtk.Button) -> None: assert self._contact is not None self._client.get_module('MUC').cancel_captcha( self._contact.jid) + self.emit('finish', True) + return
- self.emit('finish') + if self._mode in ('error', 'captcha-error'): + self.emit('finish', True) + return + + self.emit('finish', False)
def _invite(self, invited_jid: JID) -> None: assert self._contact is not None @@ -323,3 +343,22 @@ def focus(self) -> None:
def get_text(self) -> str: return self._entry.get_text() + + +class ErrorWidget(Gtk.Box): + def __init__(self, error_text: str) -> None: + Gtk.Box.__init__(self, + orientation=Gtk.Orientation.VERTICAL, + spacing=12) + image = Gtk.Image.new_from_icon_name( + 'dialog-error-symbolic', Gtk.IconSize.DIALOG) + image.get_style_context().add_class('error-color') + heading = Gtk.Label(label=_('Error')) + heading.get_style_context().add_class('bold16') + label = Gtk.Label(label=error_text) + label.set_max_width_chars(40) + + self.add(image) + self.add(heading) + self.add(label) + self.show_all()
===================================== gajim/gtk/chat_stack.py ===================================== @@ -24,7 +24,9 @@ from gi.repository import Gio from gi.repository import Gtk
+from nbxmpp.errors import StanzaError from nbxmpp.protocol import JID +from nbxmpp.structs import MessageProperties from nbxmpp.structs import PresenceProperties
from gajim.common import app @@ -106,6 +108,8 @@ def show_chat(self, account: str, jid: JID) -> None: 'user-role-changed': self._on_user_role_changed, 'user-affiliation-changed': self._on_user_affiliation_changed, 'state-changed': self._on_muc_state_changed, + 'room-captcha-challenge': self._on_muc_captcha_challenge, + 'room-captcha-error': self._on_muc_captcha_error, }) self._update_group_chat_actions(self._current_contact) elif isinstance(self._current_contact, GroupchatParticipant): @@ -118,17 +122,36 @@ def show_chat(self, account: str, jid: JID) -> None: str(self._current_contact.jid)) if muc_data is not None: if muc_data.state.is_captcha_request: - self._chat_function_page.set_mode( - self._current_contact, 'captcha-request') + self._show_chat_function_page('captcha-request') return + + if muc_data.error == 'captcha-failed': + self._show_chat_function_page( + 'captcha-error', muc_data.error_text) + return + if muc_data.state.is_password_request: - self._chat_function_page.set_mode( - self._current_contact, 'password-request') + self._show_chat_function_page('password-request') return
self.set_transition_type(Gtk.StackTransitionType.NONE) self.set_visible_child_name('controls')
+ def _on_muc_captcha_challenge(self, + contact: GroupchatContact, + _signal_name: str, + properties: MessageProperties + ) -> None: + self._show_chat_function_page('captcha-request') + + def _on_muc_captcha_error(self, + _contact: GroupchatContact, + _signal_name: str, + error: StanzaError + ) -> None: + error_text = helpers.to_user_string(error) + self._show_chat_function_page('captcha-error', error_text) + def _on_muc_state_changed(self, contact: GroupchatContact, _signal_name: str @@ -412,8 +435,16 @@ def _show_chat_function_page(self, self.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN) self.set_visible_child_name('function')
- def _on_function_finished(self, _function_page: ChatFunctionPage) -> None: - # TODO: close control on password/captcha cancel + def _on_function_finished(self, + _function_page: ChatFunctionPage, + close_control: bool + ) -> None: + if close_control: + self._close_control() + self.set_visible_child_name('empty') + self.set_transition_type(Gtk.StackTransitionType.NONE) + return + self.set_visible_child_name('controls') self.set_transition_type(Gtk.StackTransitionType.NONE)
@@ -485,6 +516,14 @@ def _on_send_message(self) -> None:
self._message_action_box.msg_textview.clear()
+ def _close_control(self) -> None: + assert self._current_contact is not None + app.window.activate_action( + 'remove-chat', + GLib.Variant('as', + [self._current_contact.account, + str(self._current_contact.jid)])) + def clear(self) -> None: self.set_visible_child_name('empty') self._control_stack.clear()
===================================== gajim/gtk/controls/groupchat.py ===================================== @@ -60,7 +60,6 @@ from gajim.gui.const import TARGET_TYPE_URI_LIST from gajim.gui.dialogs import DialogButton from gajim.gui.dialogs import ConfirmationDialog -from gajim.gui.dataform import DataFormWidget from gajim.gui.groupchat_roster import GroupchatRoster from gajim.gui.groupchat_state import GroupchatState from gajim.gui.util import open_window @@ -112,9 +111,6 @@ def __init__(self, account: str, jid: JID) -> None: 'hide_groupchat_occupants_list', self._show_roster) self.roster.connect('row-activated', self._on_roster_row_activated)
- # Holds CaptchaRequest widget - self._captcha_request: Optional[DataFormWidget] = None - self._subject_text = ''
self._set_control_inactive() @@ -147,8 +143,6 @@ def _connect_contact_signals(self) -> None: 'room-creation-failed': self._on_room_creation_failed, 'room-presence-error': self._on_room_presence_error, 'room-voice-request': self._on_room_voice_request, - 'room-captcha-challenge': self._on_room_captcha_challenge, - 'room-captcha-error': self._on_room_captcha_error, 'room-subject': self._on_room_subject, 'room-joined': self._on_room_joined, 'room-join-failed': self._on_room_join_failed, @@ -792,9 +786,7 @@ def delegate_action(self, action: str) -> int:
if self._get_current_page() == 'password': self._on_password_cancel_clicked() - elif self._get_current_page() == 'captcha': - self._on_captcha_cancel_clicked() - elif self._get_current_page() in ('error', 'captcha-error'): + elif self._get_current_page() == 'error': self._on_page_close_clicked() else: self._show_page('groupchat') @@ -816,8 +808,6 @@ def focus(self) -> None: pass elif page_name == 'password': self.xml.password_entry.grab_focus_without_selecting() - elif page_name == 'captcha': - self._captcha_request.focus_first_entry()
def _on_page_change(self, stack: Gtk.Stack, _param: Any) -> None: page_name = stack.get_visible_child_name() @@ -827,10 +817,6 @@ def _on_page_change(self, stack: Gtk.Stack, _param: Any) -> None: self.xml.password_entry.set_text('') self.xml.password_entry.grab_focus() self.xml.password_set_button.grab_default() - elif page_name == 'captcha': - self.xml.captcha_set_button.grab_default() - elif page_name == 'captcha-error': - self.xml.captcha_try_again_button.grab_default() elif page_name == 'error': self.xml.close_button.grab_default()
@@ -846,63 +832,6 @@ def _on_password_changed(self, entry: Gtk.Entry, *args: Any) -> None: def _on_password_cancel_clicked(self, *args: Any) -> None: self._close_control()
- def _on_room_captcha_challenge(self, - _contact: GroupchatContact, - _signal_name: str, - properties: MessageProperties - ) -> None: - self._remove_captcha_request() - form = properties.captcha.form - - options = {'no-scrolling': True, - 'entry-activates-default': True} - self._captcha_request = DataFormWidget(form, options=options) - self._captcha_request.connect('is-valid', self._on_captcha_changed) - self._captcha_request.set_valign(Gtk.Align.START) - self._captcha_request.show_all() - self.xml.captcha_box.add(self._captcha_request) - - self._show_page('captcha') - self._captcha_request.focus_first_entry() - - def _on_room_captcha_error(self, - _contact: GroupchatContact, - _signal_name: str, - error: StanzaError - ) -> None: - error_text = to_user_string(error) - self.xml.captcha_error_label.set_text(error_text) - self._show_page('captcha-error') - - def _remove_captcha_request(self) -> None: - if self._captcha_request is None: - return - if self._captcha_request in self.xml.captcha_box.get_children(): - self.xml.captcha_box.remove(self._captcha_request) - self._captcha_request.destroy() - self._captcha_request = None - - def _on_captcha_changed(self, - _widget: DataFormWidget, - is_valid: bool - ) -> None: - self.xml.captcha_set_button.set_sensitive(is_valid) - - def _on_captcha_set_clicked(self, _button: Gtk.Button) -> None: - form_node = self._captcha_request.get_submit_form() - self._client.get_module('MUC').send_captcha(self.room_jid, form_node) - self._remove_captcha_request() - self._show_page('groupchat') - - def _on_captcha_cancel_clicked(self, *args: Any) -> None: - self._client.get_module('MUC').cancel_captcha(self.room_jid) - self._remove_captcha_request() - self._close_control() - - def _on_captcha_try_again_clicked(self, _button: Gtk.Button) -> None: - self._client.get_module('MUC').join(self.room_jid) - self._show_page('groupchat') - def _on_remove_bookmark_button_clicked(self, _button: Gtk.Button) -> None: self._client.get_module('Bookmarks').remove(self.room_jid) self._close_control()
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/c671abf28db63c2bbf50ac07a6f54bc9...