Philipp Hörist pushed to branch newcontrol at gajim / gajim
Commits: 74571a31 by lovetox at 2022-07-04T19:24:53+02:00 refactor: Replace reduce_chars_newlines() with stdlib
- - - - - a5f77d71 by lovetox at 2022-07-04T19:24:53+02:00 fix: GtkStatusIcon: Always show when Gajim has not toplevel focus
- - - - - ee623949 by lovetox at 2022-07-04T19:24:53+02:00 fix: StatusIcon: Don’t lose new message icon on state change
Fixes #11013
- - - - - 3228ed27 by wurstsalat at 2022-07-04T19:24:53+02:00 imprv: ChatActionProcessor: Improve detection of emoji shortcode start
- - - - - 1a13e06d by wurstsalat at 2022-07-04T19:24:53+02:00 imprv: Roster: Display show value in tooltip
Fixes #11010
- - - - - aa24823a by wurstsalat at 2022-07-04T19:24:53+02:00 chore: Update pyrightconfig
- - - - - d3349704 by André Apitzsch at 2022-07-04T19:24:53+02:00 fix: CodeWidget: Don't highlight matching brackets
Highlighting matching brackets is confusing without a visible cursor.
Fixes #11026.
- - - - - b8550beb by lovetox at 2022-07-04T19:24:53+02:00 chore: Raise nbxmpp version
- - - - - 00878b99 by Peter Shkenev at 2022-07-04T19:24:53+02:00 chore: Use proper dependency names in README
- - - - - 17ac9d48 by lovetox at 2022-07-04T19:24:53+02:00 Things2
- - - - - 2e97128a by lovetox at 2022-07-04T19:24:53+02:00 Refactor Actions
- - - - -
17 changed files:
- README.md - gajim/common/helpers.py - gajim/gajim.py - gajim/gtk/account_page.py - gajim/gtk/application.py - gajim/gtk/chat_action_processor.py - gajim/gtk/const.py - gajim/gtk/conversation/code_widget.py - gajim/gtk/conversation/rows/message.py - gajim/gtk/groupchat_roster.py - gajim/gtk/main.py - gajim/gtk/menus.py - gajim/gtk/notification_manager.py - gajim/gtk/roster.py - gajim/gtk/status_icon.py - gajim/gtk/tooltips.py - pyrightconfig.json
Changes:
===================================== README.md ===================================== @@ -2,46 +2,45 @@ # Welcome to Gajim
### Runtime Requirements
-- python3.9 or higher -- python3-gi -- python3-gi-cairo -- python3-nbxmpp (>=2.99.0) -- python3-openssl (>=16.2) -- python3-css-parser -- python3-keyring -- python3-precis-i18n -- python3-packaging -- python3-pil -- python3-setuptools -- Gtk3 (>=3.22) -- GLib (>=2.60.0) -- gir1.2-gtksource-4 -- gir1.2-soup-2.4 +- [Python](https://www.python.org/) (>=3.9) +- [PyGObject](https://pypi.org/project/PyGObject/) +- [pycairo](https://pypi.org/project/pycairo/) +- [nbxmpp](https://pypi.org/project/nbxmpp/) (>=3.0.0) +- [pyOpenSSL](https://pypi.org/project/pyOpenSSL/) (>=16.2) +- [css-parser](https://pypi.org/project/css-parser/) +- [keyring](https://pypi.org/project/keyring/) +- [precis_i18n](https://pypi.org/project/precis-i18n/) +- [packaging](https://pypi.org/project/packaging/) +- [Pillow](https://pypi.org/project/Pillow/) +- [setuptools](https://pypi.org/project/setuptools/) +- [Gtk3](https://gitlab.com/gnome/gtk) (>=3.22) +- [GLib](https://gitlab.com/gnome/glib) (>=2.60.0) +- [GtkSourceView](https://gitlab.gnome.org/GNOME/gtksourceview) +- [libsoup](https://gitlab.gnome.org/GNOME/libsoup/)
### Optional Runtime Requirements
-- python3-sentry-sdk for Sentry error reporting to dev.gajim.org (users decide whether to send reports or not) -- gir1.2-gspell-1 and hunspell-LANG where lang is your locale eg. en, fr etc -- gir1.2-secret-1 for GNOME Keyring or KDE support as password storage - D-Bus running to have gajim-remote working -- gir1.2-gupnpigd-1.0 for better NAT traversing -- gir1.2-networkmanager-1.0 for network lose detection -- gir1.2-geoclue-2.0 for sharing your location -- gir1.2-gsound-1.0 for sound on Linux +- [sentry-sdk](https://pypi.org/project/sentry-sdk/) for Sentry error reporting to dev.gajim.org (users decide whether to send reports or not) +- [gspell](https://gitlab.gnome.org/GNOME/gspell) and hunspell-LANG where lang is your locale eg. en, fr etc +- [libsecret](https://gitlab.gnome.org/GNOME/libsecret/) for GNOME Keyring or KDE support as password storage +- [GUPnP-IGD](https://gitlab.gnome.org/GNOME/gupnp) for better NAT traversing +- [NetworkManager](https://gitlab.freedesktop.org/NetworkManager/NetworkManager) for network lose detection +- [GeoClue](https://gitlab.freedesktop.org/geoclue/geoclue) for sharing your location +- [GSound](https://gitlab.gnome.org/GNOME/gsound) for sound on Linux
#### For Video and Audio Calls
-- gir1.2-farstream-0.2 for video calls -- gir1.2-gstreamer-1.0 for video and audio calls -- gir1.2-gst-plugins-base-1.0 for video and audio calls -- gstreamer1.0-plugins-ugly for video and audio calls -- gstreamer1.0-libav for video and audio calls -- gstreamer1.0-gtk3 +- [Farstream](https://gitlab.freedesktop.org/farstream/farstream) +- [GStreamer](https://gitlab.freedesktop.org/gstreamer/gstreamer) +- [gst-plugins-base](https://gitlab.freedesktop.org/gstreamer/gst-plugins-base) +- [gst-plugins-ugly](https://gitlab.freedesktop.org/gstreamer/gst-plugins-ugly) +- [gst-libav](https://gitlab.freedesktop.org/gstreamer/gst-libav)
### Compile-time Requirements
-- python3-setuptools -- gettext +- [setuptools](https://pypi.org/project/setuptools/) +- [gettext](https://savannah.gnu.org/projects/gettext/)
### Running Tests
===================================== gajim/common/helpers.py ===================================== @@ -385,36 +385,6 @@ def sanitize_filename(filename: str) -> str: return filename
-def reduce_chars_newlines(text: str, max_chars: int = 0, - max_lines: int = 0) -> str: - """ - Cut the chars after 'max_chars' on each line and show only the first - 'max_lines' - - If any of the params is not present (None or 0) the action on it is not - performed - """ - def _cut_if_long(string_: str) -> str: - if len(string_) > max_chars: - string_ = string_[:max_chars - 3] + '…' - return string_ - - if max_lines == 0: - lines = text.split('\n') - else: - lines = text.split('\n', max_lines)[:max_lines] - if max_chars > 0: - if lines: - lines = [_cut_if_long(e) for e in lines] - if lines: - reduced_text = '\n'.join(lines) - if reduced_text != text: - reduced_text += '…' - else: - reduced_text = '' - return reduced_text - - def get_contact_dict_for_account(account: str) -> dict[str, types.BareContact]: """ Creates a dict of jid -> contact with all contacts of account
===================================== gajim/gajim.py ===================================== @@ -30,7 +30,7 @@ from gajim.common import i18n
-_MIN_NBXMPP_VER = '3.0.0.dev3' +_MIN_NBXMPP_VER = '3.0.0' _MIN_GTK_VER = '3.22.27' _MIN_CAIRO_VER = '1.16.0' _MIN_PYGOBJECT_VER = '3.32.0'
===================================== gajim/gtk/account_page.py ===================================== @@ -75,7 +75,7 @@ def __init__(self, account: str) -> None:
roster_menu = Gio.Menu() for action, label in ROSTER_MENU_DICT.items(): - roster_menu.append(label, f'win.{action}-{account}') + roster_menu.append(label, f'win.{action}') self._ui.roster_menu_button.set_menu_model(roster_menu)
self._ui.connect_signals(self)
===================================== gajim/gtk/application.py ===================================== @@ -524,6 +524,8 @@ def _on_feature_discovered(self, event: events.FeatureDiscovered) -> None: elif event.feature == Namespace.BLOCKING: action = '%s-blocking' % event.account self.set_action_state(action, True) + action = '%s-block-contact' % event.account + self.set_action_state(action, True)
# Action Callbacks
===================================== gajim/gtk/chat_action_processor.py ===================================== @@ -210,7 +210,7 @@ def _check_for_emoji(self, start_iter: Gtk.TextIter) -> bool: start, _end = search colon_offset = start.get_offset() before_colon = self._buf.get_iter_at_offset(colon_offset - 1) - if before_colon.get_char() != ' ': + if before_colon.get_char() not in (' ', '\n'): # We want to show the menu only if text begins with a colon, # or if a colon follows on a space. This avoids showing the # menu within normal sentences containing colons.
===================================== gajim/gtk/const.py ===================================== @@ -212,11 +212,17 @@ def __str__(self): ('set-encryption', 's', 'disabled', False), ('send-chatstate', 's', 'disabled', False), ('send-marker', 'b', False, False), + ('sort-by-show', 'b', 'app-setting:showoffline', True), + ('show-offline', 'b', 'app-setting:sort_by_show_in_roster', True), ]
ACCOUNT_ACTIONS = [ ('add-contact', 'as'), + ('block-contact', 's'), + ('remove-contact', 's'), + ('execute-command', 's'), + ('modify-gateway', 's'), ('archive', 's'), ('blocking', 's'), ('bookmarks', 's'), @@ -242,6 +248,9 @@ def __str__(self):
ONLINE_ACCOUNT_ACTIONS = { 'add-contact', + 'remove-contact', + 'execute-command', + 'modify-gateway', 'bookmarks', 'import-contacts', 'open-chat', @@ -256,4 +265,5 @@ def __str__(self): FEATURE_ACCOUNT_ACTIONS = { 'archive', 'blocking', + 'block-contact', }
===================================== gajim/gtk/conversation/code_widget.py ===================================== @@ -100,6 +100,7 @@ def __init__(self) -> None: self.set_top_margin(2) self.set_bottom_margin(2) self.set_monospace(True) + self.get_buffer().set_highlight_matching_brackets(False)
self._source_manager = GtkSource.LanguageManager.get_default() self._style_scheme_manager = GtkSource.StyleSchemeManager.get_default()
===================================== gajim/gtk/conversation/rows/message.py ===================================== @@ -18,6 +18,7 @@
from datetime import datetime from datetime import timedelta +import textwrap
from gi.repository import Gdk from gi.repository import GdkPixbuf @@ -40,7 +41,6 @@ from gajim.common.helpers import get_group_chat_nick from gajim.common.helpers import get_muc_context from gajim.common.helpers import message_needs_highlight -from gajim.common.helpers import reduce_chars_newlines from gajim.common.helpers import to_user_string from gajim.common.i18n import _ from gajim.common.i18n import Q_ @@ -187,8 +187,10 @@ def __init__(self, if correction_original is not None: self._original_text = correction_original self._message_icons.set_correction_icon_visible(True) - original_text = reduce_chars_newlines( - correction_original, max_chars=150, max_lines=10) + original_text = textwrap.fill(correction_original, + width=150, + max_lines=10, + placeholder='…') self._message_icons.set_correction_tooltip( _('Message corrected. Original message:' '\n%s') % original_text) @@ -425,8 +427,10 @@ def set_correction(self, text: str, nickname: Optional[str]) -> None: self._message_icons.set_receipt_icon_visible(False) self._message_icons.set_correction_icon_visible(True)
- original_text = reduce_chars_newlines( - self._original_text, max_chars=150, max_lines=10) + original_text = textwrap.fill(self._original_text, + width=150, + max_lines=10, + placeholder='…') self._message_icons.set_correction_tooltip( _('Message corrected. Original message:\n%s') % original_text)
===================================== gajim/gtk/groupchat_roster.py ===================================== @@ -378,7 +378,6 @@ def _show_contact_menu(self, nick: str, rect: Gdk.Rectangle) -> None: assert self_contact is not None contact = self._group_chat_contact.get_resource(nick) menu = get_groupchat_roster_menu(self._account, - self._control_id, self_contact, contact)
===================================== gajim/gtk/main.py ===================================== @@ -335,6 +335,11 @@ def _add_actions2(self) -> None: self.add_action(act)
for action, variant_type, default, enabled in MAIN_WIN_STATEFUL_ACTIONS: + + if isinstance(default, str) and default.startswith('app-setting:'): + setting = default.removeprefix('app-setting:') + default = app.settings.get(setting) + action = Gio.SimpleAction.new_stateful( action, GLib.VariantType(variant_type),
===================================== gajim/gtk/menus.py ===================================== @@ -21,6 +21,7 @@ from typing import Optional from typing import Union
+import textwrap from urllib.parse import quote
from gi.repository import Gtk @@ -34,7 +35,6 @@ from gajim.common.helpers import is_affiliation_change_allowed from gajim.common.helpers import is_role_change_allowed from gajim.common.helpers import jid_is_blocked -from gajim.common.helpers import reduce_chars_newlines from gajim.common.i18n import _ from gajim.common.i18n import get_short_lang_code from gajim.common.const import URIType @@ -77,7 +77,7 @@ def get_singlechat_menu(contact: types.BareContact) -> GajimMenu:
menu = GajimMenu() menu.add_item(_('Details'), 'win.show-contact-info') - menu.add_item(_('Block Contact…'), f'win.block-contact-{account}') + menu.add_item(_('Block Contact…'), f'app.{account}-block-contact')
submenu = get_send_file_submenu() menu.append_submenu(_('Send File'), submenu) @@ -207,7 +207,9 @@ def get_encryption_menu() -> GajimMenu: def get_conv_action_context_menu(account: str, selected_text: str ) -> Gtk.MenuItem: - selected_text_short = reduce_chars_newlines(selected_text, 10, 1) + selected_text_short = textwrap.shorten(selected_text, + width=10, + placeholder='…')
action_menu_item = Gtk.MenuItem.new_with_mnemonic( _('_Actions for "%s"') % selected_text_short) @@ -356,15 +358,15 @@ def get_roster_menu(account: str, jid: str, gateway: bool = False) -> GajimMenu: value = f'"{jid}"'
menuitems: MenuItemListT = [ - (_('Details'), f'win.contact-info-{account}', value), - (_('Execute Command…'), f'win.execute-command-{account}', value), - (block_label, f'win.block-contact-{account}', value), - (_('Remove…'), f'win.remove-contact-{account}', value), + (_('Details'), f'app.{account}-contact-info', value), + (_('Execute Command…'), f'app.{account}-execute-command', value), + (block_label, f'app.{account}-block-contact', value), + (_('Remove…'), f'app.{account}-remove-contact', value), ]
if gateway: menuitems.insert( - 1, (_('Modify Gateway…'), f'win.modify-gateway-{account}', value)) + 1, (_('Modify Gateway…'), f'app.{account}-modify-gateway', value))
return GajimMenu.from_list(menuitems)
===================================== gajim/gtk/notification_manager.py ===================================== @@ -378,7 +378,7 @@ def __init__(self, account: str, jid: str) -> None: remove_button.set_valign(Gtk.Align.CENTER) remove_button.set_tooltip_text('Remove from contact list') remove_button.set_action_name( - f'win.remove-contact-{self._account}') + f'win.{self._account}-remove-contact') remove_button.set_action_target_value(GLib.Variant('s', str(self.jid))) self.grid.attach(remove_button, 3, 1, 1, 2)
===================================== gajim/gtk/roster.py ===================================== @@ -116,8 +116,6 @@ def __init__(self, account: str) -> None: self._ui.connect_signals(self)
self.register_events([ - ('account-connected', ged.CORE, self._on_account_state), - ('account-disconnected', ged.CORE, self._on_account_state), ('roster-received', ged.CORE, self._on_roster_received), ('theme-update', ged.GUI2, self._on_theme_update), ('roster-push', ged.GUI2, self._on_roster_push), @@ -134,73 +132,36 @@ def __init__(self, account: str) -> None: self._filter_enabled = False self._filter_string = ''
- self._add_actions() + self._connect_actions() self._initial_draw()
- def _add_actions(self): - actions = [ - ('contact-info', self._on_contact_info), - ('modify-gateway', self._on_modify_gateway), - ('execute-command', self._on_execute_command), - ('block-contact', self._on_block_contact), - ('remove-contact', self._on_remove_contact), + def _connect_actions(self): + app_actions = [ + (f'{self._account}-modify-gateway', self._on_modify_gateway), + (f'{self._account}-execute-command', self._on_execute_command), + (f'{self._account}-block-contact', self._on_block_contact), + (f'{self._account}-remove-contact', self._on_remove_contact), ] - for action in actions: - action_name, func = action - act = Gio.SimpleAction.new( - f'{action_name}-{self._account}', GLib.VariantType.new('s')) - act.connect('activate', func) - app.window.add_action(act) - - action = Gio.SimpleAction.new_stateful( - f'show-offline-{self._account}', - None, - GLib.Variant.new_boolean(app.settings.get('showoffline'))) - action.connect('change-state', self._on_show_offline) - app.window.add_action(action) - - action = Gio.SimpleAction.new_stateful( - f'sort-by-show-{self._account}', - None, - GLib.Variant.new_boolean( - app.settings.get('sort_by_show_in_roster'))) - action.connect('change-state', self._on_sort_by_show) - app.window.add_action(action) - - def update_actions(self): - online = app.account_is_connected(self._account) - blocking_support = self._client.get_module('Blocking').supported - - actions = [('contact-info', online), - ('modify-gateway', online), - ('execute-command', online), - ('block-contact', online and blocking_support), - ('remove-contact', online)] - - for action, enabled in actions: - act = app.window.lookup_action(f'{action}-{self._account}') - assert act is not None - act.set_enabled(enabled) - - def _remove_actions(self): - actions = [ - 'contact-info', - 'modify-gateway', - 'execute-command', - 'block-contact', - 'remove-contact', - 'show-offline', - 'sort-by-show', + + win_actions = [ + ('show-offline', self._on_show_offline), + ('sort-by-show', self._on_sort_by_show), ] - for action in actions: - app.window.remove_action(f'{action}-{self._account}') + + for action in app_actions: + action_name, func = action + action = app.app.lookup_action(action_name) + assert action is not None + action.connect('activate', func) + + for action in win_actions: + action_name, func = action + action = app.window.get_action(action_name) + action.connect('change-state', func)
def _get_contact(self, jid: str) -> types.BareContact: return self._client.get_module('Contacts').get_contact(jid)
- def _on_account_state(self, _event: ApplicationEvent) -> None: - self.update_actions() - def _on_theme_update(self, _event: ApplicationEvent) -> None: self.redraw()
@@ -785,7 +746,6 @@ def process_event(self, event: ApplicationEvent) -> None: return
def _on_destroy(self, _roster: Roster) -> None: - self._remove_actions() self._contact_refs.clear() self._group_refs.clear() self._unset_model()
===================================== gajim/gtk/status_icon.py ===================================== @@ -80,8 +80,7 @@ def _can_use_libindicator() -> bool: app.is_installed('AYATANA_APPINDICATOR'))
def _on_setting_changed(self, *args: Any) -> None: - count = app.window.get_total_unread_count() - self._backend.update_state(count=count) + self._backend.update_state()
def connect_unread_widget(self, widget: Gtk.Widget, signal: str) -> None: widget.connect(signal, self._on_unread_count_changed) @@ -89,8 +88,7 @@ def connect_unread_widget(self, widget: Gtk.Widget, signal: str) -> None: def _on_unread_count_changed(self, *args: Any) -> None: if not app.settings.get('trayicon_notification_on_events'): return - count = app.window.get_total_unread_count() - self._backend.update_state(count=count) + self._backend.update_state()
def is_visible(self) -> bool: return self._backend.is_visible() @@ -100,7 +98,7 @@ def shutdown(self) -> None:
class NoneBackend: - def update_state(self, count: int = 0) -> None: + def update_state(self, init: bool = False) -> None: pass
def is_visible(self) -> bool: @@ -133,7 +131,7 @@ def __init__(self) -> None: client.connect_signal('state-changed', self._on_client_state_changed)
- def update_state(self, count: int = 0) -> None: + def update_state(self, init: bool = False) -> None: raise NotImplementedError
def is_visible(self) -> bool: @@ -188,9 +186,6 @@ def _on_sounds_mute(widget: Gtk.CheckMenuItem) -> None: app.settings.set('sounds_on', not widget.get_active())
def _on_toggle_window(self, _widget: Gtk.MenuItem) -> None: - self._on_activate() - - def _on_activate(self, *args: Any) -> None: if app.window.is_minimized(): app.window.unminimize() elif app.window.is_withdrawn(): @@ -223,9 +218,9 @@ def __init__(self) -> None: self._status_icon.connect('popup-menu', self._on_popup_menu) self._status_icon.connect('size-changed', self._on_size_changed)
- self.update_state() + self.update_state(init=True)
- def update_state(self, count: int = 0) -> None: + def update_state(self, init: bool = False) -> None: if self._shutdown: # Shutdown in progress, don't update icon return @@ -236,7 +231,7 @@ def update_state(self, count: int = 0) -> None:
self._status_icon.set_visible(True)
- if count > 0: + if not init and app.window.get_total_unread_count(): self._status_icon.set_from_icon_name('mail-message-new') return
@@ -258,17 +253,22 @@ def _on_size_changed(self, self.update_state()
def _on_popup_menu(self, - _status_icon: Gtk.StatusIcon, + status_icon: Gtk.StatusIcon, button: int, activate_time: int) -> None: - if button == 1: - self._on_activate() - elif button == 2: - self._on_activate() + + if button in (1, 2): + self._on_activate(status_icon) elif button == 3: - self._build_menu(activate_time) + self._build_menu(button, activate_time) + + def _on_activate(self, _status_icon: Gtk.StatusIcon) -> None: + if app.window.has_toplevel_focus(): + app.window.hide() + else: + app.window.show()
- def _build_menu(self, event_time: int) -> None: + def _build_menu(self, button: int, event_time: int) -> None: for menu in self._popup_menus: menu.destroy()
@@ -289,7 +289,7 @@ def _build_menu(self, event_time: int) -> None:
self._ui.systray_context_menu.show_all() self._ui.systray_context_menu.popup( - None, None, None, None, 0, event_time) + None, None, None, None, button, event_time)
class AppIndicator(GtkMenuBackend): @@ -306,16 +306,16 @@ def __init__(self) -> None: self._status_icon.set_secondary_activate_target( self._ui.toggle_window_menuitem)
- self.update_state() + self.update_state(init=True)
- def update_state(self, count: int = 0) -> None: + def update_state(self, init: bool = False) -> None: if not app.settings.get('show_trayicon'): self._status_icon.set_status(appindicator.IndicatorStatus.PASSIVE) return
self._status_icon.set_status(appindicator.IndicatorStatus.ACTIVE)
- if count > 0: + if not init and app.window.get_total_unread_count(): icon_name = 'mail-message-new' self._status_icon.set_icon_full(icon_name, _('Pending Event')) return
===================================== gajim/gtk/tooltips.py ===================================== @@ -206,6 +206,8 @@ def _add_resources(self, contact: types.BareContact) -> None: show_image.set_halign(Gtk.Align.START) show_image.set_valign(Gtk.Align.CENTER)
+ show_string = helpers.get_uf_show(resource.show.value) + assert resource.jid.resource is not None resource_string = GLib.markup_escape_text(resource.jid.resource) resource_label = Gtk.Label() @@ -213,7 +215,7 @@ def _add_resources(self, contact: types.BareContact) -> None: resource_label.set_xalign(0) resource_label.set_ellipsize(Pango.EllipsizeMode.END) resource_label.set_max_width_chars(30) - resource_label.set_text(resource_string) + resource_label.set_text(f'{show_string} ({resource_string})')
base_box = Gtk.Box(spacing=6) base_box.add(show_image)
===================================== pyrightconfig.json ===================================== @@ -41,6 +41,7 @@ "gajim/common/sound.py", "gajim/common/storage/", "gajim/common/task_manager.py", + "gajim/gtk/conversation", "gajim/gtk/about.py", "gajim/gtk/account_page.py", "gajim/gtk/account_side_bar.py", @@ -65,9 +66,8 @@ "gajim/gtk/control_stack.py", "gajim/gtk/const.py", "gajim/gtk/contact_info.py", - "gajim/gtk/contacts_flowbox.py", "gajim/gtk/contact_settings.py", - "gajim/gtk/conversation", + "gajim/gtk/contacts_flowbox.py", "gajim/gtk/dialogs.py", "gajim/gtk/emoji_data_gtk.py", "gajim/gtk/exception.py", @@ -79,11 +79,13 @@ "gajim/gtk/groupchat_details.py", "gajim/gtk/groupchat_invitation.py", "gajim/gtk/groupchat_inviter.py", - "gajim/gtk/groupchat_nick.py", + "gajim/gtk/groupchat_join.py", "gajim/gtk/groupchat_nick_completion.py", + "gajim/gtk/groupchat_nick.py", "gajim/gtk/groupchat_outcasts.py", "gajim/gtk/groupchat_settings.py", "gajim/gtk/groupchat_state.py", + "gajim/gtk/gstreamer.py", "gajim/gtk/history_export.py", "gajim/gtk/history_sync.py", "gajim/gtk/main_stack.py",
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/3c0101fd66eef81dbea65691646778a4...