Philipp Hörist pushed to branch master at gajim / gajim
Commits:
970d6f8c by Philipp Hörist at 2017-10-26T22:52:20+02:00
New style for ChatControl
- Move ActionBar into HeaderMenu
- Make Design of ChatControl look cleaner
- Hide the Roster in Groupchats per default
- Add Button to hide/show Roster in Groupchats
- Move Groupchat topic into popover
- Display Avatars on the right side of the ChatControl and status on the
left
- Add a default Avatar for contacts that have none
- - - - -
3c103315 by Philipp Hörist at 2017-10-26T22:52:27+02:00
Refactor Speller
- - - - -
347f0a8a by Philipp Hörist at 2017-10-27T00:21:01+02:00
Merge branch 'newdesign' into 'master'
New Style for Chat Windows
See merge request gajim/gajim!144
- - - - -
23 changed files:
- gajim/chat_control.py
- gajim/chat_control_base.py
- gajim/command_system/implementation/standard.py
- gajim/common/config.py
- gajim/common/connection.py
- gajim/common/const.py
- gajim/config.py
- gajim/data/gui/chat_control.ui
- gajim/data/gui/groupchat_control.ui
- gajim/data/gui/message_window.ui
- gajim/data/gui/preferences_window.ui
- gajim/data/gui/roster_window.ui
- gajim/data/style/gajim.css
- gajim/dialogs.py
- gajim/emoticons.py
- gajim/gajim.py
- gajim/groupchat_control.py
- gajim/gtkgui_helpers.py
- gajim/gtkspell.py
- gajim/gui_interface.py
- gajim/gui_menu_builder.py
- gajim/message_control.py
- gajim/message_textview.py
The diff was not included because it is too large.
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/398ad0eed82278da339b4061547c4b159…
---
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/398ad0eed82278da339b4061547c4b159…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / python-nbxmpp
Commits:
3275b1fd by Philipp Hörist at 2017-10-22T22:05:27+02:00
Update .gitignore
- - - - -
3417ccbf by Philipp Hörist at 2017-10-22T22:07:08+02:00
Add getStanzaIDAttrs() method
- - - - -
2 changed files:
- .gitignore
- nbxmpp/protocol.py
Changes:
=====================================
.gitignore
=====================================
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-build/
\ No newline at end of file
+build/
+__pycache__/
=====================================
nbxmpp/protocol.py
=====================================
--- a/nbxmpp/protocol.py
+++ b/nbxmpp/protocol.py
@@ -1043,6 +1043,16 @@ class Message(Protocol):
"""
return self.getTagAttr('origin-id', namespace=NS_SID, attr='id')
+ def getStanzaIDAttrs(self):
+ """
+ Return the stanza-id attributes of the message
+ """
+ try:
+ attrs = self.getTag('stanza-id', namespace=NS_SID).getAttrs()
+ except Exception:
+ return None, None
+ return attrs['id'], attrs['by']
+
def setBody(self, val):
"""
Set the text of the message"""
View it on GitLab: https://dev.gajim.org/gajim/python-nbxmpp/compare/b9fa37be8d6bcb7296926a755…
---
View it on GitLab: https://dev.gajim.org/gajim/python-nbxmpp/compare/b9fa37be8d6bcb7296926a755…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
0aa07522 by Philipp Hörist at 2017-10-22T20:12:21+02:00
Logger: Refactor get_jid_id()
- Cache jid_id so we save on DB querys
- - - - -
1c736922 by Philipp Hörist at 2017-10-22T20:12:25+02:00
MUC: Save last message time after each message
- - - - -
accef11c by Philipp Hörist at 2017-10-22T20:12:26+02:00
Logger: Get rid of old jids list
Use _jid_ids dict instead
- - - - -
b95759df by Philipp Hörist at 2017-10-22T20:12:26+02:00
Logger: Refactor insert_jid()
- make it use get_jid_id() and return the jid_id
- - - - -
398ad0ee by Philipp Hörist at 2017-10-22T20:12:26+02:00
Fix removing avatar sha from DB
the account name was passed instead of the account jid
- - - - -
5 changed files:
- gajim/common/connection.py
- gajim/common/connection_handlers.py
- gajim/common/connection_handlers_events.py
- gajim/common/logger.py
- gajim/groupchat_control.py
Changes:
=====================================
gajim/common/connection.py
=====================================
--- a/gajim/common/connection.py
+++ b/gajim/common/connection.py
@@ -665,7 +665,6 @@ class Connection(CommonConnection, ConnectionHandlers):
self.annotations = {}
self.last_io = app.idlequeue.current_time()
self.last_sent = []
- self.last_history_time = {}
self.password = passwords.get_password(name)
self.music_track_info = 0
@@ -2576,20 +2575,11 @@ class Connection(CommonConnection, ConnectionHandlers):
# Never join a room when invisible
return
- # last date/time in history to avoid duplicate
- if room_jid not in self.last_history_time:
- # Not in memory, get it from DB
- last_log = 0
- if app.config.should_log(self.name, room_jid):
- # Check time first in the FAST table
- last_log = app.logger.get_room_last_message_time(
- self.name, room_jid)
- if not last_log:
- last_log = 0
-
- # Create self.last_history_time[room_jid] even if not logging,
- # could be used in connection_handlers
- self.last_history_time[room_jid] = last_log
+ # Check time first in the FAST table
+ last_date = app.logger.get_room_last_message_time(
+ self.name, room_jid)
+ if not last_date:
+ last_date = 0
p = nbxmpp.Presence(to='%s/%s' % (room_jid, nick),
show=show, status=self.status)
@@ -2608,7 +2598,6 @@ class Connection(CommonConnection, ConnectionHandlers):
'muc_restore_timeout')
if timeout is None or timeout == -2:
timeout = app.config.get('muc_restore_timeout')
- last_date = self.last_history_time[room_jid]
if last_date == 0 and timeout >= 0:
last_date = time.time() - timeout * 60
elif not rejoin and timeout >= 0:
@@ -2736,16 +2725,6 @@ class Connection(CommonConnection, ConnectionHandlers):
# disconnect from jabber server
self.connection.send(p)
- def gc_got_disconnected(self, room_jid):
- """
- A groupchat got disconnected. This can be or purpose or not
-
- Save the time we had last message to avoid duplicate logs AND be faster
- than get that date from DB. Save time that we have in mem in a small
- table (with fast access)
- """
- app.logger.set_room_last_message_time(room_jid, self.last_history_time[room_jid])
-
def gc_set_role(self, room_jid, nick, role, reason=''):
"""
Role is for all the life of the room so it's based on nick
=====================================
gajim/common/connection_handlers.py
=====================================
--- a/gajim/common/connection_handlers.py
+++ b/gajim/common/connection_handlers.py
@@ -294,7 +294,8 @@ class ConnectionVcard:
# Remove avatar
app.log('avatar').info('Remove: %s', obj.jid)
app.contacts.set_avatar(self.name, obj.jid, None)
- app.logger.set_avatar_sha(self.name, obj.jid, None)
+ own_jid = self.get_own_jid().getStripped()
+ app.logger.set_avatar_sha(own_jid, obj.jid, None)
app.interface.update_avatar(self.name, obj.jid)
else:
app.log('avatar').info(
@@ -1017,9 +1018,8 @@ class ConnectionHandlersBase:
def _nec_gc_message_received(self, obj):
if obj.conn.name != self.name:
return
- if app.config.should_log(obj.conn.name, obj.jid) and not \
- obj.timestamp < obj.conn.last_history_time[obj.jid] and obj.msgtxt and \
- obj.nick:
+ if (app.config.should_log(obj.conn.name, obj.jid) and
+ obj.msgtxt and obj.nick):
# if not obj.nick, it means message comes from room itself
# usually it hold description and can be send at each connection
# so don't store it in logs
@@ -1029,10 +1029,7 @@ class ConnectionHandlersBase:
message=obj.msgtxt,
contact_name=obj.nick,
additional_data=obj.additional_data)
- # store in memory time of last message logged.
- # this will also be saved in rooms_last_message_time table
- # when we quit this muc
- obj.conn.last_history_time[obj.jid] = obj.timestamp
+ app.logger.set_room_last_message_time(obj.room_jid, obj.timestamp)
# process and dispatch an error message
def dispatch_error_message(self, msg, msgtxt, session, frm, tim):
=====================================
gajim/common/connection_handlers_events.py
=====================================
--- a/gajim/common/connection_handlers_events.py
+++ b/gajim/common/connection_handlers_events.py
@@ -1564,9 +1564,6 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
# Ignore message from room in which we are not
self.displaymarking = seclabel.getTag('displaymarking')
- if self.jid not in self.conn.last_history_time:
- return
-
self.captcha_form = None
captcha_tag = self.stanza.getTag('captcha', namespace=nbxmpp.NS_CAPTCHA)
if captcha_tag:
=====================================
gajim/common/logger.py
=====================================
--- a/gajim/common/logger.py
+++ b/gajim/common/logger.py
@@ -107,7 +107,7 @@ class SubscriptionConstant(IntEnum):
class Logger:
def __init__(self):
- self.jids_already_in = [] # holds jids that we already have in DB
+ self._jid_ids = {}
self.con = None
self.commit_timout_id = None
@@ -188,7 +188,7 @@ class Logger:
def init_vars(self):
self.open_db()
- self.get_jids_already_in_db()
+ self.get_jid_ids_from_db()
@staticmethod
def _get_timeout():
@@ -226,22 +226,17 @@ class Logger:
self.cur.execute(sql_to_commit)
self._timeout_commit()
- def get_jids_already_in_db(self):
- try:
- self.cur.execute('SELECT jid FROM jids')
- rows = self.cur.fetchall()
- except sqlite.DatabaseError:
- raise exceptions.DatabaseMalformed(LOG_DB_PATH)
- self.jids_already_in = []
+ def get_jid_ids_from_db(self):
+ """
+ Load all jid/jid_id tuples into a dict for faster access
+ """
+ rows = self.con.execute(
+ 'SELECT jid_id, jid, type FROM jids').fetchall()
for row in rows:
- if not row.jid:
- # malformed jid, ignore line
- pass
- else:
- self.jids_already_in.append(row.jid)
+ self._jid_ids[row.jid] = row
def get_jids_in_db(self):
- return self.jids_already_in
+ return self._jid_ids.keys()
def jid_is_from_pm(self, jid):
"""
@@ -288,42 +283,41 @@ class Logger:
return [user['jid'] for user in family]
return [jid]
- def get_jid_id(self, jid, typestr=None):
- """
- jids table has jid and jid_id logs table has log_id, jid_id,
- contact_name, time, kind, show, message so to ask logs we need jid_id
- that matches our jid in jids table this method wants jid and returns the
- jid_id for later sql-ing on logs typestr can be 'ROOM' or anything else
- depending on the type of JID and is only needed to be specified when the
- JID is new in DB
- """
- if jid.find('/') != -1: # if it has a /
- jid_is_from_pm = self.jid_is_from_pm(jid)
- if not jid_is_from_pm: # it's normal jid with resource
- jid = jid.split('/', 1)[0] # remove the resource
- if jid in self.jids_already_in: # we already have jids in DB
- self.cur.execute('SELECT jid_id FROM jids WHERE jid=?', [jid])
- row = self.cur.fetchone()
- if row:
- return row.jid_id
- # oh! a new jid :), we add it now
- if typestr == 'ROOM':
- typ = JIDConstant.ROOM_TYPE
- else:
- typ = JIDConstant.NORMAL_TYPE
- try:
- self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid,
- typ))
- self.con.commit()
- except sqlite.IntegrityError:
- # Jid already in DB, maybe added by another instance. re-read DB
- self.get_jids_already_in_db()
- return self.get_jid_id(jid, typestr)
- except sqlite.OperationalError as e:
- raise exceptions.PysqliteOperationalError(str(e))
- jid_id = self.cur.lastrowid
- self.jids_already_in.append(jid)
- return jid_id
+ def get_jid_id(self, jid, kind=None, type_=None):
+ """
+ Get the jid id from a jid.
+ In case the jid id is not found create a new one.
+
+ :param jid: The JID
+
+ :param type_: The JIDConstant type
+
+ return the jid id
+ """
+
+ if kind in (KindConstant.GC_MSG, KindConstant.GCSTATUS):
+ type_ = JIDConstant.ROOM_TYPE
+
+ result = self._jid_ids.get(jid, None)
+ if result is not None:
+ return result.jid_id
+
+ sql = 'SELECT jid_id, jid, type FROM jids WHERE jid = ?'
+ row = self.con.execute(sql, [jid]).fetchone()
+ if row is not None:
+ self._jid_ids[jid] = row
+ return row.jid_id
+
+ if type_ is None:
+ raise ValueError(
+ 'Unable to insert new JID because type is missing')
+
+ sql = 'INSERT INTO jids (jid, type) VALUES (?, ?)'
+ lastrowid = self.con.execute(sql, (jid, type_)).lastrowid
+ Row = namedtuple('Row', 'jid_id jid type')
+ self._jid_ids[jid] = Row(lastrowid, jid, type_)
+ self._timeout_commit()
+ return lastrowid
def convert_kind_values_to_db_api_values(self, kind):
"""
@@ -758,13 +752,16 @@ class Logger:
:param jid: The jid
:param timestamp: The timestamp in epoch
+
"""
- self.insert_jid(jid, type_=JIDConstant.ROOM_TYPE)
+ jid_id = self.get_jid_id(jid, type_=JIDConstant.ROOM_TYPE)
sql = '''REPLACE INTO rooms_last_message_time
- VALUES ((SELECT jid_id FROM jids WHERE jid = ?), ?)'''
+ VALUES (:jid_id, COALESCE(
+ (SELECT time FROM rooms_last_message_time
+ WHERE jid_id = :jid_id AND time >= :time), :time))'''
- self.con.execute(sql, (jid, timestamp))
+ self.con.execute(sql, {"jid_id": jid_id, "time": timestamp})
self._timeout_commit()
def save_transport_type(self, jid, type_):
@@ -961,7 +958,7 @@ class Logger:
try:
account_jid_id = self.get_jid_id(account_jid)
- jid_id = self.get_jid_id(jid)
+ jid_id = self.get_jid_id(jid, type_=JIDConstant.NORMAL_TYPE)
except exceptions.PysqliteOperationalError as e:
raise exceptions.PysqliteOperationalError(str(e))
@@ -993,7 +990,7 @@ class Logger:
Return the accound_jid roster in NonBlockingRoster format
"""
data = {}
- account_jid_id = self.get_jid_id(account_jid)
+ account_jid_id = self.get_jid_id(account_jid, type_=JIDConstant.NORMAL_TYPE)
# First we fill data with roster_entry informations
self.cur.execute('''
@@ -1085,6 +1082,7 @@ class Logger:
def insert_jid(self, jid, kind=None, type_=JIDConstant.NORMAL_TYPE):
"""
Insert a new jid into the `jids` table.
+ This is an alias of get_jid_id() for better readablility.
:param jid: The jid as string
@@ -1092,14 +1090,7 @@ class Logger:
:param type_: A JIDConstant
"""
- if jid in self.jids_already_in:
- return
- if kind in (KindConstant.GC_MSG, KindConstant.GCSTATUS):
- type_ = JIDConstant.ROOM_TYPE
- sql = 'INSERT OR IGNORE INTO jids (jid, type) VALUES (?, ?)'
- self.con.execute(sql, (jid, type_))
- self.jids_already_in.append(jid)
- self._timeout_commit()
+ return self.get_jid_id(jid, kind, type_)
def insert_into_logs(self, jid, time_, kind, unread=True, **kwargs):
"""
@@ -1117,15 +1108,15 @@ class Logger:
:param kwargs: Every additional named argument must correspond to
a field in the `logs` table
"""
- self.insert_jid(jid, kind=kind)
+ jid_id = self.get_jid_id(jid, kind=kind)
sql = '''
INSERT INTO logs (jid_id, time, kind, {columns})
- VALUES ((SELECT jid_id FROM jids WHERE jid = ?), ?, ?, {values})
+ VALUES (?, ?, ?, {values})
'''.format(columns=', '.join(kwargs.keys()),
values=', '.join('?' * len(kwargs)))
- lastrowid = self.con.execute(sql, (jid, time_, kind) + tuple(kwargs.values())).lastrowid
+ lastrowid = self.con.execute(sql, (jid_id, time_, kind) + tuple(kwargs.values())).lastrowid
if unread and kind == KindConstant.CHAT_MSG_RECV:
sql = '''INSERT INTO unread_messages (message_id, jid_id)
@@ -1149,7 +1140,7 @@ class Logger:
"""
account_jid_id = self.get_jid_id(account_jid)
- jid_id = self.get_jid_id(jid)
+ jid_id = self.get_jid_id(jid, type_=JIDConstant.NORMAL_TYPE)
sql = '''
UPDATE roster_entry SET avatar_sha = ?
=====================================
gajim/groupchat_control.py
=====================================
--- a/gajim/groupchat_control.py
+++ b/gajim/groupchat_control.py
@@ -1499,8 +1499,6 @@ class GroupchatControl(ChatControlBase):
app.contacts.remove_gc_contact(self.account, gc_contact)
app.gc_connected[self.account][self.room_jid] = False
ChatControlBase.got_disconnected(self)
- # Tell connection to note the date we disconnect to avoid duplicate logs
- app.connections[self.account].gc_got_disconnected(self.room_jid)
# We don't redraw the whole banner here, because only icon change
self._update_banner_state_image()
if self.parent_win:
@@ -2141,10 +2139,6 @@ class GroupchatControl(ChatControlBase):
if self.room_jid in app.gc_connected[self.account] and \
app.gc_connected[self.account][self.room_jid]:
- # Tell connection to note the date we disconnect to avoid duplicate
- # logs. We do it only when connected because if connection was lost
- # there may be new messages since disconnection.
- app.connections[self.account].gc_got_disconnected(self.room_jid)
app.connections[self.account].send_gc_status(self.nick,
self.room_jid, show='offline', status=status)
nick_list = app.contacts.get_nick_list(self.account, self.room_jid)
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/9e39287d8e2492e5d30e6b6692a25796e…
---
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/9e39287d8e2492e5d30e6b6692a25796e…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
9e39287d by Philipp Hörist at 2017-10-20T20:51:40+02:00
ProfileWindow: Fix typo
- - - - -
1 changed file:
- gajim/profile_window.py
Changes:
=====================================
gajim/profile_window.py
=====================================
--- a/gajim/profile_window.py
+++ b/gajim/profile_window.py
@@ -253,7 +253,7 @@ class ProfileWindow:
else:
if i == 'DESC':
self.xml.get_object('DESC_textview').get_buffer().set_text(
- vcard_[i], len(vcard[i].encode('utf-8')))
+ vcard_[i], len(vcard_[i].encode('utf-8')))
else:
self.set_value(i + '_entry', vcard_[i])
if self.update_progressbar_timeout_id is not None:
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/9e39287d8e2492e5d30e6b6692a25796e8…
---
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/9e39287d8e2492e5d30e6b6692a25796e8…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
021d5c70 by Philipp Hörist at 2017-10-20T19:15:43+02:00
Add libwebp to Windows build
A special Client only publishes Images in this Format
- - - - -
1 changed file:
- win/_base.sh
Changes:
=====================================
win/_base.sh
=====================================
--- a/win/_base.sh
+++ b/win/_base.sh
@@ -73,6 +73,7 @@ function install_deps {
mingw-w64-"${ARCH}"-"${PYTHON_ID}"-pip \
mingw-w64-"${ARCH}"-gstreamer \
mingw-w64-"${ARCH}"-adwaita-icon-theme \
+ mingw-w64-"${ARCH}"-libwebp \
mingw-w64-"${ARCH}"-sqlite3 \
mingw-w64-"${ARCH}"-goocanvas
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/021d5c709e4d1b3e6d24982a50ef6d79f3…
---
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/021d5c709e4d1b3e6d24982a50ef6d79f3…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
456d7f87 by Philipp Hörist at 2017-10-20T19:09:41+02:00
Catch OSError when converting with Pillow
- - - - -
1 changed file:
- gajim/gui_interface.py
Changes:
=====================================
gajim/gui_interface.py
=====================================
--- a/gajim/gui_interface.py
+++ b/gajim/gui_interface.py
@@ -2399,7 +2399,7 @@ class Interface:
'avatar image using pillow', filename)
try:
avatar = Image.open(path).convert("RGBA")
- except NameError:
+ except (NameError, OSError):
app.log('avatar').warning('Pillow convert failed: %s', filename)
app.log('avatar').debug('Error', exc_info=True)
return
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/456d7f87ffa0f1d9c3bf3fa3ca5274a189…
---
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/456d7f87ffa0f1d9c3bf3fa3ca5274a189…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
2c7861bc by Philipp Hörist at 2017-10-19T15:54:49+02:00
Groupchat: Dont update avatar on offline presence
- - - - -
1 changed file:
- gajim/common/connection_handlers.py
Changes:
=====================================
gajim/common/connection_handlers.py
=====================================
--- a/gajim/common/connection_handlers.py
+++ b/gajim/common/connection_handlers.py
@@ -313,6 +313,9 @@ class ConnectionVcard:
if server.startswith('irc') or obj.avatar_sha is None:
return
+ if obj.show == 'offline':
+ return
+
gc_contact = app.contacts.get_gc_contact(
self.name, obj.room_jid, obj.nick)
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/2c7861bc1de8f8b0547e02b4ca8fd167c1…
---
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/2c7861bc1de8f8b0547e02b4ca8fd167c1…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
1f0cc2b6 by Philipp Hörist at 2017-10-17T16:28:56+02:00
Fix bindtextdomain not available on MacOS
- - - - -
0d16ef32 by Philipp Hörist at 2017-10-17T18:36:02+02:00
Fix StatusIcon on MacOS
- - - - -
2 changed files:
- gajim/common/i18n.py
- gajim/statusicon.py
Changes:
=====================================
gajim/common/i18n.py
=====================================
--- a/gajim/common/i18n.py
+++ b/gajim/common/i18n.py
@@ -76,7 +76,10 @@ else:
if base is None or base[0] != '/':
base = os.path.expanduser('~/.local/share')
localedir = os.path.join(base, "locale")
- locale.bindtextdomain(APP, localedir)
+
+ if hasattr(locale, 'bindtextdomain'):
+ locale.bindtextdomain(APP, localedir)
+
gettext.install(APP, localedir)
if gettext._translations:
=====================================
gajim/statusicon.py
=====================================
--- a/gajim/statusicon.py
+++ b/gajim/statusicon.py
@@ -26,6 +26,7 @@
from gi.repository import Gtk
from gi.repository import Gdk
import os
+import sys
from gajim import dialogs
from gajim import config
@@ -85,6 +86,8 @@ class StatusIcon:
if not self.status_icon:
self.status_icon = Gtk.StatusIcon()
self.statusicon_size = '16'
+ if sys.platform == 'darwin':
+ self.statusicon_size = '24'
self.status_icon.set_property('has-tooltip', True)
self.status_icon.connect('activate', self.on_status_icon_left_clicked)
self.status_icon.connect('popup-menu',
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/f234722456bebeb5d113e63e5d6eeeda9…
---
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/f234722456bebeb5d113e63e5d6eeeda9…
You're receiving this email because of your account on dev.gajim.org.