diff options
author | wurstsalat <mailtrash@posteo.de> | 2022-08-29 00:48:51 +0300 |
---|---|---|
committer | wurstsalat <mailtrash@posteo.de> | 2022-08-29 00:49:25 +0300 |
commit | 720472e274c64f03b5c8682d8b8f59c20aa52b97 (patch) | |
tree | 7c0dffc77ae5cec51f34f7bc5323d9d670e43e76 | |
parent | 8431f218054de1bfb6ffb29ecd32b24d0c735032 (diff) |
change: ChatBanner: Add button for managing voice requestsvoice-request-button
-rw-r--r-- | gajim/common/modules/muc.py | 28 | ||||
-rw-r--r-- | gajim/data/gui/chat_banner.ui | 4 | ||||
-rw-r--r-- | gajim/data/style/gajim.css | 12 | ||||
-rw-r--r-- | gajim/gtk/builder.pyi | 3 | ||||
-rw-r--r-- | gajim/gtk/chat_banner.py | 16 | ||||
-rw-r--r-- | gajim/gtk/control.py | 26 | ||||
-rw-r--r-- | gajim/gtk/groupchat_voice_requests_button.py | 150 | ||||
-rw-r--r-- | pyrightconfig.json | 1 |
8 files changed, 208 insertions, 32 deletions
diff --git a/gajim/common/modules/muc.py b/gajim/common/modules/muc.py index 4521cacbd..57c32e1a5 100644 --- a/gajim/common/modules/muc.py +++ b/gajim/common/modules/muc.py @@ -39,6 +39,7 @@ from nbxmpp.structs import DiscoInfo from nbxmpp.structs import MessageProperties from nbxmpp.structs import PresenceProperties from nbxmpp.structs import StanzaHandler +from nbxmpp.structs import VoiceRequest from nbxmpp.task import Task from gi.repository import GLib @@ -149,6 +150,8 @@ class MUC(BaseModule): str, dict[str, MUCPresenceData]] = defaultdict(dict) self._mucs: dict[str, MUCData] = {} self._muc_nicknames = {} + self._voice_requests: dict[ + GroupchatContact, list[VoiceRequest]] = defaultdict(list) def _on_resume_failed(self, _client: types.Client, @@ -808,10 +811,35 @@ class MUC(BaseModule): return room = self._get_contact(properties.jid.bare) + assert isinstance(room, GroupchatContact) + assert properties.voice_request is not None + + self._voice_requests[room].append(properties.voice_request) room.notify('room-voice-request', properties) raise nbxmpp.NodeProcessed + def get_voice_requests(self, + contact: GroupchatContact + ) -> Optional[list[VoiceRequest]]: + + return self._voice_requests.get(contact, []) + + def approve_voice_request(self, + contact: GroupchatContact, + voice_request: VoiceRequest + ) -> None: + + self._voice_requests[contact].remove(voice_request) + self._nbxmpp('MUC').approve_voice_request(contact.jid, voice_request) + + def decline_voice_request(self, + contact: GroupchatContact, + voice_request: VoiceRequest + ) -> None: + + self._voice_requests[contact].remove(voice_request) + def _on_captcha_challenge(self, _con: types.xmppClient, _stanza: Message, diff --git a/gajim/data/gui/chat_banner.ui b/gajim/data/gui/chat_banner.ui index 2b6b4d12b..d2a17de69 100644 --- a/gajim/data/gui/chat_banner.ui +++ b/gajim/data/gui/chat_banner.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.38.2 --> +<!-- Generated with glade 3.40.0 --> <interface> <requires lib="gtk+" version="3.24"/> <object class="GtkBox" id="banner_box"> @@ -125,7 +125,7 @@ </packing> </child> <child> - <object class="GtkBox" id="account_badge_box"> + <object class="GtkBox" id="additional_items_box"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="halign">end</property> diff --git a/gajim/data/style/gajim.css b/gajim/data/style/gajim.css index 3b7fa4910..6f752a474 100644 --- a/gajim/data/style/gajim.css +++ b/gajim/data/style/gajim.css @@ -813,6 +813,18 @@ button.flat.link { padding: 0; border: 0; } font-weight: bold; } +@keyframes pulse-opacity { + 0% { opacity: 1.0; } + 20% { opacity: 0.5; } + 45% { opacity: 1.0; } +} +.pulse-opacity { + animation-name: pulse-opacity; + animation-duration: 2.0s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; +} + @keyframes pulse { 0% { -gtk-icon-transform: scale(1.0); } 20% { -gtk-icon-transform: scale(1.1); } diff --git a/gajim/gtk/builder.pyi b/gajim/gtk/builder.pyi index e4004f4e2..7e1e51aad 100644 --- a/gajim/gtk/builder.pyi +++ b/gajim/gtk/builder.pyi @@ -18,7 +18,6 @@ class AccountPageBuilder(Builder): avatar_image: Gtk.Image account_label: Gtk.Label account_action_box: Gtk.Box - account_settings: Gtk.Button status_message_box: Gtk.Box @@ -202,7 +201,7 @@ class ChatBannerBuilder(Builder): phone_image: Gtk.Image toggle_roster_button: Gtk.Button toggle_roster_image: Gtk.Image - account_badge_box: Gtk.Box + additional_items_box: Gtk.Box visitor_box: Gtk.Box visitor_menu_button: Gtk.MenuButton visitor_popover: Gtk.Popover diff --git a/gajim/gtk/chat_banner.py b/gajim/gtk/chat_banner.py index a6d48ed31..6fc24d3ec 100644 --- a/gajim/gtk/chat_banner.py +++ b/gajim/gtk/chat_banner.py @@ -40,6 +40,7 @@ from gajim.common.modules.contacts import GroupchatContact from gajim.common.modules.contacts import GroupchatParticipant from .builder import get_builder +from .groupchat_voice_requests_button import VoiceRequestsButton from .util import AccountBadge @@ -58,6 +59,9 @@ class ChatBanner(Gtk.Box, EventHelper): self._ui.connect_signals(self) self._account_badge: Optional[AccountBadge] = None + self._voice_requests_button = VoiceRequestsButton() + self._ui.additional_items_box.pack_start( + self._voice_requests_button, False, True, 0) hide_roster = app.settings.get('hide_groupchat_occupants_list') self._set_toggle_roster_button_icon(hide_roster) @@ -103,7 +107,8 @@ class ChatBanner(Gtk.Box, EventHelper): if isinstance(self._contact, GroupchatContact): self._contact.multi_connect({ 'user-role-changed': self._on_user_role_changed, - 'state-changed': self._on_muc_state_changed + 'state-changed': self._on_muc_state_changed, + 'room-voice-request': self._on_room_voice_request }) self._ui.toggle_roster_button.show() hide_banner = app.settings.get('hide_groupchat_banner') @@ -130,6 +135,8 @@ class ChatBanner(Gtk.Box, EventHelper): self._ui.phone_image.set_visible( self._contact in self._last_message_from_phone) + self._voice_requests_button.switch_contact(self._contact) + if hide_banner: self.set_no_show_all(True) self.hide() @@ -184,6 +191,10 @@ class ChatBanner(Gtk.Box, EventHelper): if contact.is_joined: self._update_content() + def _on_room_voice_request(self, *args: Any) -> None: + self._voice_requests_button.set_no_show_all(False) + self._voice_requests_button.show_all() + def _on_user_role_changed(self, _contact: GroupchatContact, _signal_name: str, @@ -284,7 +295,8 @@ class ChatBanner(Gtk.Box, EventHelper): enabled_accounts = app.get_enabled_accounts_with_labels() if len(enabled_accounts) > 1: self._account_badge = AccountBadge(account) - self._ui.account_badge_box.add(self._account_badge) + self._ui.additional_items_box.pack_end( + self._account_badge, False, True, 0) def _on_request_voice_clicked(self, _button: Gtk.Button) -> None: self._ui.visitor_popover.popdown() diff --git a/gajim/gtk/control.py b/gajim/gtk/control.py index 6236b4ef6..f0e388375 100644 --- a/gajim/gtk/control.py +++ b/gajim/gtk/control.py @@ -57,8 +57,6 @@ from gajim.common.storage.archive import ConversationRow from gajim.gui.conversation.scrolled import ScrolledView from gajim.gui.conversation.jump_to_end_button import JumpToEndButton from gajim.gui.builder import get_builder -from gajim.gui.dialogs import DialogButton -from gajim.gui.dialogs import ConfirmationDialog from gajim.gui.groupchat_roster import GroupchatRoster from gajim.gui.groupchat_state import GroupchatState @@ -200,7 +198,6 @@ class ChatControl(EventHelper): 'room-config-finished': self._on_room_config_finished, 'room-config-changed': self._on_room_config_changed, 'room-presence-error': self._on_room_presence_error, - 'room-voice-request': self._on_room_voice_request, 'room-subject': self._on_room_subject, }) @@ -779,29 +776,6 @@ class ChatControl(EventHelper): status=status) self.add_info_message(message) - def _on_room_voice_request(self, - _contact: GroupchatContact, - _signal_name: str, - properties: MessageProperties - ) -> None: - voice_request = properties.voice_request - assert voice_request is not None - - def on_approve() -> None: - self.client.get_module('MUC').approve_voice_request( - self.contact.jid, voice_request) - - ConfirmationDialog( - _('Voice Request'), - _('Voice Request'), - _('<b>%(nick)s</b> from <b>%(room_name)s</b> requests voice') % { - 'nick': voice_request.nick, 'room_name': self.contact.name}, - [DialogButton.make('Cancel'), - DialogButton.make('Accept', - text=_('_Approve'), - callback=on_approve)], - modal=False).show() - def add_muc_message(self, text: str, tim: float, diff --git a/gajim/gtk/groupchat_voice_requests_button.py b/gajim/gtk/groupchat_voice_requests_button.py new file mode 100644 index 000000000..8e43041cd --- /dev/null +++ b/gajim/gtk/groupchat_voice_requests_button.py @@ -0,0 +1,150 @@ +# 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/>. + +from __future__ import annotations + +from typing import cast + +from gi.repository import GLib +from gi.repository import Gtk +from gi.repository import Pango + +from nbxmpp.structs import VoiceRequest + +from gajim.common import app +from gajim.common.types import ChatContactT +from gajim.common.i18n import _ +from gajim.common.modules.contacts import GroupchatContact + + +class VoiceRequestsButton(Gtk.Button): + def __init__(self) -> None: + Gtk.Button.__init__(self) + self.set_tooltip_text(_('Pending Voice Requests')) + self.set_no_show_all(True) + image = Gtk.Image.new_from_icon_name( + 'dialog-question-symbolic', Gtk.IconSize.BUTTON) + self.add(image) + self.get_style_context().add_class('pulse-opacity') + self.connect('clicked', self._on_button_clicked) + + def switch_contact(self, contact: ChatContactT) -> None: + if not isinstance(contact, GroupchatContact): + self._contact = None + self.set_no_show_all(True) + self.hide() + return + + self._contact = contact + self._update() + + def _update(self) -> None: + assert self._contact is not None + client = app.get_client(self._contact.account) + voice_requests = client.get_module('MUC').get_voice_requests( + self._contact) + self.hide() + if voice_requests: + self.set_no_show_all(False) + self.show_all() + + def _on_button_clicked(self, _button: VoiceRequestsButton) -> None: + assert self._contact is not None + client = app.get_client(self._contact.account) + voice_requests = client.get_module('MUC').get_voice_requests( + self._contact) + if not voice_requests: + return + + menu_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + menu_box.get_style_context().add_class('padding-12') + menu_box.set_hexpand(True) + + desc_label = Gtk.Label(label=_('Participants asking for voice:')) + desc_label.get_style_context().add_class('dim-label') + desc_label.set_max_width_chars(35) + desc_label.set_line_wrap(True) + desc_label.set_margin_bottom(6) + menu_box.add(desc_label) + + for request in cast(list[VoiceRequest], voice_requests): + request_box = Gtk.Box(spacing=12) + + name_label = Gtk.Label(label=request.nick) + name_label.set_width_chars(10) + name_label.set_max_width_chars(20) + name_label.set_ellipsize(Pango.EllipsizeMode.END) + name_label.set_xalign(0) + request_box.add(name_label) + + decline_button = Gtk.Button.new_from_icon_name( + 'process-stop-symbolic', Gtk.IconSize.BUTTON) + decline_button.set_tooltip_text(_('Decline')) + decline_button.connect( + 'clicked', + self._on_decline, + request, + self._contact) + request_box.pack_end(decline_button, False, False, 0) + + approve_button = Gtk.Button.new_from_icon_name( + 'feather-check-symbolic', Gtk.IconSize.BUTTON) + approve_button.set_tooltip_text(_('Approve')) + approve_button.connect( + 'clicked', + self._on_approve, + request, + self._contact) + request_box.pack_end(approve_button, False, False, 0) + + if voice_requests.index(request) > 0: + menu_box.add(Gtk.Separator()) + + menu_box.add(request_box) + + menu_box.show_all() + + popover = Gtk.PopoverMenu() + popover.get_style_context().add_class('padding-6') + popover.set_relative_to(self) + popover.set_position(Gtk.PositionType.BOTTOM) + popover.add(menu_box) + popover.connect('closed', self._on_closed) + popover.popup() + + @staticmethod + def _on_closed(popover: Gtk.Popover) -> None: + GLib.idle_add(popover.destroy) + + def _on_approve(self, + _button: Gtk.Button, + voice_request: VoiceRequest, + contact: GroupchatContact + ) -> None: + + client = app.get_client(contact.account) + client.get_module('MUC').approve_voice_request( + contact, voice_request) + self._update() + + def _on_decline(self, + _button: Gtk.Button, + voice_request: VoiceRequest, + contact: GroupchatContact + ) -> None: + + client = app.get_client(contact.account) + client.get_module('MUC').decline_voice_request( + contact, voice_request) + self._update() diff --git a/pyrightconfig.json b/pyrightconfig.json index 1a1f3cff2..05e6e8ed9 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -90,6 +90,7 @@ "gajim/gtk/groupchat_roster.py", "gajim/gtk/groupchat_settings.py", "gajim/gtk/groupchat_state.py", + "gajim/gtk/groupchat_voice_requests_button.py", "gajim/gtk/gstreamer.py", "gajim/gtk/history_export.py", "gajim/gtk/history_sync.py", |