Add support for Room Avatars
- Groupchats promote a vcard hash with presence Refactoring: - Dont delete groupchat contacts if they are maximized from the roster - Roster and GroupchatControl use the same contact object
This commit is contained in:
parent
70a7000d44
commit
290e761f88
|
@ -291,6 +291,8 @@ class ConnectionVcard:
|
|||
self._vcard_presence_received)
|
||||
app.ged.register_event_handler('gc-presence-received', ged.GUI2,
|
||||
self._vcard_gc_presence_received)
|
||||
app.ged.register_event_handler('room-avatar-received', ged.GUI2,
|
||||
self._vcard_presence_received)
|
||||
|
||||
def _vcard_presence_received(self, obj):
|
||||
if obj.conn.name != self.name:
|
||||
|
@ -300,6 +302,10 @@ class ConnectionVcard:
|
|||
# No Avatar is advertised
|
||||
return
|
||||
|
||||
room_avatar = False
|
||||
if isinstance(obj, RoomAvatarReceivedEvent):
|
||||
room_avatar = True
|
||||
|
||||
if self.get_own_jid().bareMatch(obj.jid):
|
||||
app.log('avatar').info('Update (vCard): %s %s',
|
||||
obj.jid, obj.avatar_sha)
|
||||
|
@ -324,16 +330,33 @@ class ConnectionVcard:
|
|||
app.log('avatar').debug('Remove: %s', obj.jid)
|
||||
app.contacts.set_avatar(self.name, obj.jid, None)
|
||||
own_jid = self.get_own_jid().getStripped()
|
||||
if not room_avatar:
|
||||
app.logger.set_avatar_sha(own_jid, obj.jid, None)
|
||||
app.interface.update_avatar(self.name, obj.jid)
|
||||
app.interface.update_avatar(
|
||||
self.name, obj.jid, room_avatar=room_avatar)
|
||||
else:
|
||||
app.log('avatar').info(
|
||||
'Update (vCard): %s %s', obj.jid, obj.avatar_sha)
|
||||
current_sha = app.contacts.get_avatar_sha(self.name, obj.jid)
|
||||
|
||||
if obj.avatar_sha != current_sha:
|
||||
if room_avatar:
|
||||
# We dont save the room avatar hash in our DB, so check
|
||||
# if we previously downloaded it
|
||||
if app.interface.avatar_exists(obj.avatar_sha):
|
||||
app.contacts.set_avatar(self.name, obj.jid, obj.avatar_sha)
|
||||
app.interface.update_avatar(
|
||||
self.name, obj.jid, room_avatar=room_avatar)
|
||||
else:
|
||||
app.log('avatar').info(
|
||||
'Request (vCard): %s', obj.jid)
|
||||
self.request_vcard(self._on_room_avatar_received, obj.jid)
|
||||
|
||||
else:
|
||||
app.log('avatar').info(
|
||||
'Request (vCard): %s', obj.jid)
|
||||
self.request_vcard(self._on_avatar_received, obj.jid)
|
||||
|
||||
else:
|
||||
app.log('avatar').info(
|
||||
'Avatar already known (vCard): %s %s',
|
||||
|
@ -568,6 +591,14 @@ class ConnectionVcard:
|
|||
self.send_avatar_presence()
|
||||
self.avatar_presence_sent = True
|
||||
|
||||
def _on_room_avatar_received(self, jid, resource, room, vcard):
|
||||
avatar_sha, photo_decoded = self._get_vcard_photo(vcard, jid)
|
||||
app.interface.save_avatar(photo_decoded)
|
||||
|
||||
app.log('avatar').info('Received (vCard): %s %s', jid, avatar_sha)
|
||||
app.contacts.set_avatar(self.name, jid, avatar_sha)
|
||||
app.interface.update_avatar(self.name, jid, room_avatar=True)
|
||||
|
||||
def _on_avatar_received(self, jid, resource, room, vcard):
|
||||
"""
|
||||
Called when we receive a vCard Parse the vCard and trigger Events
|
||||
|
|
|
@ -797,6 +797,17 @@ PresenceHelperEvent):
|
|||
time_str = idle_tag.getAttr('since')
|
||||
tim = helpers.datetime_tuple(time_str)
|
||||
self.idle_time = timegm(tim)
|
||||
|
||||
# Check if presence is from the room itself, used when the room
|
||||
# sends a avatar hash
|
||||
contact = app.contacts.get_groupchat_contact(self.conn.name, self.fjid)
|
||||
if contact:
|
||||
app.nec.push_incoming_event(
|
||||
RoomAvatarReceivedEvent(
|
||||
None, conn=self.conn, stanza=self.stanza,
|
||||
contact=contact, jid=self.jid))
|
||||
return
|
||||
|
||||
xtags = self.stanza.getTags('x')
|
||||
for x in xtags:
|
||||
namespace = x.getNamespace()
|
||||
|
@ -2229,6 +2240,25 @@ class UpdateRosterAvatarEvent(nec.NetworkIncomingEvent):
|
|||
def generate(self):
|
||||
return True
|
||||
|
||||
class UpdateRoomAvatarEvent(nec.NetworkIncomingEvent):
|
||||
name = 'update-room-avatar'
|
||||
base_network_events = []
|
||||
|
||||
def generate(self):
|
||||
return True
|
||||
|
||||
class RoomAvatarReceivedEvent(nec.NetworkIncomingEvent):
|
||||
name = 'room-avatar-received'
|
||||
base_network_events = []
|
||||
|
||||
def generate(self):
|
||||
vcard = self.stanza.getTag('x', namespace=nbxmpp.NS_VCARD_UPDATE)
|
||||
if vcard is None:
|
||||
log.warning('Invalid room self presence:\n%s', self.stanza)
|
||||
return
|
||||
self.avatar_sha = vcard.getTagData('photo')
|
||||
return True
|
||||
|
||||
class PEPConfigReceivedEvent(nec.NetworkIncomingEvent):
|
||||
name = 'pep-config-received'
|
||||
base_network_events = []
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
##
|
||||
|
||||
try:
|
||||
from gajim.common import app
|
||||
from gajim.common import caps_cache
|
||||
from gajim.common.account import Account
|
||||
from gajim import common
|
||||
|
@ -92,7 +93,7 @@ class Contact(CommonContact):
|
|||
"""
|
||||
def __init__(self, jid, account, name='', groups=None, show='', status='',
|
||||
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
|
||||
our_chatstate=None, chatstate=None, idle_time=None, avatar_sha=None):
|
||||
our_chatstate=None, chatstate=None, idle_time=None, avatar_sha=None, groupchat=False):
|
||||
if not isinstance(jid, str):
|
||||
print('no str')
|
||||
if groups is None:
|
||||
|
@ -104,6 +105,7 @@ class Contact(CommonContact):
|
|||
self.contact_name = '' # nick choosen by contact
|
||||
self.groups = [i if i else _('General') for i in set(groups)] # filter duplicate values
|
||||
self.avatar_sha = avatar_sha
|
||||
self._is_groupchat = groupchat
|
||||
|
||||
self.sub = sub
|
||||
self.ask = ask
|
||||
|
@ -164,10 +166,7 @@ class Contact(CommonContact):
|
|||
return is_observer
|
||||
|
||||
def is_groupchat(self):
|
||||
for account in common.app.gc_connected:
|
||||
if self.jid in common.app.gc_connected[account]:
|
||||
return True
|
||||
return False
|
||||
return self._is_groupchat
|
||||
|
||||
def is_transport(self):
|
||||
# if not '@' or '@' starts the jid then contact is transport
|
||||
|
@ -249,7 +248,7 @@ class LegacyContactsAPI:
|
|||
def create_contact(self, jid, account, name='', groups=None, show='',
|
||||
status='', sub='', ask='', resource='', priority=0, keyID='',
|
||||
client_caps=None, our_chatstate=None, chatstate=None, idle_time=None,
|
||||
avatar_sha=None):
|
||||
avatar_sha=None, groupchat=False):
|
||||
if groups is None:
|
||||
groups = []
|
||||
# Use Account object if available
|
||||
|
@ -258,7 +257,7 @@ class LegacyContactsAPI:
|
|||
show=show, status=status, sub=sub, ask=ask, resource=resource,
|
||||
priority=priority, keyID=keyID, client_caps=client_caps,
|
||||
our_chatstate=our_chatstate, chatstate=chatstate,
|
||||
idle_time=idle_time, avatar_sha=avatar_sha)
|
||||
idle_time=idle_time, avatar_sha=avatar_sha, groupchat=groupchat)
|
||||
|
||||
def create_self_contact(self, jid, account, resource, show, status, priority,
|
||||
name='', keyID=''):
|
||||
|
@ -304,6 +303,9 @@ class LegacyContactsAPI:
|
|||
if remove_meta:
|
||||
self._metacontact_manager.remove_metacontact(account, jid)
|
||||
|
||||
def get_groupchat_contact(self, account, jid):
|
||||
return self._accounts[account].contacts.get_groupchat_contact(jid)
|
||||
|
||||
def get_contacts(self, account, jid):
|
||||
return self._accounts[account].contacts.get_contacts(jid)
|
||||
|
||||
|
@ -518,6 +520,15 @@ class Contacts():
|
|||
if c.resource == resource:
|
||||
return c
|
||||
|
||||
def get_groupchat_contact(self, jid):
|
||||
if jid in self._contacts:
|
||||
contacts = self._contacts[jid]
|
||||
if len(contacts) > 1:
|
||||
app.log('contacts').warning(
|
||||
'Groupchat Contact found more than once')
|
||||
if contacts[0].is_groupchat():
|
||||
return contacts[0]
|
||||
|
||||
def get_avatar(self, jid, size=None, scale=None):
|
||||
if jid not in self._contacts:
|
||||
return None
|
||||
|
|
|
@ -301,7 +301,7 @@ class GroupchatControl(ChatControlBase):
|
|||
# will be processed with this command host.
|
||||
COMMAND_HOST = GroupChatCommands
|
||||
|
||||
def __init__(self, parent_win, contact, acct, is_continued=False):
|
||||
def __init__(self, parent_win, contact, nick, acct, is_continued=False):
|
||||
ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
|
||||
'groupchat_control', contact, acct)
|
||||
|
||||
|
@ -362,7 +362,7 @@ class GroupchatControl(ChatControlBase):
|
|||
self.handlers[id_] = widget
|
||||
|
||||
self.room_jid = self.contact.jid
|
||||
self.nick = contact.name
|
||||
self.nick = nick
|
||||
self.new_nick = ''
|
||||
self.name = ''
|
||||
for bm in app.connections[self.account].bookmarks:
|
||||
|
@ -371,6 +371,7 @@ class GroupchatControl(ChatControlBase):
|
|||
break
|
||||
if not self.name:
|
||||
self.name = self.room_jid.split('@')[0]
|
||||
self.contact.name = self.name
|
||||
|
||||
self.widget_set_visible(self.xml.get_object('banner_eventbox'),
|
||||
app.config.get('hide_groupchat_banner'))
|
||||
|
@ -519,6 +520,8 @@ class GroupchatControl(ChatControlBase):
|
|||
self._nec_vcard_published)
|
||||
app.ged.register_event_handler('update-gc-avatar', ged.GUI1,
|
||||
self._nec_update_avatar)
|
||||
app.ged.register_event_handler('update-room-avatar', ged.GUI1,
|
||||
self._nec_update_room_avatar)
|
||||
app.ged.register_event_handler('gc-subject-received', ged.GUI1,
|
||||
self._nec_gc_subject_received)
|
||||
app.ged.register_event_handler('gc-config-changed-received', ged.GUI1,
|
||||
|
@ -1091,6 +1094,12 @@ class GroupchatControl(ChatControlBase):
|
|||
banner_status_img = self.xml.get_object('gc_banner_status_image')
|
||||
if self.room_jid in app.gc_connected[self.account] and \
|
||||
app.gc_connected[self.account][self.room_jid]:
|
||||
if self.contact.avatar_sha:
|
||||
surface = app.interface.get_avatar(self.contact.avatar_sha,
|
||||
AvatarSize.ROSTER,
|
||||
self.scale_factor)
|
||||
banner_status_img.set_from_surface(surface)
|
||||
return
|
||||
icon = gtkgui_helpers.get_iconset_name_for('muc-active')
|
||||
else:
|
||||
icon = gtkgui_helpers.get_iconset_name_for('muc-inactive')
|
||||
|
@ -1147,6 +1156,11 @@ class GroupchatControl(ChatControlBase):
|
|||
obj.contact.name, obj.contact.avatar_sha)
|
||||
self.draw_avatar(obj.contact)
|
||||
|
||||
def _nec_update_room_avatar(self, obj):
|
||||
if obj.jid != self.room_jid:
|
||||
return
|
||||
self._update_banner_state_image()
|
||||
|
||||
def _nec_mam_decrypted_message_received(self, obj):
|
||||
if not obj.groupchat:
|
||||
return
|
||||
|
@ -2161,8 +2175,8 @@ class GroupchatControl(ChatControlBase):
|
|||
ctrl.parent_win = None
|
||||
self.send_chatstate('inactive', self.contact)
|
||||
|
||||
app.interface.roster.add_groupchat(self.contact.jid, self.account,
|
||||
status = self.subject)
|
||||
app.interface.roster.minimize_groupchat(
|
||||
self.account, self.contact.jid, status=self.subject)
|
||||
|
||||
del win._controls[self.account][self.contact.jid]
|
||||
|
||||
|
@ -2233,6 +2247,8 @@ class GroupchatControl(ChatControlBase):
|
|||
self._nec_vcard_published)
|
||||
app.ged.remove_event_handler('update-gc-avatar', ged.GUI1,
|
||||
self._nec_update_avatar)
|
||||
app.ged.remove_event_handler('update-room-avatar', ged.GUI1,
|
||||
self._nec_update_room_avatar)
|
||||
app.ged.remove_event_handler('gc-subject-received', ged.GUI1,
|
||||
self._nec_gc_subject_received)
|
||||
app.ged.remove_event_handler('gc-config-changed-received', ged.GUI1,
|
||||
|
|
|
@ -92,7 +92,8 @@ from gajim.common import passwords
|
|||
from gajim.common import logging_helpers
|
||||
from gajim.common.connection_handlers_events import (
|
||||
OurShowEvent, FileRequestErrorEvent, FileTransferCompletedEvent,
|
||||
UpdateRosterAvatarEvent, UpdateGCAvatarEvent, HTTPUploadProgressEvent)
|
||||
UpdateRosterAvatarEvent, UpdateGCAvatarEvent, UpdateRoomAvatarEvent,
|
||||
HTTPUploadProgressEvent)
|
||||
from gajim.common.connection import Connection
|
||||
from gajim.common.file_props import FilesProp
|
||||
from gajim.common import pep
|
||||
|
@ -2071,8 +2072,10 @@ class Interface:
|
|||
if minimize:
|
||||
# GCMIN
|
||||
contact = app.contacts.create_contact(jid=room_jid,
|
||||
account=account, name=nick)
|
||||
gc_control = GroupchatControl(None, contact, account)
|
||||
account=account, groups=[_('Groupchats')], sub='none',
|
||||
groupchat=True)
|
||||
app.contacts.add_contact(account, contact)
|
||||
gc_control = GroupchatControl(None, contact, nick, account)
|
||||
app.interface.minimized_controls[account][room_jid] = \
|
||||
gc_control
|
||||
self.roster.add_groupchat(room_jid, account)
|
||||
|
@ -2094,12 +2097,13 @@ class Interface:
|
|||
# Get target window, create a control, and associate it with the window
|
||||
# GCMIN
|
||||
contact = app.contacts.create_contact(jid=room_jid, account=account,
|
||||
name=nick)
|
||||
groups=[_('Groupchats')], sub='none', groupchat=True)
|
||||
app.contacts.add_contact(account, contact)
|
||||
mw = self.msg_win_mgr.get_window(contact.jid, account)
|
||||
if not mw:
|
||||
mw = self.msg_win_mgr.create_window(contact, account,
|
||||
GroupchatControl.TYPE_ID)
|
||||
gc_control = GroupchatControl(mw, contact, account,
|
||||
gc_control = GroupchatControl(mw, contact, nick, account,
|
||||
is_continued=is_continued)
|
||||
mw.new_tab(gc_control)
|
||||
mw.set_active_tab(gc_control)
|
||||
|
@ -2421,8 +2425,11 @@ class Interface:
|
|||
sys.exit()
|
||||
|
||||
@staticmethod
|
||||
def update_avatar(account=None, jid=None, contact=None):
|
||||
if contact is None:
|
||||
def update_avatar(account=None, jid=None, contact=None, room_avatar=False):
|
||||
if room_avatar:
|
||||
app.nec.push_incoming_event(
|
||||
UpdateRoomAvatarEvent(None, account=account, jid=jid))
|
||||
elif contact is None:
|
||||
app.nec.push_incoming_event(
|
||||
UpdateRosterAvatarEvent(None, account=account, jid=jid))
|
||||
else:
|
||||
|
@ -2524,6 +2531,13 @@ class Interface:
|
|||
return pixbuf
|
||||
return Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale)
|
||||
|
||||
@staticmethod
|
||||
def avatar_exists(filename):
|
||||
path = os.path.join(app.AVATAR_PATH, filename)
|
||||
if not os.path.isfile(path):
|
||||
return False
|
||||
return True
|
||||
|
||||
def auto_join_bookmarks(self, account):
|
||||
"""
|
||||
Autojoin bookmarked GCs that have 'auto join' on for this account
|
||||
|
@ -2538,12 +2552,6 @@ class Interface:
|
|||
minimize = bm['minimize'] in ('1', 'true')
|
||||
self.join_gc_room(account, jid, bm['nick'],
|
||||
bm['password'], minimize = minimize)
|
||||
elif jid in self.minimized_controls[account]:
|
||||
# more or less a hack:
|
||||
# On disconnect the minimized gc contact instances
|
||||
# were set to offline. Reconnect them to show up in the
|
||||
# roster.
|
||||
self.roster.add_groupchat(jid, account)
|
||||
|
||||
def add_gc_bookmark(self, account, name, jid, autojoin, minimize, password,
|
||||
nick):
|
||||
|
|
|
@ -743,7 +743,7 @@ class RosterWindow:
|
|||
|
||||
return contacts[0][0] # it's contact/big brother with highest priority
|
||||
|
||||
def remove_contact(self, jid, account, force=False, backend=False):
|
||||
def remove_contact(self, jid, account, force=False, backend=False, maximize=False):
|
||||
"""
|
||||
Remove contact from roster
|
||||
|
||||
|
@ -785,6 +785,8 @@ class RosterWindow:
|
|||
# If a window is still opened: don't remove contact instance
|
||||
# Remove contact before redrawing, otherwise the old
|
||||
# numbers will still be show
|
||||
if not maximize:
|
||||
# Dont remove contact when we maximize a room
|
||||
app.contacts.remove_jid(account, jid, remove_meta=True)
|
||||
if iters:
|
||||
rest_of_family = [data for data in family
|
||||
|
@ -829,49 +831,26 @@ class RosterWindow:
|
|||
self.model[self_iter][Column.JID] = new_jid
|
||||
self.draw_contact(new_jid, account)
|
||||
|
||||
def add_groupchat(self, jid, account, status=''):
|
||||
def minimize_groupchat(self, account, jid, status=''):
|
||||
gc_control = app.interface.msg_win_mgr.get_gc_control(jid, account)
|
||||
app.interface.minimized_controls[account][jid] = gc_control
|
||||
self.add_groupchat(jid, account)
|
||||
|
||||
def add_groupchat(self, jid, account):
|
||||
"""
|
||||
Add groupchat to roster and draw it. Return the added contact instance
|
||||
"""
|
||||
contact = app.contacts.get_contact_with_highest_priority(account, jid)
|
||||
# Do not show gc if we are disconnected and minimize it
|
||||
contact = app.contacts.get_groupchat_contact(account, jid)
|
||||
show = 'offline'
|
||||
if app.account_is_connected(account):
|
||||
show = 'online'
|
||||
else:
|
||||
show = 'offline'
|
||||
status = ''
|
||||
|
||||
if contact is None:
|
||||
gc_control = app.interface.msg_win_mgr.get_gc_control(jid,
|
||||
account)
|
||||
if gc_control:
|
||||
# there is a window that we can minimize
|
||||
app.interface.minimized_controls[account][jid] = gc_control
|
||||
name = gc_control.name
|
||||
elif jid in app.interface.minimized_controls[account]:
|
||||
name = app.interface.minimized_controls[account][jid].name
|
||||
else:
|
||||
name = jid.split('@')[0]
|
||||
# New groupchat
|
||||
contact = app.contacts.create_contact(jid=jid, account=account,
|
||||
name=name, groups=[_('Groupchats')], show=show, status=status,
|
||||
sub='none')
|
||||
app.contacts.add_contact(account, contact)
|
||||
self.add_contact(jid, account)
|
||||
else:
|
||||
if jid not in app.interface.minimized_controls[account]:
|
||||
# there is a window that we can minimize
|
||||
gc_control = app.interface.msg_win_mgr.get_gc_control(jid,
|
||||
account)
|
||||
app.interface.minimized_controls[account][jid] = gc_control
|
||||
contact.show = show
|
||||
contact.status = status
|
||||
self.adjust_and_draw_contact_context(jid, account)
|
||||
self.add_contact(jid, account)
|
||||
|
||||
return contact
|
||||
|
||||
|
||||
def remove_groupchat(self, jid, account):
|
||||
def remove_groupchat(self, jid, account, maximize=False):
|
||||
"""
|
||||
Remove groupchat from roster and redraw account and group
|
||||
"""
|
||||
|
@ -879,7 +858,7 @@ class RosterWindow:
|
|||
if contact.is_groupchat():
|
||||
if jid in app.interface.minimized_controls[account]:
|
||||
del app.interface.minimized_controls[account][jid]
|
||||
self.remove_contact(jid, account, force=True, backend=True)
|
||||
self.remove_contact(jid, account, force=True, backend=True, maximize=maximize)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@ -3137,7 +3116,7 @@ class RosterWindow:
|
|||
ctrl.on_groupchat_maximize()
|
||||
mw.new_tab(ctrl)
|
||||
mw.set_active_tab(ctrl)
|
||||
self.remove_groupchat(jid, account)
|
||||
self.remove_groupchat(jid, account, maximize=True)
|
||||
|
||||
def on_edit_account(self, widget, account):
|
||||
if 'accounts' in app.interface.instances:
|
||||
|
@ -5912,6 +5891,8 @@ class RosterWindow:
|
|||
self._nec_pep_received)
|
||||
app.ged.register_event_handler('update-roster-avatar', ged.GUI1,
|
||||
self._nec_update_avatar)
|
||||
app.ged.register_event_handler('update-room-avatar', ged.GUI1,
|
||||
self._nec_update_avatar)
|
||||
app.ged.register_event_handler('gc-subject-received', ged.GUI1,
|
||||
self._nec_gc_subject_received)
|
||||
app.ged.register_event_handler('metacontacts-received', ged.GUI2,
|
||||
|
|
Loading…
Reference in New Issue