Daniel Brötzmann pushed to branch chat_markers at gajim / gajim
Commits: 39805924 by lovetox at 2020-10-11T07:46:16+02:00 Preferences: Add setting to disable metacontacts
- - - - - 362c3d01 by lovetox at 2020-10-11T10:26:41+02:00 PEPConfig: Fix task callback
- - - - - bb740ca4 by lovetox at 2020-10-11T10:27:59+02:00 Remove unused import
- - - - - 159be3fa by André Apitzsch at 2020-10-11T12:28:05+02:00 Fix format of translatable strings
- - - - - fc8b5f28 by Yann Leboulanger at 2020-10-11T15:09:41+02:00 Add Chat Markers (XEP-0333)
- - - - - 01e60650 by wurstsalat at 2020-10-11T15:09:41+02:00 Improvements
- - - - -
27 changed files:
- gajim/chat_control.py - gajim/chat_control_base.py - gajim/common/const.py - gajim/common/events.py - gajim/common/jingle_rtp.py - gajim/common/modules/__init__.py - + gajim/common/modules/chat_markers.py - gajim/common/modules/message.py - gajim/common/modules/metacontacts.py - gajim/common/setting_values.py - gajim/common/zeroconf/connection_zeroconf.py - gajim/conversation_textview.py - gajim/data/style/gajim.css - gajim/groupchat_control.py - gajim/gtk/about.py - gajim/gtk/add_contact.py - gajim/gtk/bookmarks.py - gajim/gtk/filetransfer.py - gajim/gtk/groupchat_settings.py - gajim/gtk/history_sync.py - gajim/gtk/pep_config.py - gajim/gtk/preferences.py - gajim/gtk/roster_item_exchange.py - gajim/gtk/start_chat.py - gajim/gui_interface.py - gajim/gui_menu_builder.py - gajim/roster_window.py
Changes:
===================================== gajim/chat_control.py ===================================== @@ -123,7 +123,7 @@ class ChatControl(ChatControlBase):
# Menu for the HeaderBar self.control_menu = gui_menu_builder.get_singlechat_menu( - self.control_id, self.account, self.contact.jid) + self.control_id, self.account, self.contact.jid, self._type)
# Settings menu self.xml.settings_menu.set_menu_model(self.control_menu) @@ -228,6 +228,7 @@ class ChatControl(ChatControlBase): ('mam-decrypted-message-received', ged.GUI1, self._on_mam_decrypted_message_received), ('decrypted-message-received', ged.GUI1, self._on_decrypted_message_received), ('receipt-received', ged.GUI1, self._receipt_received), + ('displayed-received', ged.GUI1, self._displayed_received), ('message-error', ged.GUI1, self._on_message_error), ('zeroconf-error', ged.GUI1, self._on_zeroconf_error), ]) @@ -271,6 +272,15 @@ class ChatControl(ChatControlBase): act.connect('change-state', self._on_send_chatstate) self.parent_win.window.add_action(act)
+ marker = self.contact.settings.get('send_marker') + + act = Gio.SimpleAction.new_stateful( + f'send-marker-{self.control_id}', + None, + GLib.Variant.new_boolean(marker)) + act.connect('change-state', self._on_send_marker) + self.parent_win.window.add_action(act) + def update_actions(self): win = self.parent_win.window online = app.account_is_connected(self.account) @@ -363,6 +373,7 @@ class ChatControl(ChatControlBase): 'information-', 'start-call-', 'send-chatstate-', + 'send-marker', ] for action in actions: self.parent_win.window.remove_action(f'{action}{self.control_id}') @@ -435,6 +446,10 @@ class ChatControl(ChatControlBase): action.set_state(param) self.contact.settings.set('send_chatstate', param.get_string())
+ def _on_send_marker(self, action, param): + action.set_state(param) + self.contact.settings.set('send_marker', param.get_boolean()) + def subscribe_events(self): """ Register listeners to the events class @@ -643,6 +658,10 @@ class ChatControl(ChatControlBase): def _receipt_received(self, event): self.conv_textview.show_receipt(event.receipt_id)
+ @event_filter(['account', 'jid']) + def _displayed_received(self, event): + self.conv_textview.show_displayed(event.marker_id) + @event_filter(['account', 'jid']) def _on_zeroconf_error(self, event): self.add_status_message(event.message) @@ -1384,6 +1403,14 @@ class ChatControl(ChatControlBase): self.set_session(event.session) if message_ids: app.storage.archive.set_read_messages(message_ids) + + # XEP-0333 Send <displayed> marker + con = app.connections[self.account] + con.get_module('ChatMarkers').send_displayed_marker( + self.contact, + self.last_msg_id, + self._type) + self.last_msg_id = None app.events.remove_events(self.account, jid_with_resource, types=[str(self._type)])
===================================== gajim/chat_control_base.py ===================================== @@ -214,6 +214,9 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): self.received_history_pos = 0 self.orig_msg = None
+ # For XEP-0333 + self.last_msg_id = None + self.correcting = False self.last_sent_msg = None
@@ -1154,6 +1157,9 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): if restored: return
+ if message_id: + self.last_msg_id = message_id + if kind == 'incoming': if (not self._type.is_groupchat or self.contact.can_notify() or @@ -1198,7 +1204,9 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper):
event = event_type(text, subject, - self, msg_log_id, + self, + msg_log_id, + message_id=message_id, show_in_roster=show_in_roster, show_in_systray=show_in_systray) app.events.add_event(self.account, full_jid, event) @@ -1334,7 +1342,7 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): _('Privacy'), _('Warning'), _('If you send a file to <b>%s</b>, your real XMPP ' - 'address will be revealed.' % gc_contact.name), + 'address will be revealed.') % gc_contact.name, [DialogButton.make('Cancel'), DialogButton.make( 'OK', @@ -1350,7 +1358,7 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): jid = self.contact.jid if self.conv_textview.autoscroll: # we are at the end - type_ = ['printed_%s' % self._type] + type_ = [f'printed_{self._type}'] if self._type.is_groupchat: type_ = ['printed_gc_msg', 'printed_marked_gc_msg'] if not app.events.remove_events(self.account, @@ -1358,6 +1366,12 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): types=type_): # There were events to remove self.redraw_after_event_removed(jid) + # XEP-0333 Send <displayed> marker + con.get_module('ChatMarkers').send_displayed_marker( + self.contact, + self.last_msg_id, + self._type) + self.last_msg_id = None # send chatstate inactive to the one we're leaving # and active to the one we visit if self.msg_textview.has_text(): @@ -1386,20 +1400,27 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): types_list = [] if self._type.is_groupchat: types_list = ['printed_gc_msg', 'gc_msg', 'printed_marked_gc_msg'] - else: # Not a GC - types_list = ['printed_%s' % self._type, str(self._type)] + else: + types_list = [f'printed_{self._type}', str(self._type)]
if not app.events.get_events(self.account, jid, types_list): return if not self.parent_win: return - if self.parent_win.get_active_control() == self and \ - self.parent_win.window.is_active(): + if (self.parent_win.get_active_control() == self and + self.parent_win.window.is_active()): # we are at the end if not app.events.remove_events( self.account, jid, types=types_list): # There were events to remove self.redraw_after_event_removed(jid) + # XEP-0333 Send <displayed> tag + con = app.connections[self.account] + con.get_module('ChatMarkers').send_displayed_marker( + self.contact, + self.last_msg_id, + self._type) + self.last_msg_id = None
def _on_scrollbar_button_release(self, scrollbar, event): if event.get_button()[1] != 1: @@ -1464,6 +1485,8 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper): We just removed a 'printed_*' event, redraw contact in roster or gc_roster and titles in roster and msg_win """ + if not self.parent_win: # minimized groupchat + return self.parent_win.redraw_tab(self) self.parent_win.show_title() # TODO : get the contact and check get_show_in_roster()
===================================== gajim/common/const.py ===================================== @@ -970,6 +970,7 @@ COMMON_FEATURES = [ Namespace.SECLABEL, Namespace.CONFERENCE, Namespace.CORRECT, + Namespace.CHATMARKERS, Namespace.EME, Namespace.XHTML_IM, Namespace.HASHES_2,
===================================== gajim/common/events.py ===================================== @@ -91,13 +91,14 @@ class PmEvent(ChatEvent): class PrintedChatEvent(Event): type_ = 'printed_chat' def __init__(self, message, subject, control, msg_log_id, time_=None, - show_in_roster=False, show_in_systray=True): + message_id=None, show_in_roster=False, show_in_systray=True): Event.__init__(self, time_, show_in_roster=show_in_roster, show_in_systray=show_in_systray) self.message = message self.subject = subject self.control = control self.msg_log_id = msg_log_id + self.message_id = message_id
class PrintedGcMsgEvent(PrintedChatEvent): type_ = 'printed_gc_msg'
===================================== gajim/common/jingle_rtp.py ===================================== @@ -130,7 +130,7 @@ class JingleRTPContent(JingleContent): None, conn=self.session.connection, level='error', - pri_txt=_('%s configuration error' % text), + pri_txt=_('%s configuration error') % text, sec_txt=_('Couldn’t set up %(text)s. Check your ' 'configuration.\nPipeline:\n%(pipeline)s\n' 'Error:\n%(error)s') % {
===================================== gajim/common/modules/__init__.py ===================================== @@ -40,6 +40,7 @@ MODULES = [ 'bookmarks', 'caps', 'carbons', + 'chat_markers', 'chatstates', 'delimiter', 'discovery',
===================================== gajim/common/modules/chat_markers.py ===================================== @@ -0,0 +1,113 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see http://www.gnu.org/licenses/. + +# Chat Markers (XEP-0333) + +import nbxmpp +from nbxmpp.namespaces import Namespace +from nbxmpp.structs import StanzaHandler + +from gajim.common import app +from gajim.common.nec import NetworkEvent +from gajim.common.modules.base import BaseModule + + +class ChatMarkers(BaseModule): + + _nbxmpp_extends = 'ChatMarkers' + + def __init__(self, con): + BaseModule.__init__(self, con) + + self.handlers = [ + StanzaHandler(name='message', + callback=self._process_chat_marker, + ns=Namespace.CHATMARKERS, + priority=47), + ] + + def _process_chat_marker(self, _con, _stanza, properties): + if not properties.is_marker or not properties.marker.is_displayed: + return + + if properties.type.is_error: + return + + if properties.type.is_groupchat: + manager = self._con.get_module('MUC').get_manager() + muc_data = manager.get(properties.muc_jid) + if muc_data is None: + return + + if properties.muc_nickname != muc_data.nick: + return + + self._raise_event('read-state-sync', properties) + return + + if properties.is_carbon_message and properties.carbon.is_sent: + self._raise_event('read-state-sync', properties) + return + + if properties.is_mam_message: + if properties.from_.bareMatch(self._con.get_own_jid()): + return + + self._raise_event('displayed-received', properties) + + def _raise_event(self, name, properties): + self._log.info('%s: %s %s', + name, + properties.jid, + properties.marker.id) + + jid = properties.jid + if not properties.is_muc_pm and not properties.type.is_groupchat: + jid = properties.jid.bare + + app.nec.push_outgoing_event( + NetworkEvent(name, + account=self._account, + jid=jid, + properties=properties, + type=properties.type, + is_muc_pm=properties.is_muc_pm, + marker_id=properties.marker.id)) + + def _send_marker(self, contact, marker, id_, type_): + jid = contact.jid + if contact.is_pm_contact: + jid = app.get_jid_without_resource(contact.jid) + + if type_ in ('gc', 'pm'): + if not app.settings.get_group_chat_setting( + self._account, jid, 'send_marker'): + return + else: + if not app.settings.get_contact_setting( + self._account, jid, 'send_marker'): + return + + typ = 'groupchat' if type_ == 'gc' else 'chat' + message = nbxmpp.Message(to=contact.jid, typ=typ) + message.setMarker(marker, id_) + self._log.info('Send %s: %s', marker, contact.jid) + self._nbxmpp().send(message) + + def send_displayed_marker(self, contact, id_, type_): + self._send_marker(contact, 'displayed', id_, str(type_)) + + +def get_instance(*args, **kwargs): + return ChatMarkers(*args, **kwargs), 'ChatMarkers'
===================================== gajim/common/modules/message.py ===================================== @@ -305,6 +305,8 @@ class Message(BaseModule): stanza.setID(message.message_id) stanza.setOriginID(message.message_id)
+ stanza.setMarkable() + if message.label: stanza.addChild(node=message.label)
@@ -347,6 +349,10 @@ class Message(BaseModule): stanza.setTag('no-store', namespace=Namespace.MSG_HINTS)
+ # XEP-0333 + if message.message: + stanza.setMarkable() + # Add other nodes if message.nodes is not None: for node in message.nodes:
===================================== gajim/common/modules/metacontacts.py ===================================== @@ -30,6 +30,10 @@ class MetaContacts(BaseModule): self.available = False
def get_metacontacts(self): + if not app.settings.get('metacontacts_enabled'): + self._con.connect_machine() + return + self._log.info('Request') node = nbxmpp.Node('storage', attrs={'xmlns': 'storage:metacontacts'}) iq = nbxmpp.Iq('get', Namespace.PRIVATE, payload=node)
===================================== gajim/common/setting_values.py ===================================== @@ -139,6 +139,7 @@ APP_SETTINGS = { 'chat_merge_consecutive_nickname': False, 'chat_merge_consecutive_nickname_indent': ' ', 'ctrl_tab_go_to_next_composing': True, + 'metacontacts_enabled': True, 'confirm_metacontacts': '', 'confirm_block': '', 'enable_negative_priority': False, @@ -254,12 +255,15 @@ ACCOUNT_SETTINGS = { 'filetransfer_preference': 'httpupload', 'send_chatstate_default': 'composing_only', 'gc_send_chatstate_default': 'composing_only', + 'send_marker_default': True, + 'gc_send_marker_default': True, 'chat_history_max_age': -1, },
'contact': { 'speller_language': '', 'send_chatstate': HAS_ACCOUNT_DEFAULT, + 'send_marker': HAS_ACCOUNT_DEFAULT, 'encryption': '', },
@@ -271,6 +275,7 @@ ACCOUNT_SETTINGS = { 'minimize_on_autojoin': True, 'minimize_on_close': True, 'send_chatstate': HAS_ACCOUNT_DEFAULT, + 'send_marker': HAS_ACCOUNT_DEFAULT, 'encryption': '', 'sync_threshold': HAS_APP_DEFAULT, },
===================================== gajim/common/zeroconf/connection_zeroconf.py ===================================== @@ -261,7 +261,7 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf): if result is False: app.nec.push_incoming_event(ConnectionLostEvent(None, conn=self, title=_('Could not start local service'), - msg=_('Unable to bind to port %d.' % self.port))) + msg=_('Unable to bind to port %d.') % self.port)) else: # result is None app.nec.push_incoming_event(ConnectionLostEvent(None, conn=self, title=_('Could not start local service'),
===================================== gajim/conversation_textview.py ===================================== @@ -38,6 +38,7 @@ from gajim.common import app from gajim.common import helpers from gajim.common import i18n from gajim.common.i18n import _ +from gajim.common.i18n import Q_ from gajim.common.helpers import AdditionalDataDict from gajim.common.const import StyleAttr from gajim.common.const import Trust @@ -311,6 +312,12 @@ class ConversationTextview(GObject.GObject): return line.set_receipt()
+ def show_displayed(self, id_): + line = self._get_message_line(id_) + if line is None: + return + line.set_displayed() + def show_error(self, id_, error): line = self._get_message_line(id_) if line is None: @@ -1194,16 +1201,27 @@ class MessageLine: self.timestamp = timestamp self.start_mark = start_mark self._has_receipt = False + self._has_displayed = False self._message_icons = message_icons
@property def has_receipt(self): return self._has_receipt
+ @property + def has_displayed(self): + return self._has_displayed + def set_receipt(self): self._has_receipt = True + if self._has_displayed: + return self._message_icons.set_receipt_icon_visible(True)
+ def set_displayed(self): + self._has_displayed = True + self._message_icons.set_displayed_icon_visible(True) + def set_correction(self, tooltip): self._message_icons.set_correction_icon_visible(True) self._message_icons.set_correction_tooltip(tooltip) @@ -1222,12 +1240,12 @@ class MessageIcons(Gtk.Box): 'document-edit-symbolic', Gtk.IconSize.MENU) self._correction_image.set_no_show_all(True)
- self._receipt_image = Gtk.Image.new_from_icon_name( + self._marker_image = Gtk.Image.new_from_icon_name( 'emblem-ok-symbolic', Gtk.IconSize.MENU) - self._receipt_image.get_style_context().add_class( + self._marker_image.get_style_context().add_class( 'receipt-received-color') - self._receipt_image.set_tooltip_text(_('Received')) - self._receipt_image.set_no_show_all(True) + self._marker_image.set_tooltip_text(_('Received')) + self._marker_image.set_no_show_all(True)
self._error_image = Gtk.Image.new_from_icon_name( 'dialog-warning-symbolic', Gtk.IconSize.MENU) @@ -1235,14 +1253,22 @@ class MessageIcons(Gtk.Box): self._error_image.set_no_show_all(True)
self.add(self._correction_image) - self.add(self._receipt_image) + self.add(self._marker_image) self.add(self._error_image) self.show_all()
def set_receipt_icon_visible(self, visible): if not app.settings.get('positive_184_ack'): return - self._receipt_image.set_visible(visible) + self._marker_image.set_visible(visible) + + def set_displayed_icon_visible(self, visible): + self._marker_image.set_visible(visible) + self._marker_image.get_style_context().remove_class( + 'receipt-received-color') + self._marker_image.get_style_context().add_class( + 'displayed-received-color') + self._marker_image.set_tooltip_text(Q_('?Message state:Read'))
def set_correction_icon_visible(self, visible): self._correction_image.set_visible(visible)
===================================== gajim/data/style/gajim.css ===================================== @@ -295,6 +295,7 @@ button.flat.link { padding: 0; border: 0; }
/*Receipts*/ .receipt-received-color { color: rgb(75, 181, 67); } +.displayed-received-color { color: rgb(0, 141, 242); }
/*Dataforms*/ .field-fixed { font-size: 16px; font-weight: bold; padding-top:5px;}
===================================== gajim/groupchat_control.py ===================================== @@ -495,7 +495,7 @@ class GroupchatControl(ChatControlBase): con = app.connections[self.account] message_id = con.get_module('MUC').invite(self.room_jid, contact_jid) self.add_info_message( - _('%s has been invited to this group chat' % contact_jid), + _('%s has been invited to this group chat') % contact_jid, message_id=message_id)
def _on_destroy_room(self, _action, _param): @@ -591,7 +591,7 @@ class GroupchatControl(ChatControlBase): try: task.finish() except Exception as error: - self.add_info_message(_('Avatar upload failed: %s' % error)) + self.add_info_message(_('Avatar upload failed: %s') % error)
else: self.add_info_message(_('Avatar upload successful')) @@ -612,7 +612,7 @@ class GroupchatControl(ChatControlBase): def _on_kick(self, _action, param): nick = param.get_string() self._kick_nick = nick - self.xml.kick_label.set_text(_('Kick %s' % nick)) + self.xml.kick_label.set_text(_('Kick %s') % nick) self.xml.kick_reason_entry.grab_focus() self.xml.kick_participant_button.grab_default() self._show_page('kick') @@ -621,7 +621,7 @@ class GroupchatControl(ChatControlBase): jid = param.get_string() self._ban_jid = jid nick = app.get_nick_from_jid(jid) - self.xml.ban_label.set_text(_('Ban %s' % nick)) + self.xml.ban_label.set_text(_('Ban %s') % nick) self.xml.ban_reason_entry.grab_focus() self.xml.ban_participant_button.grab_default() self._show_page('ban') @@ -656,6 +656,17 @@ class GroupchatControl(ChatControlBase): self.update_actions() self.set_lock_image() self.draw_banner_text() + type_ = ['printed_gc_msg', 'printed_marked_gc_msg'] + if not app.events.remove_events(self.account, + self.get_full_jid(), + types=type_): + # XEP-0333 Send <displayed> marker + con = app.connections[self.account] + con.get_module('ChatMarkers').send_displayed_marker( + self.contact, + self.last_msg_id, + self._type) + self.last_msg_id = None
def _on_roster_row_activated(self, _roster, nick): self._start_private_message(nick) @@ -799,12 +810,12 @@ class GroupchatControl(ChatControlBase): tim=event.properties.timestamp, displaymarking=event.displaymarking, correct_id=event.correct_id, - message_id=event.properties.id, + message_id=event.properties.stanza_id.id, additional_data=event.additional_data) event.needs_highlight = self.needs_visual_notification(event.msgtxt)
def on_private_message(self, nick, sent, msg, tim, session, additional_data, - msg_log_id=None, displaymarking=None): + message_id, msg_log_id=None, displaymarking=None): # Do we have a queue? fjid = self.room_jid + '/' + nick
@@ -817,7 +828,9 @@ class GroupchatControl(ChatControlBase): session=session, displaymarking=displaymarking, sent_forwarded=sent, - additional_data=additional_data) + additional_data=additional_data, + message_id=message_id) + app.events.add_event(self.account, fjid, event)
if allow_popup_window(self.account): @@ -1055,6 +1068,7 @@ class GroupchatControl(ChatControlBase): event.properties.timestamp, self.session, event.additional_data, + event.properties.id, msg_log_id=event.msg_log_id, displaymarking=event.displaymarking)
===================================== gajim/gtk/about.py ===================================== @@ -49,8 +49,8 @@ class AboutDialog(Gtk.AboutDialog):
comments = [] comments.append(_('A GTK XMPP client')) - comments.append(_('GTK Version: %s' % gtk_ver)) - comments.append(_('GLib Version: %s' % glib_ver)) + comments.append(_('GTK Version: %s') % gtk_ver) + comments.append(_('GLib Version: %s') % glib_ver) comments.append(_('PyGObject Version: %s') % gobject_ver) comments.append(_('python-nbxmpp Version: %s') % nbxmpp.__version__)
===================================== gajim/gtk/add_contact.py ===================================== @@ -258,7 +258,7 @@ class AddNewContactWindow(Gtk.ApplicationWindow, EventHelper): if not jid: ErrorDialog( _('%s Missing') % self.uid_label.get_text(), - _('You must supply the %s of the new contact.' % + (_('You must supply the %s of the new contact.') % self.uid_label.get_text()) ) return
===================================== gajim/gtk/bookmarks.py ===================================== @@ -45,7 +45,7 @@ class Bookmarks(Gtk.ApplicationWindow): self.set_application(app.app) self.set_position(Gtk.WindowPosition.CENTER) self.set_show_menubar(False) - self.set_title(_('Bookmarks for %s' % app.get_account_label(account))) + self.set_title(_('Bookmarks for %s') % app.get_account_label(account)) self.set_default_size(700, 500)
self.account = account
===================================== gajim/gtk/filetransfer.py ===================================== @@ -396,7 +396,7 @@ class FileTransfersWindow: file_name = GLib.markup_escape_text( os.path.basename(file_path)) ErrorDialog( - _('Cannot overwrite existing file '%s'' % file_name), + _('Cannot overwrite existing file '%s'') % file_name, _('A file with this name already exists and you do ' 'not have permission to overwrite it.')) return
===================================== gajim/gtk/groupchat_settings.py ===================================== @@ -71,6 +71,12 @@ class GroupChatSettings(SettingsBox): 'send_chatstate', props={'entries': chat_state}),
+ Setting(SettingKind.SWITCH, + _('Send Chat Markers'), + SettingType.GROUP_CHAT, + 'send_marker', + desc=_('Let others know if you read up to this point')), + Setting(SettingKind.POPOVER, _('Sync Threshold'), SettingType.GROUP_CHAT,
===================================== gajim/gtk/history_sync.py ===================================== @@ -248,11 +248,11 @@ class DownloadHistoryPage(Gtk.Box): self.received += 1 if self.count: self.progress.set_fraction(self.received / self.count) - self.progress.set_text(_('%(received)s of %(max)s' % { - 'received': self.received, 'max': self.count})) + self.progress.set_text(_('%(received)s of %(max)s') % { + 'received': self.received, 'max': self.count}) else: self.progress.pulse() - self.progress.set_text(_('Downloaded %s messages' % self.received)) + self.progress.set_text(_('Downloaded %s messages') % self.received)
def finished(self): self.progress.set_fraction(1) @@ -273,7 +273,7 @@ class SummaryPage(Gtk.Box): def finished(self): received = self.assistant.download_history.received self.label.set_text(_('Finished synchronising chat history:\n' - '%s messages downloaded' % received)) + '%s messages downloaded') % received)
def nothing_to_do(self): self.label.set_text(_('Gajim is fully synchronised with the archive.'))
===================================== gajim/gtk/pep_config.py ===================================== @@ -14,7 +14,7 @@
from gi.repository import Gdk from gi.repository import Gtk -from nbxmpp.util import is_error_result +from nbxmpp.errors import StanzaError
from gajim.common import app from gajim.common import ged @@ -85,9 +85,11 @@ class PEPConfig(Gtk.ApplicationWindow, EventHelper): self._con.get_module('Discovery').disco_items( jid, callback=self._items_received)
- def _items_received(self, result): - if is_error_result(result): - ErrorDialog('Error', to_user_string(result)) + def _items_received(self, task): + try: + result = task.finish() + except StanzaError as error: + ErrorDialog('Error', to_user_string(error)) return
jid = result.jid.bare
===================================== gajim/gtk/preferences.py ===================================== @@ -249,6 +249,11 @@ class ContactList(PreferenceBox): 'mergeaccounts', callback=self._on_merge_accounts),
+ Setting(SettingKind.SWITCH, + _('Enable Metacontacts'), + SettingType.CONFIG, + 'metacontacts_enabled'), + Setting(SettingKind.SWITCH, _('Show Avatars'), SettingType.CONFIG,
===================================== gajim/gtk/roster_item_exchange.py ===================================== @@ -194,7 +194,7 @@ class RosterItemExchangeWindow(Gtk.ApplicationWindow): count += 1 # It is selected message = _('%s suggested me to add you to my ' - 'contact list.' % self.jid_from) + 'contact list.') % self.jid_from # Keep same groups and same nickname groups = model[iter_][3].split(', ') if groups == ['']:
===================================== gajim/gtk/start_chat.py ===================================== @@ -652,7 +652,7 @@ class ContactRow(Gtk.ListBoxRow): if show_account: account_badge = generate_account_badge(account) account_badge.set_tooltip_text( - _('Account: %s' % self.account_label)) + _('Account: %s') % self.account_label) account_badge.set_halign(Gtk.Align.END) account_badge.set_valign(Gtk.Align.START) account_badge.set_hexpand(True)
===================================== gajim/gui_interface.py ===================================== @@ -242,6 +242,37 @@ class Interface: if ctrl and ctrl.session and len(obj.contact_list) > 1: ctrl.remove_session(ctrl.session)
+ def handle_event_read_state_sync(self, event): + if event.type.is_groupchat: + control = app.get_groupchat_control( + event.account, event.jid.bare) + if control is None: + log.warning('Groupchat control not found') + return + + jid = event.jid.bare + types = ['printed_gc_msg', 'printed_marked_gc_msg'] + + else: + types = ['chat', 'pm', 'printed_chat', 'printed_pm'] + jid = event.jid + + control = app.interface.msg_win_mgr.get_control(jid, event.account) + + # Compare with control.last_msg_id. + events_ = app.events.get_events(event.account, jid, types) + if not events_: + log.warning('No Events') + return + + if events_[-1].message_id != event.marker_id: + return + + if not app.events.remove_events(event.account, jid, types=types): + # There were events to remove + if control is not None: + control.redraw_after_event_removed(event.jid) + @staticmethod def handle_event_msgsent(obj): if not obj.play_sound: @@ -926,7 +957,7 @@ class Interface: if helpers.allow_showing_notification(account): heading = _('Incoming Call') contact = app.get_name_from_jid(account, event.jid) - text = _('%s is calling' % contact) + text = _('%s is calling') % contact app.notification.popup( heading, event.fjid, @@ -1060,6 +1091,7 @@ class Interface: 'unsubscribed-presence-received': [ self.handle_event_unsubscribed_presence], 'zeroconf-name-conflict': [self.handle_event_zc_name_conflict], + 'read-state-sync': [self.handle_event_read_state_sync], }
def register_core_handlers(self): @@ -1983,7 +2015,7 @@ class Interface: _('Update Available'), _('Gajim Update Available'), _('There is an update available for Gajim ' - '(latest version: %s)' % str(latest_version)), + '(latest version: %s)') % str(latest_version), _('_Do not show again'), [DialogButton.make('Cancel', text=_('_Later'),
===================================== gajim/gui_menu_builder.py ===================================== @@ -511,12 +511,13 @@ def get_transport_menu(contact, account): return menu
-def get_singlechat_menu(control_id, account, jid): +def get_singlechat_menu(control_id, account, jid, type_): singlechat_menu = [ (_('Send File'), [ ('win.send-file-httpupload-', _('Upload File…')), ('win.send-file-jingle-', _('Send File Directly…')), ]), + ('win.send-marker-', _('Send Read Markers')), (_('Send Chatstate'), ['chatstate']), ('win.invite-contacts-', _('Invite Contacts…')), ('win.add-to-roster-', _('Add to Contact List…')), @@ -545,6 +546,9 @@ def get_singlechat_menu(control_id, account, jid): for item in preset: if isinstance(item[1], str): action_name, label = item + if action_name == 'win.send-marker-' and type_ == 'pm': + continue + if action_name == 'app.browse-history': menuitem = Gio.MenuItem.new(label, action_name) dict_ = {'account': GLib.Variant('s', account),
===================================== gajim/roster_window.py ===================================== @@ -66,7 +66,6 @@ from gajim.gtk.dialogs import ConfirmationDialog from gajim.gtk.dialogs import ConfirmationCheckDialog from gajim.gtk.dialogs import ErrorDialog from gajim.gtk.dialogs import InputDialog -from gajim.gtk.dialogs import WarningDialog from gajim.gtk.dialogs import InformationDialog from gajim.gtk.single_message import SingleMessageWindow from gajim.gtk.add_contact import AddNewContactWindow @@ -3664,11 +3663,7 @@ class RosterWindow: con_dest = app.connections[account_dest] if (not con_source.get_module('MetaContacts').available or not con_dest.get_module('MetaContacts').available): - WarningDialog(_('Metacontacts storage not supported by ' - 'your server'), - _('Your server does not support storing metacontacts ' - 'information. So this information will not be saved on next ' - 'reconnection.')) + return
def merge_contacts(is_checked=None): contacts = 0
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/082588d1bab56882fb48d1648f2182ce...