686 lines
28 KiB
Python
686 lines
28 KiB
Python
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
|
|
|
|
import traceback
|
|
|
|
from common.tox_save import ToxSave
|
|
from contacts.friend import Friend
|
|
from contacts.group_chat import GroupChat
|
|
from contacts.group_peer_contact import GroupPeerContact
|
|
from groups.group_peer import GroupChatPeer
|
|
from messenger.messages import *
|
|
|
|
# LOG=util.log
|
|
global LOG
|
|
import logging
|
|
|
|
LOG = logging.getLogger('app.'+__name__)
|
|
|
|
def LOG_ERROR(l): print('ERROR_: '+l)
|
|
def LOG_WARN(l): print('WARN_: '+l)
|
|
def LOG_INFO(l): print('INFO_: '+l)
|
|
def LOG_DEBUG(l): print('DEBUG_: '+l)
|
|
def LOG_TRACE(l): pass # print('TRACE+ '+l)
|
|
|
|
|
|
UINT32_MAX = 2 ** 32 -1
|
|
|
|
def set_contact_kind(contact):
|
|
bInvite = len(contact.name) == TOX_PUBLIC_KEY_SIZE * 2 and \
|
|
contact.status_message == ''
|
|
bBot = not bInvite and contact.name.lower().endswith(' bot')
|
|
if type(contact) == Friend and bInvite:
|
|
contact._kind = 'invite'
|
|
elif type(contact) == Friend and bBot:
|
|
contact._kind = 'bot'
|
|
elif type(contact) == Friend:
|
|
contact._kind = 'friend'
|
|
elif type(contact) == GroupChat:
|
|
contact._kind = 'group'
|
|
elif type(contact) == GroupChatPeer:
|
|
contact._kind = 'grouppeer'
|
|
|
|
class ContactsManager(ToxSave):
|
|
"""
|
|
Represents contacts list.
|
|
"""
|
|
|
|
def __init__(self, tox, settings, screen, profile_manager, contact_provider, history, tox_dns,
|
|
messages_items_factory):
|
|
super().__init__(tox)
|
|
self._settings = settings
|
|
self._screen = screen
|
|
self._ms = screen
|
|
self._profile_manager = profile_manager
|
|
self._contact_provider = contact_provider
|
|
self._tox_dns = tox_dns
|
|
self._messages_items_factory = messages_items_factory
|
|
self._messages = screen.messages
|
|
self._contacts = []
|
|
self._active_contact = -1
|
|
self._active_contact_changed = Event()
|
|
self._sorting = settings['sorting']
|
|
self._filter_string = ''
|
|
screen.contacts_filter.setCurrentIndex(int(self._sorting))
|
|
self._history = history
|
|
self._load_contacts()
|
|
|
|
def _log(self, s):
|
|
try:
|
|
self._ms._log(s)
|
|
except: pass
|
|
|
|
def get_contact(self, num):
|
|
if num < 0 or num >= len(self._contacts):
|
|
return None
|
|
return self._contacts[num]
|
|
|
|
def get_curr_contact(self):
|
|
return self._contacts[self._active_contact] if self._active_contact + 1 else None
|
|
|
|
def save_profile(self):
|
|
data = self._tox.get_savedata()
|
|
self._profile_manager.save_profile(data)
|
|
|
|
def is_friend_active(self, friend_number):
|
|
if not self.is_active_a_friend():
|
|
return False
|
|
|
|
return self.get_curr_contact().number == friend_number
|
|
|
|
def is_group_active(self, group_number):
|
|
if self.is_active_a_friend():
|
|
return False
|
|
|
|
return self.get_curr_contact().number == group_number
|
|
|
|
def is_contact_active(self, contact):
|
|
if self._active_contact == -1:
|
|
# LOG.debug("No self._active_contact")
|
|
return False
|
|
if self._active_contact >= len(self._contacts):
|
|
LOG.warn(f"ERROR _active_contact={self._active_contact} >= contacts len={len(self._contacts)}")
|
|
return False
|
|
if not self._contacts[self._active_contact]:
|
|
LOG.warn(f"ERROR NULL {self._contacts[self._active_contact]} {contact.tox_id}")
|
|
return False
|
|
|
|
return self._contacts[self._active_contact].tox_id == contact.tox_id
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Reconnection support
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def reset_contacts_statuses(self):
|
|
for contact in self._contacts:
|
|
contact.status = None
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Work with active friend
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def get_active(self):
|
|
return self._active_contact
|
|
|
|
def set_active(self, value):
|
|
"""
|
|
Change current active friend or update info
|
|
:param value: number of new active friend in friend's list
|
|
"""
|
|
if value is None and self._active_contact == -1: # nothing to update
|
|
return
|
|
if value == -1: # all friends were deleted
|
|
self._screen.account_name.setText('')
|
|
self._screen.account_status.setText('')
|
|
self._screen.account_status.setToolTip('')
|
|
self._active_contact = -1
|
|
self._screen.account_avatar.setHidden(True)
|
|
self._messages.clear()
|
|
self._screen.messageEdit.clear()
|
|
return
|
|
try:
|
|
self._screen.typing.setVisible(False)
|
|
current_contact = self.get_curr_contact()
|
|
if current_contact is not None:
|
|
# TODO: send when needed
|
|
current_contact.typing_notification_handler.send(self._tox, False)
|
|
current_contact.remove_messages_widgets() # TODO: if required
|
|
self._unsubscribe_from_events(current_contact)
|
|
|
|
if self._active_contact >= 0 and self._active_contact != value:
|
|
try:
|
|
current_contact.curr_text = self._screen.messageEdit.toPlainText()
|
|
except:
|
|
pass
|
|
# IndexError: list index out of range
|
|
contact = self._contacts[value]
|
|
self._subscribe_to_events(contact)
|
|
contact.remove_invalid_unsent_files()
|
|
if self._active_contact != value:
|
|
self._screen.messageEdit.setPlainText(contact.curr_text)
|
|
self._active_contact = value
|
|
contact.reset_messages()
|
|
if not self._settings['save_history']:
|
|
contact.delete_old_messages()
|
|
self._messages.clear()
|
|
contact.load_corr()
|
|
corr = contact.get_corr()[-PAGE_SIZE:]
|
|
for message in corr:
|
|
if message.type == MESSAGE_TYPE['FILE_TRANSFER']:
|
|
self._messages_items_factory.create_file_transfer_item(message)
|
|
elif message.type == MESSAGE_TYPE['INLINE']:
|
|
self._messages_items_factory.create_inline_item(message)
|
|
else:
|
|
self._messages_items_factory.create_message_item(message)
|
|
self._messages.scrollToBottom()
|
|
# if value in self._call:
|
|
# self._screen.active_call()
|
|
# elif value in self._incoming_calls:
|
|
# self._screen.incoming_call()
|
|
# else:
|
|
# self._screen.call_finished()
|
|
self._set_current_contact_data(contact)
|
|
self._active_contact_changed(contact)
|
|
except Exception as ex: # no friend found. ignore
|
|
LOG.warn(f"no friend found. Friend value: {value!s}")
|
|
LOG.error('in set active: ' + str(ex))
|
|
# gulp raise
|
|
|
|
active_contact = property(get_active, set_active)
|
|
|
|
def get_active_contact_changed(self):
|
|
return self._active_contact_changed
|
|
|
|
active_contact_changed = property(get_active_contact_changed)
|
|
|
|
def update(self):
|
|
if self._active_contact + 1:
|
|
self.set_active(self._active_contact)
|
|
|
|
def is_active_a_friend(self):
|
|
return type(self.get_curr_contact()) is Friend
|
|
|
|
def is_active_a_group(self):
|
|
return type(self.get_curr_contact()) is GroupChat
|
|
|
|
def is_active_a_group_chat_peer(self):
|
|
return type(self.get_curr_contact()) is GroupPeerContact
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Filtration
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def filtration_and_sorting(self, sorting=0, filter_str=''):
|
|
"""
|
|
Filtration of friends list
|
|
:param sorting: 0 - no sorting, 1 - online only, 2 - online first, 3 - by name,
|
|
4 - online and by name, 5 - online first and by name, 6 kind
|
|
:param filter_str: show contacts which name contains this substring
|
|
"""
|
|
filter_str = filter_str.lower()
|
|
current_contact = self.get_curr_contact()
|
|
|
|
for index, contact in enumerate(self._contacts):
|
|
if not contact._kind:
|
|
set_contact_kind(contact)
|
|
|
|
if sorting > 6 or sorting < 0:
|
|
sorting = 0
|
|
|
|
if sorting in (1, 2, 4, 5): # online first
|
|
self._contacts = sorted(self._contacts, key=lambda x: int(x.status is not None), reverse=True)
|
|
sort_by_name = sorting in (4, 5)
|
|
# save results of previous sorting
|
|
online_friends = filter(lambda x: x.status is not None, self._contacts)
|
|
online_friends_count = len(list(online_friends))
|
|
part1 = self._contacts[:online_friends_count]
|
|
part2 = self._contacts[online_friends_count:]
|
|
key_lambda = lambda x: x.name.lower() if sort_by_name else x.number
|
|
part1 = sorted(part1, key=key_lambda)
|
|
part2 = sorted(part2, key=key_lambda)
|
|
self._contacts = part1 + part2
|
|
elif sorting == 0:
|
|
# AttributeError: 'NoneType' object has no attribute 'number'
|
|
for (i, contact) in enumerate(self._contacts):
|
|
if contact is None or not hasattr(contact, 'number'):
|
|
LOG.error(f"Contact {i} is None or not hasattr 'number'")
|
|
del self._contacts[i]
|
|
continue
|
|
contacts = sorted(self._contacts, key=lambda c: c.number)
|
|
friends = filter(lambda c: type(c) is Friend, contacts)
|
|
groups = filter(lambda c: type(c) is GroupChat, contacts)
|
|
group_peers = filter(lambda c: type(c) is GroupPeerContact, contacts)
|
|
self._contacts = list(friends) + list(groups) + list(group_peers)
|
|
elif sorting == 6:
|
|
self._contacts = sorted(self._contacts, key=lambda x: x._kind)
|
|
else:
|
|
self._contacts = sorted(self._contacts, key=lambda x: x.name.lower())
|
|
|
|
|
|
# change item widgets
|
|
for index, contact in enumerate(self._contacts):
|
|
list_item = self._screen.friends_list.item(index)
|
|
item_widget = self._screen.friends_list.itemWidget(list_item)
|
|
contact.set_widget(item_widget)
|
|
|
|
for index, friend in enumerate(self._contacts):
|
|
filtered_by_name = filter_str in friend.name.lower()
|
|
friend.visibility = (friend.status is not None or sorting not in (1, 4)) and filtered_by_name
|
|
# show friend even if it's hidden when there any unread messages/actions
|
|
friend.visibility = friend.visibility or friend.messages or friend.actions
|
|
item = self._screen.friends_list.item(index)
|
|
item_widget = self._screen.friends_list.itemWidget(item)
|
|
item.setSizeHint(QtCore.QSize(250, item_widget.height() if friend.visibility else 0))
|
|
|
|
# save soring results
|
|
self._sorting, self._filter_string = sorting, filter_str
|
|
self._settings['sorting'] = self._sorting
|
|
self._settings.save()
|
|
|
|
# update active contact
|
|
if current_contact is not None:
|
|
index = self._contacts.index(current_contact)
|
|
self.set_active(index)
|
|
|
|
def update_filtration(self):
|
|
"""
|
|
Update list of contacts when 1 of friends change connection status
|
|
"""
|
|
self.filtration_and_sorting(self._sorting, self._filter_string)
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Contact getters
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def get_friend_by_number(self, number):
|
|
return list(filter(lambda c: c.number == number and type(c) is Friend, self._contacts))[0]
|
|
|
|
def get_group_by_number(self, number):
|
|
return list(filter(lambda c: c.number == number and type(c) is GroupChat, self._contacts))[0]
|
|
|
|
def get_or_create_group_peer_contact(self, group_number, peer_id):
|
|
group = self.get_group_by_number(group_number)
|
|
peer = group.get_peer_by_id(peer_id)
|
|
if peer: # broken?
|
|
if not hasattr(peer, 'public_key') or not peer.public_key:
|
|
LOG.error(f'no peer public_key ' + repr(dir(peer)))
|
|
else:
|
|
if not self.check_if_contact_exists(peer.public_key):
|
|
self.add_group_peer(group, peer)
|
|
return self.get_contact_by_tox_id(peer.public_key)
|
|
else:
|
|
LOG.warn(f'no peer group_number={group_number} peer_id={peer_id}')
|
|
|
|
def check_if_contact_exists(self, tox_id):
|
|
return any(filter(lambda c: c.tox_id == tox_id, self._contacts))
|
|
|
|
def get_contact_by_tox_id(self, tox_id):
|
|
return list(filter(lambda c: c.tox_id == tox_id, self._contacts))[0]
|
|
|
|
def get_active_number(self):
|
|
return self.get_curr_contact().number if self._active_contact + 1 else -1
|
|
|
|
def get_active_name(self):
|
|
return self.get_curr_contact().name if self._active_contact + 1 else ''
|
|
|
|
def is_active_online(self):
|
|
return self._active_contact + 1 and self.get_curr_contact().status is not None
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Work with friends (remove, block, set alias, get public key)
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def set_alias(self, num):
|
|
"""
|
|
Set new alias for friend
|
|
"""
|
|
friend = self._contacts[num]
|
|
name = friend.name
|
|
text = util_ui.tr("Enter new alias for friend {} or leave empty to use friend's name:").format(name)
|
|
title = util_ui.tr('Set alias')
|
|
text, ok = util_ui.text_dialog(text, title, name)
|
|
if not ok:
|
|
return
|
|
aliases = self._settings['friends_aliases']
|
|
if text:
|
|
friend.name = text
|
|
try:
|
|
index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
|
|
aliases[index] = (friend.tox_id, text)
|
|
except:
|
|
aliases.append((friend.tox_id, text))
|
|
friend.set_alias(text)
|
|
else: # use default name
|
|
friend.name = self._tox.friend_get_name(friend.number)
|
|
friend.set_alias('')
|
|
try:
|
|
index = list(map(lambda x: x[0], aliases)).index(friend.tox_id)
|
|
del aliases[index]
|
|
except:
|
|
pass
|
|
self._settings.save()
|
|
|
|
def friend_public_key(self, num):
|
|
return self._contacts[num].tox_id
|
|
|
|
def delete_friend(self, num):
|
|
"""
|
|
Removes friend from contact list
|
|
:param num: number of friend in list
|
|
"""
|
|
friend = self._contacts[num]
|
|
self._cleanup_contact_data(friend)
|
|
try:
|
|
self._tox.friend_delete(friend.number)
|
|
except Exception as e:
|
|
LOG.warn(f"'There was no friend with the given friend number {e}")
|
|
self._delete_contact(num)
|
|
|
|
def add_friend(self, tox_id):
|
|
"""
|
|
Adds friend to list
|
|
"""
|
|
self._tox.friend_add_norequest(tox_id)
|
|
self._add_friend(tox_id)
|
|
self.update_filtration()
|
|
|
|
def block_user(self, tox_id):
|
|
"""
|
|
Block user with specified tox id (or public key) - delete from friends list and ignore friend requests
|
|
"""
|
|
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
|
|
if tox_id == self._tox.self_get_address()[:TOX_PUBLIC_KEY_SIZE * 2]:
|
|
return
|
|
if tox_id not in self._settings['blocked']:
|
|
self._settings['blocked'].append(tox_id)
|
|
self._settings.save()
|
|
try:
|
|
num = self._tox.friend_by_public_key(tox_id)
|
|
self.delete_friend(num)
|
|
self.save_profile()
|
|
except: # not in friend list
|
|
pass
|
|
|
|
def unblock_user(self, tox_id, add_to_friend_list):
|
|
"""
|
|
Unblock user
|
|
:param tox_id: tox id of contact
|
|
:param add_to_friend_list: add this contact to friend list or not
|
|
"""
|
|
self._settings['blocked'].remove(tox_id)
|
|
self._settings.save()
|
|
if add_to_friend_list:
|
|
self.add_friend(tox_id)
|
|
self.save_profile()
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Groups support
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def get_group_chats(self):
|
|
return list(filter(lambda c: type(c) is GroupChat, self._contacts))
|
|
|
|
def add_group(self, group_number):
|
|
index = len(self._contacts)
|
|
group = self._contact_provider.get_group_by_number(group_number)
|
|
# group num >= 0?
|
|
if group is None:
|
|
LOG.warn(f"CM.add_group: NO group {group_number}")
|
|
else:
|
|
LOG.info(f"CM.add_group: Adding group {group._name}")
|
|
self._contacts.append(group)
|
|
LOG.info(f"contacts_manager.add_group: saving profile")
|
|
self._save_profile()
|
|
group.reset_avatar(self._settings['identicons'])
|
|
LOG.info(f"contacts_manager.add_group: setting active")
|
|
self.set_active(index)
|
|
self.update_filtration()
|
|
|
|
def delete_group(self, group_number):
|
|
group = self.get_group_by_number(group_number)
|
|
self._cleanup_contact_data(group)
|
|
num = self._contacts.index(group)
|
|
self._delete_contact(num)
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Groups private messaging
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def add_group_peer(self, group, peer):
|
|
contact = self._contact_provider.get_group_peer_by_id(group, peer.id)
|
|
if self.check_if_contact_exists(contact.tox_id):
|
|
return
|
|
contact._kind = 'grouppeer'
|
|
self._contacts.append(contact)
|
|
contact.reset_avatar(self._settings['identicons'])
|
|
self._save_profile()
|
|
|
|
def remove_group_peer_by_id(self, group, peer_id):
|
|
peer = group.get_peer_by_id(peer_id)
|
|
if peer: # broken
|
|
if not self.check_if_contact_exists(peer.public_key):
|
|
return
|
|
contact = self.get_contact_by_tox_id(peer.public_key)
|
|
self.remove_group_peer(contact)
|
|
|
|
def remove_group_peer(self, group_peer_contact):
|
|
contact = self.get_contact_by_tox_id(group_peer_contact.tox_id)
|
|
if contact:
|
|
self._cleanup_contact_data(contact)
|
|
num = self._contacts.index(contact)
|
|
self._delete_contact(num)
|
|
|
|
def get_gc_peer_name(self, name):
|
|
group = self.get_curr_contact()
|
|
|
|
names = sorted(group.get_peers_names())
|
|
if name in names: # return next nick
|
|
index = names.index(name)
|
|
index = (index + 1) % len(names)
|
|
|
|
return names[index]
|
|
|
|
suggested_names = list(filter(lambda x: x.startswith(name), names))
|
|
if not len(suggested_names):
|
|
return '\t'
|
|
|
|
return suggested_names[0]
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Friend requests
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def send_friend_request(self, sToxPkOrId, message):
|
|
"""
|
|
Function tries to send request to contact with specified id
|
|
:param sToxPkOrId: id of new contact or tox dns 4 value
|
|
:param message: additional message
|
|
:return: True on success else error string
|
|
"""
|
|
retval = ''
|
|
try:
|
|
message = message or 'Hello! Add me to your contact list please'
|
|
if len(sToxPkOrId) == TOX_PUBLIC_KEY_SIZE * 2: # public key
|
|
self.add_friend(sToxPkOrId)
|
|
title = 'Friend added'
|
|
text = 'Friend added without sending friend request'
|
|
else:
|
|
num = self._tox.friend_add(sToxPkOrId, message.encode('utf-8'))
|
|
if num < UINT32_MAX:
|
|
tox_pk = sToxPkOrId[:TOX_PUBLIC_KEY_SIZE * 2]
|
|
self._add_friend(tox_pk)
|
|
self.update_filtration()
|
|
title = 'Friend added'
|
|
text = 'Friend added by sending friend request'
|
|
self.save_profile()
|
|
retval = True
|
|
else:
|
|
title = 'Friend failed'
|
|
text = 'Friend failed sending friend request'
|
|
retval = text
|
|
|
|
except Exception as ex: # wrong data
|
|
title = 'Friend add exception'
|
|
text = 'Friend request exception with ' + str(ex)
|
|
self._log(text)
|
|
LOG.exception(text)
|
|
LOG.warn(f"DELETE {sToxPkOrId} ?")
|
|
retval = str(ex)
|
|
title = util_ui.tr(title)
|
|
text = util_ui.tr(text)
|
|
util_ui.message_box(text, title)
|
|
return retval
|
|
|
|
def process_friend_request(self, tox_id, message):
|
|
"""
|
|
Accept or ignore friend request
|
|
:param tox_id: tox id of contact
|
|
:param message: message
|
|
"""
|
|
if tox_id in self._settings['blocked']:
|
|
return
|
|
try:
|
|
text = util_ui.tr('User {} wants to add you to contact list. Message:\n{}')
|
|
reply = util_ui.question(text.format(tox_id, message), util_ui.tr('Friend request'))
|
|
if reply: # accepted
|
|
self.add_friend(tox_id)
|
|
data = self._tox.get_savedata()
|
|
self._profile_manager.save_profile(data)
|
|
except Exception as ex: # something is wrong
|
|
LOG.error('Accept friend request failed! ' + str(ex))
|
|
|
|
def can_send_typing_notification(self):
|
|
return self._settings['typing_notifications'] and not self.is_active_a_group_chat_peer()
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Contacts numbers update
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def update_friends_numbers(self):
|
|
for friend in self._contact_provider.get_all_friends():
|
|
friend.number = self._tox.friend_by_public_key(friend.tox_id)
|
|
self.update_filtration()
|
|
|
|
def update_groups_numbers(self):
|
|
groups = self._contact_provider.get_all_groups()
|
|
LOG.info(f"update_groups_numbers len(groups)={len(groups)}")
|
|
# Thread 76 "ToxIterateThrea" received signal SIGSEGV, Segmentation fault.
|
|
for i in range(len(groups)):
|
|
chat_id = self._tox.group_get_chat_id(i)
|
|
if not chat_id:
|
|
LOG.warn(f"update_groups_numbers {i} chat_id")
|
|
continue
|
|
group = self.get_contact_by_tox_id(chat_id)
|
|
if not group:
|
|
LOG.warn(f"update_groups_numbers {i} group")
|
|
continue
|
|
group.number = i
|
|
self.update_filtration()
|
|
|
|
def update_groups_lists(self):
|
|
groups = self._contact_provider.get_all_groups()
|
|
for group in groups:
|
|
group.remove_all_peers_except_self()
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Private methods
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def _load_contacts(self):
|
|
self._load_friends()
|
|
self._load_groups()
|
|
if len(self._contacts):
|
|
self.set_active(0)
|
|
# filter(lambda c: not c.has_avatar(), self._contacts)
|
|
for (i, contact) in enumerate(self._contacts):
|
|
if contact is None:
|
|
LOG.warn(f"_load_contacts NULL contact {i}")
|
|
LOG.info(f"_load_contacts deleting NULL {self._contacts[i]}")
|
|
del self._contacts[i]
|
|
#? self.save_profile()
|
|
continue
|
|
if contact.has_avatar(): continue
|
|
contact.reset_avatar(self._settings['identicons'])
|
|
self.update_filtration()
|
|
|
|
def _load_friends(self):
|
|
self._contacts.extend(self._contact_provider.get_all_friends())
|
|
|
|
def _load_groups(self):
|
|
self._contacts.extend(self._contact_provider.get_all_groups())
|
|
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
# Current contact subscriptions
|
|
# -----------------------------------------------------------------------------------------------------------------
|
|
|
|
def _subscribe_to_events(self, contact):
|
|
contact.name_changed_event.add_callback(self._current_contact_name_changed)
|
|
contact.status_changed_event.add_callback(self._current_contact_status_changed)
|
|
contact.status_message_changed_event.add_callback(self._current_contact_status_message_changed)
|
|
contact.avatar_changed_event.add_callback(self._current_contact_avatar_changed)
|
|
|
|
def _unsubscribe_from_events(self, contact):
|
|
contact.name_changed_event.remove_callback(self._current_contact_name_changed)
|
|
contact.status_changed_event.remove_callback(self._current_contact_status_changed)
|
|
contact.status_message_changed_event.remove_callback(self._current_contact_status_message_changed)
|
|
contact.avatar_changed_event.remove_callback(self._current_contact_avatar_changed)
|
|
|
|
def _current_contact_name_changed(self, name):
|
|
self._screen.account_name.setText(name)
|
|
|
|
def _current_contact_status_changed(self, status):
|
|
pass
|
|
|
|
def _current_contact_status_message_changed(self, status_message):
|
|
self._screen.account_status.setText(status_message)
|
|
|
|
def _current_contact_avatar_changed(self, avatar_path):
|
|
self._set_current_contact_avatar(avatar_path)
|
|
|
|
def _set_current_contact_data(self, contact):
|
|
self._screen.account_name.setText(contact.name)
|
|
self._screen.account_status.setText(contact.status_message)
|
|
self._set_current_contact_avatar(contact.get_avatar_path())
|
|
|
|
def _set_current_contact_avatar(self, avatar_path):
|
|
width = self._screen.account_avatar.width()
|
|
pixmap = QtGui.QPixmap(avatar_path)
|
|
self._screen.account_avatar.setPixmap(pixmap.scaled(width, width,
|
|
QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
|
|
|
|
def _add_friend(self, tox_id):
|
|
self._history.add_friend_to_db(tox_id)
|
|
friend = self._contact_provider.get_friend_by_public_key(tox_id)
|
|
index = len(self._contacts)
|
|
self._contacts.append(friend)
|
|
if not friend.has_avatar():
|
|
friend.reset_avatar(self._settings['identicons'])
|
|
self._save_profile()
|
|
self.set_active(index)
|
|
|
|
def _save_profile(self):
|
|
data = self._tox.get_savedata()
|
|
self._profile_manager.save_profile(data)
|
|
|
|
def _cleanup_contact_data(self, contact):
|
|
try:
|
|
index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(contact.tox_id)
|
|
del self._settings['friends_aliases'][index]
|
|
except:
|
|
pass
|
|
if contact.tox_id in self._settings['notes']:
|
|
del self._settings['notes'][contact.tox_id]
|
|
self._settings.save()
|
|
self._history.delete_history(contact)
|
|
if contact.has_avatar():
|
|
avatar_path = contact.get_contact_avatar_path()
|
|
remove(avatar_path)
|
|
|
|
def _delete_contact(self, num):
|
|
self.set_active(-1 if len(self._contacts) == 1 else 0)
|
|
|
|
self._contact_provider.remove_contact_from_cache(self._contacts[num].tox_id)
|
|
del self._contacts[num]
|
|
self._screen.friends_list.takeItem(num)
|
|
self._save_profile()
|
|
|
|
self.update_filtration()
|