Daniel Brötzmann pushed to branch master at gajim / gajim
Commits: 37b77137 by wurstsalat at 2022-10-12T22:53:36+02:00 fix: ChatFunctionPage: React to connection changes
Fixes #11206
- - - - - ce4c8c87 by wurstsalat at 2022-10-12T22:56:35+02:00 refactor: ChatFunctionPage: Use FunctionMode Enum
- - - - -
2 changed files:
- gajim/gtk/chat_function_page.py - gajim/gtk/chat_stack.py
Changes:
===================================== gajim/gtk/chat_function_page.py ===================================== @@ -16,6 +16,8 @@
from typing import Optional
+from enum import Enum + from gi.repository import GObject from gi.repository import Gtk from gi.repository import Pango @@ -25,15 +27,28 @@ from nbxmpp.protocol import validate_resourcepart
from gajim.common import app -from gajim.common.client import Client +from gajim.common import types +from gajim.common.const import SimpleClientState from gajim.common.i18n import _ from gajim.common.modules.contacts import GroupchatContact -from gajim.common.types import ChatContactT
from .dataform import DataFormWidget from .groupchat_inviter import GroupChatInviter
+class FunctionMode(Enum): + INVITE = 'invite' + CHANGE_NICKNAME = 'change-nickname' + KICK = 'kick' + BAN = 'ban' + PASSWORD_REQUEST = 'password-request' + CAPTCHA_REQUEST = 'captcha-request' + CAPTCHA_ERROR = 'captcha-error' + JOIN_FAILED = 'join-failed' + CREATION_FAILED = 'creation-failed' + CONFIG_FAILED = 'config-failed' + + class ChatFunctionPage(Gtk.Box):
__gsignals__ = { @@ -49,9 +64,11 @@ def __init__(self) -> None: self.set_valign(Gtk.Align.CENTER) self.get_style_context().add_class('padding-18')
- self._client: Optional[Client] = None - self._contact: Optional[ChatContactT] = None - self._mode: Optional[str] = None + self._client: Optional[types.Client] = None + self._contact: Optional[types.ChatContactT] = None + self._mode: Optional[FunctionMode] = None + self._data: Optional[str] = None + self._ready_state = True
self._widget: Optional[Gtk.Widget] = None
@@ -87,22 +104,22 @@ def __init__(self) -> None:
def process_escape(self) -> None: close_control = self._mode in ( - 'join-failed', - 'creation-failed', - 'config-failed', - 'captcha-error') + FunctionMode.JOIN_FAILED, + FunctionMode.CREATION_FAILED, + FunctionMode.CONFIG_FAILED, + FunctionMode.CAPTCHA_ERROR) self.emit('finish', close_control)
- def _clear(self) -> None: + def _reset(self) -> None: + if self._client is not None: + self._client.disconnect_all_from_obj(self) + for child in self._content_box.get_children(): child.destroy()
- def set_mode(self, - contact: ChatContactT, - mode: str, - data: Optional[str] = None - ) -> None: - self._clear() + if self._widget is not None: + self._widget.destroy() + self._confirm_button.get_style_context().remove_class( 'destructive-action') self._confirm_button.get_style_context().remove_class( @@ -110,18 +127,30 @@ def set_mode(self, self._confirm_button.set_sensitive(False) self._confirm_button.grab_default()
+ self._forget_button.set_sensitive(True) self._forget_button.hide()
+ self._ready_state = True + + def set_mode(self, + contact: types.ChatContactT, + mode: FunctionMode, + data: Optional[str] = None + ) -> None: + + self._reset() + self._contact = contact - self._heading.set_text(self._contact.name) self._client = app.get_client(contact.account) + self._client.connect_signal( + 'state-changed', self._on_client_state_changed) + self._mode = mode self._data = data
- if self._widget is not None: - self._widget.destroy() + self._heading.set_text(self._contact.name)
- if mode == 'invite': + if mode == FunctionMode.INVITE: self._confirm_button.set_label(_('Invite')) self._confirm_button.get_style_context().add_class( 'suggested-action') @@ -130,35 +159,35 @@ def set_mode(self, self._widget.connect('listbox-changed', self._on_ready) self._widget.load_contacts()
- elif mode == 'change-nickname': + elif mode == FunctionMode.CHANGE_NICKNAME: self._confirm_button.set_label(_('Change')) self._confirm_button.get_style_context().add_class( 'suggested-action') self._widget = InputWidget(self._contact, mode) self._widget.connect('changed', self._on_ready)
- elif mode == 'kick': + elif mode == FunctionMode.KICK: self._confirm_button.set_label(_('Kick')) self._confirm_button.set_sensitive(True) self._confirm_button.get_style_context().add_class( 'destructive-action') self._widget = InputWidget(self._contact, mode, data)
- elif mode == 'ban': + elif mode == FunctionMode.BAN: self._confirm_button.set_label(_('Ban')) self._confirm_button.set_sensitive(True) self._confirm_button.get_style_context().add_class( 'destructive-action') self._widget = InputWidget(self._contact, mode, data)
- elif mode == 'password-request': + elif mode == FunctionMode.PASSWORD_REQUEST: self._confirm_button.set_label(_('Join')) self._confirm_button.get_style_context().add_class( 'suggested-action') self._widget = InputWidget(self._contact, mode) self._widget.connect('changed', self._on_ready)
- elif mode == 'captcha-request': + elif mode == FunctionMode.CAPTCHA_REQUEST: self._confirm_button.set_label(_('Join')) self._confirm_button.get_style_context().add_class( 'suggested-action') @@ -172,23 +201,25 @@ def set_mode(self, self._widget.show_all() self._widget.connect('is-valid', self._on_ready)
- elif mode == 'captcha-error': + elif mode == FunctionMode.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') self._widget = ErrorWidget(error_text=data)
- elif mode in ('join-failed', 'creation-failed', 'config-failed'): + elif mode in (FunctionMode.JOIN_FAILED, + FunctionMode.CREATION_FAILED, + FunctionMode.CONFIG_FAILED): self._confirm_button.set_label(_('Try Again')) self._confirm_button.set_sensitive(True) self._confirm_button.get_style_context().add_class( 'suggested-action') - if mode == 'join-failed': + if mode == FunctionMode.JOIN_FAILED: is_bookmark = self._client.get_module('Bookmarks').is_bookmark( self._contact.jid) self._forget_button.set_visible(is_bookmark) - self._widget = ErrorWidget(error_mode=mode, error_text=data) + self._widget = ErrorWidget(mode=mode, error_text=data)
assert self._widget is not None self._content_box.add(self._widget) @@ -197,64 +228,76 @@ def set_mode(self, elif isinstance(self._widget, DataFormWidget): self._widget.focus_first_entry()
+ def _on_client_state_changed(self, + _client: types.Client, + _signal_name: str, + _state: SimpleClientState + ) -> None: + + self._update_button_state() + def _on_ready(self, _widget: Gtk.Widget, state: bool ) -> None: - self._confirm_button.set_sensitive(state) + + self._ready_state = state + self._update_button_state() + + def _update_button_state(self) -> None: + assert self._contact is not None + if app.account_is_connected(self._contact.account): + self._confirm_button.set_sensitive(self._ready_state) + self._forget_button.set_sensitive(True) + return + + self._confirm_button.set_sensitive(False) + self._forget_button.set_sensitive(False)
def _on_confirm_clicked(self, _button: Gtk.Button) -> None: - if self._mode == 'invite': + assert self._client is not None + assert self._contact is not None + + if self._mode == FunctionMode.INVITE: assert isinstance(self._widget, GroupChatInviter) invitees = self._widget.get_invitees() for jid in invitees: self._invite(JID.from_string(jid))
- elif self._mode == 'change-nickname': + elif self._mode == FunctionMode.CHANGE_NICKNAME: assert isinstance(self._widget, InputWidget) nickname = self._widget.get_text() - assert self._client is not None - assert self._contact is not None self._client.get_module('MUC').change_nick( self._contact.jid, nickname)
- elif self._mode == 'kick': + elif self._mode == FunctionMode.KICK: assert isinstance(self._widget, InputWidget) reason = self._widget.get_text() - assert self._client is not None - assert self._contact is not None self._client.get_module('MUC').set_role( self._contact.jid, self._data, 'none', reason)
- elif self._mode == 'ban': + elif self._mode == FunctionMode.BAN: assert isinstance(self._widget, InputWidget) reason = self._widget.get_text() - assert self._client is not None - assert self._contact is not None self._client.get_module('MUC').set_affiliation( self._contact.jid, {self._data: {'affiliation': 'outcast', 'reason': reason}})
- elif self._mode == 'password-request': + elif self._mode == FunctionMode.PASSWORD_REQUEST: assert isinstance(self._widget, InputWidget) password = self._widget.get_text() - assert self._client is not None - assert self._contact is not None self._client.get_module('MUC').set_password( self._contact.jid, password) self._client.get_module('MUC').join(self._contact.jid)
- elif self._mode == 'captcha-request': + elif self._mode == FunctionMode.CAPTCHA_REQUEST: assert isinstance(self._widget, DataFormWidget) form_node = self._widget.get_submit_form() - assert self._client is not None - assert self._contact is not None self._client.get_module('MUC').send_captcha( self._contact.jid, form_node)
- elif self._mode in ('join-failed', 'captcha-error'): - assert self._client is not None - assert self._contact is not None + elif self._mode in (FunctionMode.JOIN_FAILED, + FunctionMode.CAPTCHA_ERROR): self._client.get_module('MUC').join(self._contact.jid)
self.emit('finish', False) @@ -263,27 +306,29 @@ def _on_cancel_clicked(self, _button: Gtk.Button) -> None: assert self._client is not None assert self._contact is not None
- if self._mode == 'captcha-request': - self._client.get_module('MUC').cancel_captcha( - self._contact.jid) - self.emit('finish', True) - return + connected = app.account_is_connected(self._contact.account) + close_control = False
- if self._mode == 'password-request': - self._client.get_module('MUC').cancel_password_request( - self._contact.jid) - self.emit('finish', True) - return + if self._mode == FunctionMode.CAPTCHA_REQUEST: + if connected: + self._client.get_module('MUC').cancel_captcha( + self._contact.jid) + close_control = True
- if self._mode in ( - 'join-failed', - 'creation-failed', - 'config-failed', - 'captcha-error'): - self.emit('finish', True) - return + elif self._mode == FunctionMode.PASSWORD_REQUEST: + if connected: + self._client.get_module('MUC').cancel_password_request( + self._contact.jid) + close_control = True
- self.emit('finish', False) + elif self._mode in ( + FunctionMode.JOIN_FAILED, + FunctionMode.CREATION_FAILED, + FunctionMode.CONFIG_FAILED, + FunctionMode.CAPTCHA_ERROR): + close_control = True + + self.emit('finish', close_control)
def _on_forget_clicked(self, _button: Gtk.Button) -> None: assert self._client is not None @@ -310,10 +355,11 @@ class InputWidget(Gtk.Box): }
def __init__(self, - contact: ChatContactT, - mode: str, + contact: types.ChatContactT, + mode: FunctionMode, data: Optional[str] = None ) -> None: + Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12) @@ -336,21 +382,21 @@ def __init__(self, self._entry.connect('changed', self._on_entry_changed) self.add(self._entry)
- if mode == 'change-nickname': + if mode == FunctionMode.CHANGE_NICKNAME: heading_label.set_text(_('Change Nickname')) sub_label.set_text(_('Enter your new nickname')) assert isinstance(self._contact, GroupchatContact) self._entry.set_text(self._contact.nickname or '')
- elif mode == 'kick': + elif mode == FunctionMode.KICK: heading_label.set_text(_('Kick %s') % data) sub_label.set_text(_('Reason (optional)'))
- elif mode == 'ban': + elif mode == FunctionMode.BAN: heading_label.set_text(_('Ban %s') % data) sub_label.set_text(_('Reason (optional)'))
- elif mode == 'password-request': + elif mode == FunctionMode.PASSWORD_REQUEST: heading_label.set_text(_('Password Required')) sub_label.set_text(_('Enter a password to join this chat')) self._entry.set_input_purpose(Gtk.InputPurpose.PASSWORD) @@ -365,7 +411,7 @@ def _on_destroy(self, _widget: InputWidget) -> None: def _on_entry_changed(self, entry: Gtk.Entry) -> None: text = entry.get_text()
- if self._mode == 'change-nickname': + if self._mode == FunctionMode.CHANGE_NICKNAME: assert isinstance(self._contact, GroupchatContact) if not text or text == self._contact.nickname: self.emit('changed', False) @@ -387,9 +433,10 @@ def get_text(self) -> str:
class ErrorWidget(Gtk.Box): def __init__(self, - error_mode: Optional[str] = None, + mode: Optional[FunctionMode] = None, error_text: Optional[str] = None ) -> None: + Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12) @@ -400,11 +447,11 @@ def __init__(self, heading = Gtk.Label() heading.get_style_context().add_class('bold16') heading_text = _('An Error Occurred') - if error_mode == 'join-failed': + if mode == FunctionMode.JOIN_FAILED: heading_text = _('Failed to Join Group Chat') - elif error_mode == 'creation-failed': + elif mode == FunctionMode.CREATION_FAILED: heading_text = _('Failed to Create Group Chat') - elif error_mode == 'config-failed': + elif mode == FunctionMode.CONFIG_FAILED: heading_text = _('Failed to Configure Group Chat') heading.set_text(heading_text)
===================================== gajim/gtk/chat_stack.py ===================================== @@ -46,6 +46,7 @@
from .chat_banner import ChatBanner from .chat_function_page import ChatFunctionPage +from .chat_function_page import FunctionMode from .const import TARGET_TYPE_URI_LIST from .control import ChatControl from .message_actions_box import MessageActionsBox @@ -219,21 +220,25 @@ 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._show_chat_function_page('captcha-request') + self._show_chat_function_page(FunctionMode.CAPTCHA_REQUEST) return
if muc_data.state.is_password_request: - self._show_chat_function_page('password-request') + self._show_chat_function_page(FunctionMode.PASSWORD_REQUEST) return
if not muc_data.state.is_joined: if muc_data.error == 'captcha-failed': self._show_chat_function_page( - 'captcha-error', muc_data.error_text) + FunctionMode.CAPTCHA_ERROR, muc_data.error_text) return - if muc_data.error in ('join-failed', 'creation-failed'): + if muc_data.error == 'join-failed': self._show_chat_function_page( - muc_data.error, muc_data.error_text) + FunctionMode.JOIN_FAILED, muc_data.error_text) + return + if muc_data.error == 'creation-failed': + self._show_chat_function_page( + FunctionMode.CREATION_FAILED, muc_data.error_text) return
self.set_transition_type(Gtk.StackTransitionType.NONE) @@ -254,47 +259,53 @@ def _on_room_password_required(self, _signal_name: str, _properties: MessageProperties ) -> None: - self._show_chat_function_page('password-request') + + self._show_chat_function_page(FunctionMode.PASSWORD_REQUEST)
def _on_room_captcha_challenge(self, contact: GroupchatContact, _signal_name: str, - properties: MessageProperties + _properties: MessageProperties ) -> None: - self._show_chat_function_page('captcha-request') + + self._show_chat_function_page(FunctionMode.CAPTCHA_REQUEST)
def _on_room_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) + self._show_chat_function_page(FunctionMode.CAPTCHA_ERROR, error_text)
def _on_room_creation_failed(self, _contact: GroupchatContact, _signal_name: str, properties: MessageProperties ) -> None: + assert properties.error is not None error_text = helpers.to_user_string(properties.error) - self._show_chat_function_page('creation-failed', error_text) + self._show_chat_function_page(FunctionMode.CREATION_FAILED, error_text)
def _on_room_join_failed(self, _contact: GroupchatContact, _signal_name: str, error: StanzaError ) -> None: + self._show_chat_function_page( - 'join-failed', helpers.to_user_string(error)) + FunctionMode.JOIN_FAILED, helpers.to_user_string(error))
def _on_room_config_failed(self, _contact: GroupchatContact, _signal_name: str, error: StanzaError ) -> None: + self._show_chat_function_page( - 'config-failed', helpers.to_user_string(error)) + FunctionMode.CONFIG_FAILED, helpers.to_user_string(error))
def _on_muc_state_changed(self, contact: GroupchatContact, @@ -587,10 +598,10 @@ def _on_action(self, 'ContactInfo', account=account, contact=resource_contact)
elif action_name == 'muc-invite': - self._show_chat_function_page('invite') + self._show_chat_function_page(FunctionMode.INVITE)
elif action_name == 'muc-change-nickname': - self._show_chat_function_page('change-nickname') + self._show_chat_function_page(FunctionMode.CHANGE_NICKNAME)
elif action_name == 'muc-execute-command': nick = None @@ -605,12 +616,12 @@ def _on_action(self, elif action_name == 'muc-kick': assert param is not None kick_nick = param.get_string() - self._show_chat_function_page('kick', data=kick_nick) + self._show_chat_function_page(FunctionMode.KICK, data=kick_nick)
elif action_name == 'muc-ban': assert param is not None ban_jid = param.get_string() - self._show_chat_function_page('ban', data=ban_jid) + self._show_chat_function_page(FunctionMode.BAN, data=ban_jid)
elif action_name == 'muc-change-role': assert param is not None @@ -666,12 +677,13 @@ def _on_drag_motion(self, return True
def _show_chat_function_page(self, - function: str, + function_mode: FunctionMode, data: Optional[str] = None ) -> None: + assert self._current_contact is not None self._chat_function_page.set_mode( - self._current_contact, function, data) + self._current_contact, function_mode, data) self.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN) self.set_visible_child_name('function')
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/545119ca0c348aa1f06143c77367c3a3...