From ef4a1b18fdbbe385d939e98c3561ca82c8df781c Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 May 2018 18:08:25 +0300 Subject: [PATCH] ngc - invites, gc menu, callbacks etc --- toxygen/app.py | 12 +++-- toxygen/contacts/contact_menu.py | 53 ++++++++++++++------ toxygen/contacts/contacts_manager.py | 73 ++++++++++++++++++---------- toxygen/contacts/friend_factory.py | 2 +- toxygen/contacts/group_chat.py | 42 +++++++++------- toxygen/contacts/group_factory.py | 45 ++++++++++++++++- toxygen/groups/group_peer.py | 46 +++++++++++++++--- toxygen/groups/groups_service.py | 30 ++++++++++++ toxygen/middleware/callbacks.py | 24 ++++++++- toxygen/ui/items_factories.py | 4 +- toxygen/ui/main_screen.py | 9 ++-- toxygen/wrapper/tox.py | 4 +- 12 files changed, 262 insertions(+), 82 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index b76a047..a4fa37b 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -25,7 +25,7 @@ from av.calls_manager import CallsManager from history.database import Database from ui.widgets_factory import WidgetsFactory from smileys.smileys import SmileyLoader -from ui.items_factories import MessagesItemsFactory, FriendItemsFactory +from ui.items_factories import MessagesItemsFactory, ContactItemsFactory from messenger.messenger import Messenger from network.tox_dns import ToxDns from history.history import History @@ -301,9 +301,10 @@ class App: self._ms = MainWindow(self._settings, self._tray) db = Database(self._path.replace('.tox', '.db'), self._toxes) - friend_items_factory = FriendItemsFactory(self._settings, self._ms) - self._friend_factory = FriendFactory(self._profile_manager, self._settings, self._tox, db, friend_items_factory) - self._group_factory = GroupFactory() + contact_items_factory = ContactItemsFactory(self._settings, self._ms) + self._friend_factory = FriendFactory(self._profile_manager, self._settings, + self._tox, db, contact_items_factory) + self._group_factory = GroupFactory(self._profile_manager, self._settings, self._tox, db, contact_items_factory) self._contacts_provider = ContactProvider(self._tox, self._friend_factory, self._group_factory) profile = Profile(self._profile_manager, self._tox, self._ms, self._contacts_provider, self._reset) self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) @@ -329,7 +330,8 @@ class App: self._toxes, self._version, self._groups_service) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, - self._plugin_loader, self._file_transfer_handler, history, self._calls_manager) + self._plugin_loader, self._file_transfer_handler, history, self._calls_manager, + self._groups_service) self._tray.show() self._ms.show() diff --git a/toxygen/contacts/contact_menu.py b/toxygen/contacts/contact_menu.py index c580a3f..4a0dcaa 100644 --- a/toxygen/contacts/contact_menu.py +++ b/toxygen/contacts/contact_menu.py @@ -81,16 +81,28 @@ class BaseContactMenuGenerator: def __init__(self, contact): self._contact = contact - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number): + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): return ContactMenuBuilder().build() + def _generate_copy_menu_builder(self, main_screen): + copy_menu_builder = ContactMenuBuilder() + (copy_menu_builder + .with_name(util_ui.tr('Copy')) + .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) + .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.status_message)) + .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) + ) + + return copy_menu_builder + class FriendMenuGenerator(BaseContactMenuGenerator): - def generate(self, plugin_loader, contacts_manager, main_screen, settings, number): + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): history_menu_builder = self._generate_history_menu_builder(main_screen, number) copy_menu_builder = self._generate_copy_menu_builder(main_screen) plugins_menu_builder = self._generate_plugins_menu_builder(plugin_loader, number) + groups_menu_builder = self._generate_groups_menu(contacts_manager, groups_service) allowed = self._contact.tox_id in settings['auto_accept_from_friends'] auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept') @@ -105,6 +117,7 @@ class FriendMenuGenerator(BaseContactMenuGenerator): .with_action(util_ui.tr('Block friend'), lambda: main_screen.block_friend(number)) .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) .with_optional_submenu(plugins_menu_builder) + .with_optional_submenu(groups_menu_builder) ).build() return menu @@ -125,17 +138,6 @@ class FriendMenuGenerator(BaseContactMenuGenerator): return history_menu_builder - def _generate_copy_menu_builder(self, main_screen): - copy_menu_builder = ContactMenuBuilder() - (copy_menu_builder - .with_name(util_ui.tr('Copy')) - .with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name)) - .with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.status_message)) - .with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id)) - ) - - return copy_menu_builder - @staticmethod def _generate_plugins_menu_builder(plugin_loader, number): if plugin_loader is None: @@ -151,7 +153,30 @@ class FriendMenuGenerator(BaseContactMenuGenerator): return plugins_menu_builder - def _generate_groups_menu(self, contacts_manager): # TODO: fix + def _generate_groups_menu(self, contacts_manager, groups_service): chats = contacts_manager.get_group_chats() if not len(chats) or self._contact.status is None: return None + groups_menu_builder = ContactMenuBuilder() + (groups_menu_builder + .with_name(util_ui.tr('Invite to group')) + .with_actions([(g.name, lambda: groups_service.invite_friend(self._contact.number, g.number)) for g in chats]) + ) + + return groups_menu_builder + + +class GroupMenuGenerator(BaseContactMenuGenerator): + + def generate(self, plugin_loader, contacts_manager, main_screen, settings, number, groups_service): + copy_menu_builder = self._generate_copy_menu_builder(main_screen) + + builder = ContactMenuBuilder() + menu = (builder + .with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number)) + .with_submenu(copy_menu_builder) + .with_action(util_ui.tr('Leave group'), lambda: groups_service.leave_group(self._contact.number)) + .with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact)) + ).build() + + return menu diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index 7dad02f..6c19b26 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,4 +1,5 @@ from contacts.friend import Friend +from contacts.group_chat import GroupChat from messenger.messages import * @@ -47,7 +48,7 @@ class ContactsManager: if self.is_active_a_friend(): return False - return self.get_curr_contact().number == friend_number + return self.get_curr_contact().number == group_number # ----------------------------------------------------------------------------------------------------------------- # Work with active friend @@ -194,8 +195,11 @@ class ContactsManager: # Friend getters # ----------------------------------------------------------------------------------------------------------------- - def get_friend_by_number(self, num): - return list(filter(lambda x: x.number == num and type(x) is Friend, self._contacts))[0] + def get_friend_by_number(self, number): + return list(filter(lambda x: x.number == number and type(x) is Friend, self._contacts))[0] + + def get_group_by_number(self, number): + return list(filter(lambda x: x.number == number and type(x) is GroupChat, self._contacts))[0] def get_last_message(self): if self._active_contact + 1: @@ -261,8 +265,6 @@ class ContactsManager: except: pass self._settings.save() - # if num == self.get_active_number() and self.is_active_a_friend(): - # self.update() def friend_public_key(self, num): return self._contacts[num].tox_id @@ -277,22 +279,9 @@ class ContactsManager: :param num: number of friend in list """ friend = self._contacts[num] - try: - index = list(map(lambda x: x[0], self._settings['friends_aliases'])).index(friend.tox_id) - del self._settings['friends_aliases'][index] - except: - pass - if friend.tox_id in self._settings['notes']: - del self._settings['notes'][friend.tox_id] - self._settings.save() - self._history.delete_history(friend) + self._cleanup_contact_data(friend) self._tox.friend_delete(friend.number) - del self._contacts[num] - self._screen.friends_list.takeItem(num) - if num == self._active_contact: # active friend was deleted - self.set_active(0 if len(self._contacts) else -1) - data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self._delete_contact(num) def add_friend(self, tox_id): """ @@ -302,12 +291,8 @@ class ContactsManager: self._history.add_friend_to_db(tox_id) friend = self._contact_provider.get_friend_by_public_key(tox_id) self._contacts.append(friend) - friend.reset_avatar() - - def add_group(self, group_number): - group = self._contact_provider.get_group_by_number(group_number) - self._contacts.append(group) - group.reset_avatar() + friend.reset_avatar(self._settings['identicons']) + self._save_profile() def block_user(self, tox_id): """ @@ -343,7 +328,19 @@ class ContactsManager: # ----------------------------------------------------------------------------------------------------------------- def get_group_chats(self): - return list(filter(lambda c: type(c) is not Friend, self._contacts)) # TODO: fix after gc implementation + return list(filter(lambda c: type(c) is GroupChat, self._contacts)) + + def add_group(self, group_number): + group = self._contact_provider.get_group_by_number(group_number) + self._contacts.append(group) + group.reset_avatar(self._settings['identicons']) + self._save_profile() + + 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) # ----------------------------------------------------------------------------------------------------------------- # Friend requests @@ -454,3 +451,25 @@ class ContactsManager: pixmap = QtGui.QPixmap(avatar_path) self._screen.account_avatar.setPixmap(pixmap.scaled(width, width, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) + + 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) + + def _delete_contact(self, num): + del self._contacts[num] + self._screen.friends_list.takeItem(num) + if num == self._active_contact: # active friend was deleted + self.set_active(0 if len(self._contacts) else -1) + self._save_profile() diff --git a/toxygen/contacts/friend_factory.py b/toxygen/contacts/friend_factory.py index 9ba859f..a9b0477 100644 --- a/toxygen/contacts/friend_factory.py +++ b/toxygen/contacts/friend_factory.py @@ -39,4 +39,4 @@ class FriendFactory: Method-factory :return: new widget for friend instance """ - return self._items_factory.create_friend_item() + return self._items_factory.create_contact_item() diff --git a/toxygen/contacts/group_chat.py b/toxygen/contacts/group_chat.py index d37bbe7..79c2cfa 100644 --- a/toxygen/contacts/group_chat.py +++ b/toxygen/contacts/group_chat.py @@ -1,13 +1,14 @@ from contacts import contact +from contacts.contact_menu import GroupMenuGenerator import utils.util as util -from wrapper.toxcore_enums_and_consts import * +from groups.group_peer import GroupChatPeer from wrapper import toxcore_enums_and_consts as constants class GroupChat(contact.Contact): - def __init__(self, profile_manager, name, status_message, widget, tox, group_number): - super().__init__(None, group_number, profile_manager, name, status_message, widget, None) + def __init__(self, tox, profile_manager, message_getter, number, name, status_message, widget, tox_id): + super().__init__(profile_manager, message_getter, number, name, status_message, widget, tox_id) self._tox = tox self.set_status(constants.TOX_USER_STATUS['NONE']) self._peers = [] @@ -20,25 +21,29 @@ class GroupChat(contact.Contact): def remove_invalid_unsent_files(self): pass - def get_names(self): - peers_count = self._tox.group_number_peers(self._number) - names = [] - for i in range(peers_count): - name = self._tox.group_peername(self._number, i) - names.append(name) - names = sorted(names, key=lambda n: n.lower()) - return names + def get_context_menu_generator(self): + return GroupMenuGenerator(self) - def get_full_status(self): - names = self.get_names() - return '\n'.join(names) - - def get_peer_name(self, peer_number): - return self._tox.group_peername(self._number, peer_number) + # ----------------------------------------------------------------------------------------------------------------- + # Peers methods + # ----------------------------------------------------------------------------------------------------------------- def get_self_name(self): return self._peers[0].name + def add_peer(self, peer_id): + peer = GroupChatPeer(peer_id, + self._tox.group_peer_get_name(self._number, peer_id), + self._tox.group_peer_get_status(self._number, peer_id), + self._tox.group_peer_get_role(self._number, peer_id), + self._tox.group_peer_get_public_key(self._number, peer_id)) + self._peers.append(peer) + + def get_peer(self, peer_id): + peers = list(filter(lambda p: p.id == peer_id, self._peers)) + + return peers[0] + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- @@ -48,4 +53,5 @@ class GroupChat(contact.Contact): return util.join_path(util.get_images_directory(), 'group.png') def _add_self_to_gc(self): - pass + peer_id = self._tox.group_self_get_peer_id(self._number) + self.add_peer(peer_id) diff --git a/toxygen/contacts/group_factory.py b/toxygen/contacts/group_factory.py index 953c483..089fc09 100644 --- a/toxygen/contacts/group_factory.py +++ b/toxygen/contacts/group_factory.py @@ -1,6 +1,49 @@ +from contacts.group_chat import GroupChat class GroupFactory: + def __init__(self, profile_manager, settings, tox, db, items_factory): + self._profile_manager = profile_manager + self._settings, self._tox = settings, tox + self._db = db + self._items_factory = items_factory + def create_group_by_public_key(self, public_key): - pass + group_number = self._get_group_number_by_chat_id(public_key) + + return self.create_group_by_number(group_number) + + def create_group_by_number(self, group_number): + aliases = self._settings['friends_aliases'] + tox_id = self._tox.group_get_chat_id(group_number) + try: + alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1] + except: + alias = '' + item = self._create_group_item() + name = alias or self._tox.group_get_name(group_number) or tox_id + status_message = self._tox.group_get_topic(group_number) + message_getter = self._db.messages_getter(tox_id) + group = GroupChat(self._tox, self._profile_manager, message_getter, group_number, name, status_message, + item, tox_id) + group.set_alias(alias) + + return group + + # ----------------------------------------------------------------------------------------------------------------- + # Private methods + # ----------------------------------------------------------------------------------------------------------------- + + def _create_group_item(self): + """ + Method-factory + :return: new widget for group instance + """ + return self._items_factory.create_contact_item() + + def _get_group_number_by_chat_id(self, chat_id): + for i in range(self._tox.group_get_number_groups()): + if self._tox.group_get_chat_id(i) == chat_id: + return i + return -1 diff --git a/toxygen/groups/group_peer.py b/toxygen/groups/group_peer.py index f91decc..6736185 100644 --- a/toxygen/groups/group_peer.py +++ b/toxygen/groups/group_peer.py @@ -2,9 +2,43 @@ class GroupChatPeer: - def __init__(self, peer_number, name, status, role, public_key): - self.peer_number = peer_number - self.name = name - self.status = status - self.role = role - self.public_key = public_key + def __init__(self, peer_id, name, status, role, public_key): + self._peer_id = peer_id + self._name = name + self._status = status + self._role = role + self._public_key = public_key + + def get_id(self): + return self._peer_id + + id = property(get_id) + + def get_name(self): + return self._name + + def set_name(self, name): + self._name = name + + name = property(get_name, set_name) + + def get_status(self): + return self._status + + def set_status(self, status): + self._status = status + + status = property(get_status, set_status) + + def get_role(self): + return self._role + + def set_role(self, role): + self._role = role + + role = property(get_role, set_role) + + def get_public_key(self): + return self._public_key + + public_key = property(get_public_key) diff --git a/toxygen/groups/groups_service.py b/toxygen/groups/groups_service.py index febdb9e..beb0853 100644 --- a/toxygen/groups/groups_service.py +++ b/toxygen/groups/groups_service.py @@ -1,4 +1,5 @@ import common.tox_save as tox_save +import utils.ui as util_ui class GroupsService(tox_save.ToxSave): @@ -24,9 +25,38 @@ class GroupsService(tox_save.ToxSave): group_number = self._tox.group_invite_accept(invite_data, friend_number, password) self._add_new_group_by_number(group_number) + # ----------------------------------------------------------------------------------------------------------------- + # Groups reconnect and leaving + # ----------------------------------------------------------------------------------------------------------------- + + def leave_group(self, group_number): + group = self._get_group(group_number) + self._tox.group_leave(group_number) + self._contacts_manager.delete_group(group_number) + self._contacts_provider.remove_contact_from_cache(group.tox_id) + + # ----------------------------------------------------------------------------------------------------------------- + # Group invites + # ----------------------------------------------------------------------------------------------------------------- + + def invite_friend(self, friend_number, group_number): + self._tox.group_invite_friend(group_number, friend_number) + + def process_group_invite(self, friend_number, invite_data): + friend = self._get_friend(friend_number) + text = util_ui.tr('Friend {} invites you to group. Accept?') + if util_ui.question(text.format(friend.name), util_ui.tr('Group invite')): + self.join_gc_via_invite(invite_data, friend_number, None) + # ----------------------------------------------------------------------------------------------------------------- # Private methods # ----------------------------------------------------------------------------------------------------------------- def _add_new_group_by_number(self, group_number): self._contacts_manager.add_group(group_number) + + def _get_group(self, group_number): + return self._contacts_provider.get_group_by_number(group_number) + + def _get_friend(self, friend_number): + return self._contacts_provider.get_friend_by_number(friend_number) diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 40b3284..7b0872d 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -398,8 +398,26 @@ def group_self_join(contacts_provider): def group_peer_join(contacts_provider): def wrapped(tox, group_number, peer_id, user_data): - gc = contacts_provider.get_group_by_number(group_number) - gc.add_peer(peer_id) + group = contacts_provider.get_group_by_number(group_number) + group.add_peer(peer_id) + + return wrapped + + +def group_peer_name(contacts_provider): + def wrapped(tox, group_number, peer_id, name, length, user_data): + group = contacts_provider.get_group_by_number(group_number) + peer = group.get_peer(peer_id) + peer.name = str(name[:length]) + + return wrapped + + +def group_peer_status(contacts_provider): + def wrapped(tox, group_number, peer_id, peer_status, user_data): + group = contacts_provider.get_group_by_number(group_number) + peer = group.get_peer(peer_id) + peer.status = peer_status return wrapped @@ -463,3 +481,5 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_group_invite(group_invite(groups_service), 0) tox.callback_group_self_join(group_self_join(contacts_provider), 0) tox.callback_group_peer_join(group_peer_join(contacts_provider), 0) + tox.callback_group_peer_name(group_peer_name(contacts_provider), 0) + tox.callback_group_peer_status(group_peer_status(contacts_provider), 0) diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 6197066..2ea5660 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -2,13 +2,13 @@ from ui.contact_items import * from ui.messages_widgets import * -class FriendItemsFactory: +class ContactItemsFactory: def __init__(self, settings, main_screen): self._settings = settings self._friends_list = main_screen.friends_list - def create_friend_item(self): + def create_contact_item(self): item = ContactItem(self._settings) elem = QtWidgets.QListWidgetItem(self._friends_list) elem.setSizeHint(QtCore.QSize(250, item.height())) diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index fd404b2..9a4df47 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -18,11 +18,11 @@ class MainWindow(QtWidgets.QMainWindow): self.setAcceptDrops(True) self._saved = False self._profile = None - self._file_transfer_handler = self._history_loader = self._calls_manager = None + self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None self.initUI() def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, - file_transfer_handler, history_loader, calls_manager): + file_transfer_handler, history_loader, calls_manager, groups_service): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager @@ -31,6 +31,7 @@ class MainWindow(QtWidgets.QMainWindow): self._file_transfer_handler = file_transfer_handler self._history_loader = history_loader self._calls_manager = calls_manager + self._groups_service = groups_service self.messageEdit.set_messenger(messenger) def show(self): @@ -414,7 +415,6 @@ class MainWindow(QtWidgets.QMainWindow): self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) self.messageEdit.setFocus() - #self.profile.update() def keyPressEvent(self, event): key, modifiers = event.key(), event.modifiers() @@ -598,7 +598,8 @@ class MainWindow(QtWidgets.QMainWindow): if contact is None or item is None: return generator = contact.get_context_menu_generator() - self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number) + self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number, + self._groups_service) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) self.listMenu.move(parent_position + pos) self.listMenu.show() diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py index 18c2439..f455e30 100644 --- a/toxygen/wrapper/tox.py +++ b/toxygen/wrapper/tox.py @@ -1599,7 +1599,7 @@ class Tox: result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, groupnumber, byref(error)) return result - def group_leave(self, groupnumber, message): + def group_leave(self, groupnumber, message=''): """ Leaves a group. @@ -1887,7 +1887,7 @@ class Tox: """ error = c_int() result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, groupnumber, byref(error)) - return result + return int(result) def group_get_name(self, groupnumber): """