From 2e62eb1186cd8f8efb067b04524fac1c94787d5c Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 25 Nov 2020 17:09:59 +0300 Subject: [PATCH] Start group call members list. --- Telegram/CMakeLists.txt | 2 + .../icons/{ => calls}/call_answer.png | Bin .../icons/{ => calls}/call_answer@2x.png | Bin .../icons/{ => calls}/call_answer@3x.png | Bin .../icons/{ => calls}/call_arrow_in.png | Bin .../icons/{ => calls}/call_arrow_in@2x.png | Bin .../icons/{ => calls}/call_arrow_in@3x.png | Bin .../icons/{ => calls}/call_arrow_out.png | Bin .../icons/{ => calls}/call_arrow_out@2x.png | Bin .../icons/{ => calls}/call_arrow_out@3x.png | Bin .../icons/{ => calls}/call_camera_active.png | Bin .../{ => calls}/call_camera_active@2x.png | Bin .../{ => calls}/call_camera_active@3x.png | Bin .../icons/{ => calls}/call_camera_muted.png | Bin .../{ => calls}/call_camera_muted@2x.png | Bin .../{ => calls}/call_camera_muted@3x.png | Bin .../icons/{ => calls}/call_cancel.png | Bin .../icons/{ => calls}/call_cancel@2x.png | Bin .../icons/{ => calls}/call_cancel@3x.png | Bin .../icons/{ => calls}/call_discard.png | Bin .../icons/{ => calls}/call_discard@2x.png | Bin .../icons/{ => calls}/call_discard@3x.png | Bin .../icons/{ => calls}/call_rating.png | Bin .../icons/{ => calls}/call_rating@2x.png | Bin .../icons/{ => calls}/call_rating@3x.png | Bin .../icons/{ => calls}/call_rating_filled.png | Bin .../{ => calls}/call_rating_filled@2x.png | Bin .../{ => calls}/call_rating_filled@3x.png | Bin .../icons/{ => calls}/call_record_active.png | Bin .../{ => calls}/call_record_active@2x.png | Bin .../{ => calls}/call_record_active@3x.png | Bin .../icons/{ => calls}/call_record_muted.png | Bin .../{ => calls}/call_record_muted@2x.png | Bin .../{ => calls}/call_record_muted@3x.png | Bin .../icons/{ => calls}/call_shadow_left.png | Bin .../icons/{ => calls}/call_shadow_left@2x.png | Bin .../icons/{ => calls}/call_shadow_left@3x.png | Bin .../icons/{ => calls}/call_shadow_top.png | Bin .../icons/{ => calls}/call_shadow_top@2x.png | Bin .../icons/{ => calls}/call_shadow_top@3x.png | Bin .../{ => calls}/call_shadow_top_left.png | Bin .../{ => calls}/call_shadow_top_left@2x.png | Bin .../{ => calls}/call_shadow_top_left@3x.png | Bin .../icons/{ => calls}/calls_close_main.png | Bin .../icons/{ => calls}/calls_close_main@2x.png | Bin .../icons/{ => calls}/calls_close_main@3x.png | Bin .../icons/{ => calls}/calls_close_shadow.png | Bin .../{ => calls}/calls_close_shadow@2x.png | Bin .../{ => calls}/calls_close_shadow@3x.png | Bin .../icons/{ => calls}/calls_maximize_main.png | Bin .../{ => calls}/calls_maximize_main@2x.png | Bin .../{ => calls}/calls_maximize_main@3x.png | Bin .../{ => calls}/calls_maximize_shadow.png | Bin .../{ => calls}/calls_maximize_shadow@2x.png | Bin .../{ => calls}/calls_maximize_shadow@3x.png | Bin .../icons/{ => calls}/calls_minimize_main.png | Bin .../{ => calls}/calls_minimize_main@2x.png | Bin .../{ => calls}/calls_minimize_main@3x.png | Bin .../{ => calls}/calls_minimize_shadow.png | Bin .../{ => calls}/calls_minimize_shadow@2x.png | Bin .../{ => calls}/calls_minimize_shadow@3x.png | Bin .../icons/{ => calls}/calls_mute_tooltip.png | Bin .../{ => calls}/calls_mute_tooltip@2x.png | Bin .../{ => calls}/calls_mute_tooltip@3x.png | Bin .../icons/{ => calls}/calls_mute_userpic.png | Bin .../{ => calls}/calls_mute_userpic@2x.png | Bin .../{ => calls}/calls_mute_userpic@3x.png | Bin .../icons/{ => calls}/calls_restore_main.png | Bin .../{ => calls}/calls_restore_main@2x.png | Bin .../{ => calls}/calls_restore_main@3x.png | Bin .../{ => calls}/calls_restore_shadow.png | Bin .../{ => calls}/calls_restore_shadow@2x.png | Bin .../{ => calls}/calls_restore_shadow@3x.png | Bin .../{ => calls}/calls_shadow_controls.png | Bin .../{ => calls}/calls_shadow_controls@2x.png | Bin .../{ => calls}/calls_shadow_controls@3x.png | Bin .../icons/calls/group_calls_muted.png | Bin 0 -> 718 bytes .../icons/calls/group_calls_muted@2x.png | Bin 0 -> 1288 bytes .../icons/calls/group_calls_muted@3x.png | Bin 0 -> 1894 bytes .../icons/calls/group_calls_unmuted.png | Bin 0 -> 599 bytes .../icons/calls/group_calls_unmuted@2x.png | Bin 0 -> 1073 bytes .../icons/calls/group_calls_unmuted@3x.png | Bin 0 -> 1563 bytes Telegram/SourceFiles/boxes/boxes.style | 1 - Telegram/SourceFiles/boxes/peer_list_box.cpp | 2 +- .../boxes/peers/edit_participants_box.h | 1 - Telegram/SourceFiles/calls/calls.style | 167 ++++-- .../SourceFiles/calls/calls_group_members.cpp | 528 ++++++++++++++++++ .../SourceFiles/calls/calls_group_members.h | 76 +++ .../SourceFiles/calls/calls_group_panel.cpp | 127 ++--- .../SourceFiles/calls/calls_group_panel.h | 10 +- .../view/history_view_group_call_tracker.cpp | 2 +- Telegram/SourceFiles/ui/chat/chat.style | 28 +- Telegram/lib_ui | 2 +- 93 files changed, 797 insertions(+), 149 deletions(-) rename Telegram/Resources/icons/{ => calls}/call_answer.png (100%) rename Telegram/Resources/icons/{ => calls}/call_answer@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_answer@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_arrow_in.png (100%) rename Telegram/Resources/icons/{ => calls}/call_arrow_in@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_arrow_in@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_arrow_out.png (100%) rename Telegram/Resources/icons/{ => calls}/call_arrow_out@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_arrow_out@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_camera_active.png (100%) rename Telegram/Resources/icons/{ => calls}/call_camera_active@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_camera_active@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_camera_muted.png (100%) rename Telegram/Resources/icons/{ => calls}/call_camera_muted@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_camera_muted@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_cancel.png (100%) rename Telegram/Resources/icons/{ => calls}/call_cancel@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_cancel@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_discard.png (100%) rename Telegram/Resources/icons/{ => calls}/call_discard@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_discard@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_rating.png (100%) rename Telegram/Resources/icons/{ => calls}/call_rating@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_rating@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_rating_filled.png (100%) rename Telegram/Resources/icons/{ => calls}/call_rating_filled@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_rating_filled@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_record_active.png (100%) rename Telegram/Resources/icons/{ => calls}/call_record_active@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_record_active@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_record_muted.png (100%) rename Telegram/Resources/icons/{ => calls}/call_record_muted@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_record_muted@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_left.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_left@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_left@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_top.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_top@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_top@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_top_left.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_top_left@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/call_shadow_top_left@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_close_main.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_close_main@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_close_main@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_close_shadow.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_close_shadow@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_close_shadow@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_maximize_main.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_maximize_main@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_maximize_main@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_maximize_shadow.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_maximize_shadow@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_maximize_shadow@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_minimize_main.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_minimize_main@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_minimize_main@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_minimize_shadow.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_minimize_shadow@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_minimize_shadow@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_mute_tooltip.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_mute_tooltip@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_mute_tooltip@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_mute_userpic.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_mute_userpic@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_mute_userpic@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_restore_main.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_restore_main@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_restore_main@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_restore_shadow.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_restore_shadow@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_restore_shadow@3x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_shadow_controls.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_shadow_controls@2x.png (100%) rename Telegram/Resources/icons/{ => calls}/calls_shadow_controls@3x.png (100%) create mode 100644 Telegram/Resources/icons/calls/group_calls_muted.png create mode 100644 Telegram/Resources/icons/calls/group_calls_muted@2x.png create mode 100644 Telegram/Resources/icons/calls/group_calls_muted@3x.png create mode 100644 Telegram/Resources/icons/calls/group_calls_unmuted.png create mode 100644 Telegram/Resources/icons/calls/group_calls_unmuted@2x.png create mode 100644 Telegram/Resources/icons/calls/group_calls_unmuted@3x.png create mode 100644 Telegram/SourceFiles/calls/calls_group_members.cpp create mode 100644 Telegram/SourceFiles/calls/calls_group_members.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index a483dabc4..c64e375c1 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -266,6 +266,8 @@ PRIVATE calls/calls_call.h calls/calls_group_call.cpp calls/calls_group_call.h + calls/calls_group_members.cpp + calls/calls_group_members.h calls/calls_group_panel.cpp calls/calls_group_panel.h calls/calls_emoji_fingerprint.cpp diff --git a/Telegram/Resources/icons/call_answer.png b/Telegram/Resources/icons/calls/call_answer.png similarity index 100% rename from Telegram/Resources/icons/call_answer.png rename to Telegram/Resources/icons/calls/call_answer.png diff --git a/Telegram/Resources/icons/call_answer@2x.png b/Telegram/Resources/icons/calls/call_answer@2x.png similarity index 100% rename from Telegram/Resources/icons/call_answer@2x.png rename to Telegram/Resources/icons/calls/call_answer@2x.png diff --git a/Telegram/Resources/icons/call_answer@3x.png b/Telegram/Resources/icons/calls/call_answer@3x.png similarity index 100% rename from Telegram/Resources/icons/call_answer@3x.png rename to Telegram/Resources/icons/calls/call_answer@3x.png diff --git a/Telegram/Resources/icons/call_arrow_in.png b/Telegram/Resources/icons/calls/call_arrow_in.png similarity index 100% rename from Telegram/Resources/icons/call_arrow_in.png rename to Telegram/Resources/icons/calls/call_arrow_in.png diff --git a/Telegram/Resources/icons/call_arrow_in@2x.png b/Telegram/Resources/icons/calls/call_arrow_in@2x.png similarity index 100% rename from Telegram/Resources/icons/call_arrow_in@2x.png rename to Telegram/Resources/icons/calls/call_arrow_in@2x.png diff --git a/Telegram/Resources/icons/call_arrow_in@3x.png b/Telegram/Resources/icons/calls/call_arrow_in@3x.png similarity index 100% rename from Telegram/Resources/icons/call_arrow_in@3x.png rename to Telegram/Resources/icons/calls/call_arrow_in@3x.png diff --git a/Telegram/Resources/icons/call_arrow_out.png b/Telegram/Resources/icons/calls/call_arrow_out.png similarity index 100% rename from Telegram/Resources/icons/call_arrow_out.png rename to Telegram/Resources/icons/calls/call_arrow_out.png diff --git a/Telegram/Resources/icons/call_arrow_out@2x.png b/Telegram/Resources/icons/calls/call_arrow_out@2x.png similarity index 100% rename from Telegram/Resources/icons/call_arrow_out@2x.png rename to Telegram/Resources/icons/calls/call_arrow_out@2x.png diff --git a/Telegram/Resources/icons/call_arrow_out@3x.png b/Telegram/Resources/icons/calls/call_arrow_out@3x.png similarity index 100% rename from Telegram/Resources/icons/call_arrow_out@3x.png rename to Telegram/Resources/icons/calls/call_arrow_out@3x.png diff --git a/Telegram/Resources/icons/call_camera_active.png b/Telegram/Resources/icons/calls/call_camera_active.png similarity index 100% rename from Telegram/Resources/icons/call_camera_active.png rename to Telegram/Resources/icons/calls/call_camera_active.png diff --git a/Telegram/Resources/icons/call_camera_active@2x.png b/Telegram/Resources/icons/calls/call_camera_active@2x.png similarity index 100% rename from Telegram/Resources/icons/call_camera_active@2x.png rename to Telegram/Resources/icons/calls/call_camera_active@2x.png diff --git a/Telegram/Resources/icons/call_camera_active@3x.png b/Telegram/Resources/icons/calls/call_camera_active@3x.png similarity index 100% rename from Telegram/Resources/icons/call_camera_active@3x.png rename to Telegram/Resources/icons/calls/call_camera_active@3x.png diff --git a/Telegram/Resources/icons/call_camera_muted.png b/Telegram/Resources/icons/calls/call_camera_muted.png similarity index 100% rename from Telegram/Resources/icons/call_camera_muted.png rename to Telegram/Resources/icons/calls/call_camera_muted.png diff --git a/Telegram/Resources/icons/call_camera_muted@2x.png b/Telegram/Resources/icons/calls/call_camera_muted@2x.png similarity index 100% rename from Telegram/Resources/icons/call_camera_muted@2x.png rename to Telegram/Resources/icons/calls/call_camera_muted@2x.png diff --git a/Telegram/Resources/icons/call_camera_muted@3x.png b/Telegram/Resources/icons/calls/call_camera_muted@3x.png similarity index 100% rename from Telegram/Resources/icons/call_camera_muted@3x.png rename to Telegram/Resources/icons/calls/call_camera_muted@3x.png diff --git a/Telegram/Resources/icons/call_cancel.png b/Telegram/Resources/icons/calls/call_cancel.png similarity index 100% rename from Telegram/Resources/icons/call_cancel.png rename to Telegram/Resources/icons/calls/call_cancel.png diff --git a/Telegram/Resources/icons/call_cancel@2x.png b/Telegram/Resources/icons/calls/call_cancel@2x.png similarity index 100% rename from Telegram/Resources/icons/call_cancel@2x.png rename to Telegram/Resources/icons/calls/call_cancel@2x.png diff --git a/Telegram/Resources/icons/call_cancel@3x.png b/Telegram/Resources/icons/calls/call_cancel@3x.png similarity index 100% rename from Telegram/Resources/icons/call_cancel@3x.png rename to Telegram/Resources/icons/calls/call_cancel@3x.png diff --git a/Telegram/Resources/icons/call_discard.png b/Telegram/Resources/icons/calls/call_discard.png similarity index 100% rename from Telegram/Resources/icons/call_discard.png rename to Telegram/Resources/icons/calls/call_discard.png diff --git a/Telegram/Resources/icons/call_discard@2x.png b/Telegram/Resources/icons/calls/call_discard@2x.png similarity index 100% rename from Telegram/Resources/icons/call_discard@2x.png rename to Telegram/Resources/icons/calls/call_discard@2x.png diff --git a/Telegram/Resources/icons/call_discard@3x.png b/Telegram/Resources/icons/calls/call_discard@3x.png similarity index 100% rename from Telegram/Resources/icons/call_discard@3x.png rename to Telegram/Resources/icons/calls/call_discard@3x.png diff --git a/Telegram/Resources/icons/call_rating.png b/Telegram/Resources/icons/calls/call_rating.png similarity index 100% rename from Telegram/Resources/icons/call_rating.png rename to Telegram/Resources/icons/calls/call_rating.png diff --git a/Telegram/Resources/icons/call_rating@2x.png b/Telegram/Resources/icons/calls/call_rating@2x.png similarity index 100% rename from Telegram/Resources/icons/call_rating@2x.png rename to Telegram/Resources/icons/calls/call_rating@2x.png diff --git a/Telegram/Resources/icons/call_rating@3x.png b/Telegram/Resources/icons/calls/call_rating@3x.png similarity index 100% rename from Telegram/Resources/icons/call_rating@3x.png rename to Telegram/Resources/icons/calls/call_rating@3x.png diff --git a/Telegram/Resources/icons/call_rating_filled.png b/Telegram/Resources/icons/calls/call_rating_filled.png similarity index 100% rename from Telegram/Resources/icons/call_rating_filled.png rename to Telegram/Resources/icons/calls/call_rating_filled.png diff --git a/Telegram/Resources/icons/call_rating_filled@2x.png b/Telegram/Resources/icons/calls/call_rating_filled@2x.png similarity index 100% rename from Telegram/Resources/icons/call_rating_filled@2x.png rename to Telegram/Resources/icons/calls/call_rating_filled@2x.png diff --git a/Telegram/Resources/icons/call_rating_filled@3x.png b/Telegram/Resources/icons/calls/call_rating_filled@3x.png similarity index 100% rename from Telegram/Resources/icons/call_rating_filled@3x.png rename to Telegram/Resources/icons/calls/call_rating_filled@3x.png diff --git a/Telegram/Resources/icons/call_record_active.png b/Telegram/Resources/icons/calls/call_record_active.png similarity index 100% rename from Telegram/Resources/icons/call_record_active.png rename to Telegram/Resources/icons/calls/call_record_active.png diff --git a/Telegram/Resources/icons/call_record_active@2x.png b/Telegram/Resources/icons/calls/call_record_active@2x.png similarity index 100% rename from Telegram/Resources/icons/call_record_active@2x.png rename to Telegram/Resources/icons/calls/call_record_active@2x.png diff --git a/Telegram/Resources/icons/call_record_active@3x.png b/Telegram/Resources/icons/calls/call_record_active@3x.png similarity index 100% rename from Telegram/Resources/icons/call_record_active@3x.png rename to Telegram/Resources/icons/calls/call_record_active@3x.png diff --git a/Telegram/Resources/icons/call_record_muted.png b/Telegram/Resources/icons/calls/call_record_muted.png similarity index 100% rename from Telegram/Resources/icons/call_record_muted.png rename to Telegram/Resources/icons/calls/call_record_muted.png diff --git a/Telegram/Resources/icons/call_record_muted@2x.png b/Telegram/Resources/icons/calls/call_record_muted@2x.png similarity index 100% rename from Telegram/Resources/icons/call_record_muted@2x.png rename to Telegram/Resources/icons/calls/call_record_muted@2x.png diff --git a/Telegram/Resources/icons/call_record_muted@3x.png b/Telegram/Resources/icons/calls/call_record_muted@3x.png similarity index 100% rename from Telegram/Resources/icons/call_record_muted@3x.png rename to Telegram/Resources/icons/calls/call_record_muted@3x.png diff --git a/Telegram/Resources/icons/call_shadow_left.png b/Telegram/Resources/icons/calls/call_shadow_left.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_left.png rename to Telegram/Resources/icons/calls/call_shadow_left.png diff --git a/Telegram/Resources/icons/call_shadow_left@2x.png b/Telegram/Resources/icons/calls/call_shadow_left@2x.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_left@2x.png rename to Telegram/Resources/icons/calls/call_shadow_left@2x.png diff --git a/Telegram/Resources/icons/call_shadow_left@3x.png b/Telegram/Resources/icons/calls/call_shadow_left@3x.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_left@3x.png rename to Telegram/Resources/icons/calls/call_shadow_left@3x.png diff --git a/Telegram/Resources/icons/call_shadow_top.png b/Telegram/Resources/icons/calls/call_shadow_top.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_top.png rename to Telegram/Resources/icons/calls/call_shadow_top.png diff --git a/Telegram/Resources/icons/call_shadow_top@2x.png b/Telegram/Resources/icons/calls/call_shadow_top@2x.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_top@2x.png rename to Telegram/Resources/icons/calls/call_shadow_top@2x.png diff --git a/Telegram/Resources/icons/call_shadow_top@3x.png b/Telegram/Resources/icons/calls/call_shadow_top@3x.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_top@3x.png rename to Telegram/Resources/icons/calls/call_shadow_top@3x.png diff --git a/Telegram/Resources/icons/call_shadow_top_left.png b/Telegram/Resources/icons/calls/call_shadow_top_left.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_top_left.png rename to Telegram/Resources/icons/calls/call_shadow_top_left.png diff --git a/Telegram/Resources/icons/call_shadow_top_left@2x.png b/Telegram/Resources/icons/calls/call_shadow_top_left@2x.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_top_left@2x.png rename to Telegram/Resources/icons/calls/call_shadow_top_left@2x.png diff --git a/Telegram/Resources/icons/call_shadow_top_left@3x.png b/Telegram/Resources/icons/calls/call_shadow_top_left@3x.png similarity index 100% rename from Telegram/Resources/icons/call_shadow_top_left@3x.png rename to Telegram/Resources/icons/calls/call_shadow_top_left@3x.png diff --git a/Telegram/Resources/icons/calls_close_main.png b/Telegram/Resources/icons/calls/calls_close_main.png similarity index 100% rename from Telegram/Resources/icons/calls_close_main.png rename to Telegram/Resources/icons/calls/calls_close_main.png diff --git a/Telegram/Resources/icons/calls_close_main@2x.png b/Telegram/Resources/icons/calls/calls_close_main@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_close_main@2x.png rename to Telegram/Resources/icons/calls/calls_close_main@2x.png diff --git a/Telegram/Resources/icons/calls_close_main@3x.png b/Telegram/Resources/icons/calls/calls_close_main@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_close_main@3x.png rename to Telegram/Resources/icons/calls/calls_close_main@3x.png diff --git a/Telegram/Resources/icons/calls_close_shadow.png b/Telegram/Resources/icons/calls/calls_close_shadow.png similarity index 100% rename from Telegram/Resources/icons/calls_close_shadow.png rename to Telegram/Resources/icons/calls/calls_close_shadow.png diff --git a/Telegram/Resources/icons/calls_close_shadow@2x.png b/Telegram/Resources/icons/calls/calls_close_shadow@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_close_shadow@2x.png rename to Telegram/Resources/icons/calls/calls_close_shadow@2x.png diff --git a/Telegram/Resources/icons/calls_close_shadow@3x.png b/Telegram/Resources/icons/calls/calls_close_shadow@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_close_shadow@3x.png rename to Telegram/Resources/icons/calls/calls_close_shadow@3x.png diff --git a/Telegram/Resources/icons/calls_maximize_main.png b/Telegram/Resources/icons/calls/calls_maximize_main.png similarity index 100% rename from Telegram/Resources/icons/calls_maximize_main.png rename to Telegram/Resources/icons/calls/calls_maximize_main.png diff --git a/Telegram/Resources/icons/calls_maximize_main@2x.png b/Telegram/Resources/icons/calls/calls_maximize_main@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_maximize_main@2x.png rename to Telegram/Resources/icons/calls/calls_maximize_main@2x.png diff --git a/Telegram/Resources/icons/calls_maximize_main@3x.png b/Telegram/Resources/icons/calls/calls_maximize_main@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_maximize_main@3x.png rename to Telegram/Resources/icons/calls/calls_maximize_main@3x.png diff --git a/Telegram/Resources/icons/calls_maximize_shadow.png b/Telegram/Resources/icons/calls/calls_maximize_shadow.png similarity index 100% rename from Telegram/Resources/icons/calls_maximize_shadow.png rename to Telegram/Resources/icons/calls/calls_maximize_shadow.png diff --git a/Telegram/Resources/icons/calls_maximize_shadow@2x.png b/Telegram/Resources/icons/calls/calls_maximize_shadow@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_maximize_shadow@2x.png rename to Telegram/Resources/icons/calls/calls_maximize_shadow@2x.png diff --git a/Telegram/Resources/icons/calls_maximize_shadow@3x.png b/Telegram/Resources/icons/calls/calls_maximize_shadow@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_maximize_shadow@3x.png rename to Telegram/Resources/icons/calls/calls_maximize_shadow@3x.png diff --git a/Telegram/Resources/icons/calls_minimize_main.png b/Telegram/Resources/icons/calls/calls_minimize_main.png similarity index 100% rename from Telegram/Resources/icons/calls_minimize_main.png rename to Telegram/Resources/icons/calls/calls_minimize_main.png diff --git a/Telegram/Resources/icons/calls_minimize_main@2x.png b/Telegram/Resources/icons/calls/calls_minimize_main@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_minimize_main@2x.png rename to Telegram/Resources/icons/calls/calls_minimize_main@2x.png diff --git a/Telegram/Resources/icons/calls_minimize_main@3x.png b/Telegram/Resources/icons/calls/calls_minimize_main@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_minimize_main@3x.png rename to Telegram/Resources/icons/calls/calls_minimize_main@3x.png diff --git a/Telegram/Resources/icons/calls_minimize_shadow.png b/Telegram/Resources/icons/calls/calls_minimize_shadow.png similarity index 100% rename from Telegram/Resources/icons/calls_minimize_shadow.png rename to Telegram/Resources/icons/calls/calls_minimize_shadow.png diff --git a/Telegram/Resources/icons/calls_minimize_shadow@2x.png b/Telegram/Resources/icons/calls/calls_minimize_shadow@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_minimize_shadow@2x.png rename to Telegram/Resources/icons/calls/calls_minimize_shadow@2x.png diff --git a/Telegram/Resources/icons/calls_minimize_shadow@3x.png b/Telegram/Resources/icons/calls/calls_minimize_shadow@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_minimize_shadow@3x.png rename to Telegram/Resources/icons/calls/calls_minimize_shadow@3x.png diff --git a/Telegram/Resources/icons/calls_mute_tooltip.png b/Telegram/Resources/icons/calls/calls_mute_tooltip.png similarity index 100% rename from Telegram/Resources/icons/calls_mute_tooltip.png rename to Telegram/Resources/icons/calls/calls_mute_tooltip.png diff --git a/Telegram/Resources/icons/calls_mute_tooltip@2x.png b/Telegram/Resources/icons/calls/calls_mute_tooltip@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_mute_tooltip@2x.png rename to Telegram/Resources/icons/calls/calls_mute_tooltip@2x.png diff --git a/Telegram/Resources/icons/calls_mute_tooltip@3x.png b/Telegram/Resources/icons/calls/calls_mute_tooltip@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_mute_tooltip@3x.png rename to Telegram/Resources/icons/calls/calls_mute_tooltip@3x.png diff --git a/Telegram/Resources/icons/calls_mute_userpic.png b/Telegram/Resources/icons/calls/calls_mute_userpic.png similarity index 100% rename from Telegram/Resources/icons/calls_mute_userpic.png rename to Telegram/Resources/icons/calls/calls_mute_userpic.png diff --git a/Telegram/Resources/icons/calls_mute_userpic@2x.png b/Telegram/Resources/icons/calls/calls_mute_userpic@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_mute_userpic@2x.png rename to Telegram/Resources/icons/calls/calls_mute_userpic@2x.png diff --git a/Telegram/Resources/icons/calls_mute_userpic@3x.png b/Telegram/Resources/icons/calls/calls_mute_userpic@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_mute_userpic@3x.png rename to Telegram/Resources/icons/calls/calls_mute_userpic@3x.png diff --git a/Telegram/Resources/icons/calls_restore_main.png b/Telegram/Resources/icons/calls/calls_restore_main.png similarity index 100% rename from Telegram/Resources/icons/calls_restore_main.png rename to Telegram/Resources/icons/calls/calls_restore_main.png diff --git a/Telegram/Resources/icons/calls_restore_main@2x.png b/Telegram/Resources/icons/calls/calls_restore_main@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_restore_main@2x.png rename to Telegram/Resources/icons/calls/calls_restore_main@2x.png diff --git a/Telegram/Resources/icons/calls_restore_main@3x.png b/Telegram/Resources/icons/calls/calls_restore_main@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_restore_main@3x.png rename to Telegram/Resources/icons/calls/calls_restore_main@3x.png diff --git a/Telegram/Resources/icons/calls_restore_shadow.png b/Telegram/Resources/icons/calls/calls_restore_shadow.png similarity index 100% rename from Telegram/Resources/icons/calls_restore_shadow.png rename to Telegram/Resources/icons/calls/calls_restore_shadow.png diff --git a/Telegram/Resources/icons/calls_restore_shadow@2x.png b/Telegram/Resources/icons/calls/calls_restore_shadow@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_restore_shadow@2x.png rename to Telegram/Resources/icons/calls/calls_restore_shadow@2x.png diff --git a/Telegram/Resources/icons/calls_restore_shadow@3x.png b/Telegram/Resources/icons/calls/calls_restore_shadow@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_restore_shadow@3x.png rename to Telegram/Resources/icons/calls/calls_restore_shadow@3x.png diff --git a/Telegram/Resources/icons/calls_shadow_controls.png b/Telegram/Resources/icons/calls/calls_shadow_controls.png similarity index 100% rename from Telegram/Resources/icons/calls_shadow_controls.png rename to Telegram/Resources/icons/calls/calls_shadow_controls.png diff --git a/Telegram/Resources/icons/calls_shadow_controls@2x.png b/Telegram/Resources/icons/calls/calls_shadow_controls@2x.png similarity index 100% rename from Telegram/Resources/icons/calls_shadow_controls@2x.png rename to Telegram/Resources/icons/calls/calls_shadow_controls@2x.png diff --git a/Telegram/Resources/icons/calls_shadow_controls@3x.png b/Telegram/Resources/icons/calls/calls_shadow_controls@3x.png similarity index 100% rename from Telegram/Resources/icons/calls_shadow_controls@3x.png rename to Telegram/Resources/icons/calls/calls_shadow_controls@3x.png diff --git a/Telegram/Resources/icons/calls/group_calls_muted.png b/Telegram/Resources/icons/calls/group_calls_muted.png new file mode 100644 index 0000000000000000000000000000000000000000..1c63be9cde9c27d2acd85f2634fdf5f449de9928 GIT binary patch literal 718 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6-QsS%!O zzP=1vKsE;hV|yk83y{SK#8N=az`(SC2`(bBfEmFCNgn(gJ{_pU(bL5-#DjNhnC4fFRAlj4v+BuV|Q>aIyYxJmx>)f%KWpZ(UlX|D)6nYc+t3iS@(6=MUti0jr}ljYU^>&f3pv~Eel>6qz}vm z=H55KMd{;AhVr_dF+X;GsM;%|Ft6)lh0h|juT^s$`;RNn^;7Q?H1oamO87v;>Z=wz z?!P}B{<-RnhFEu?hRCGNBCYMlR=&(S->R6FU-s-i+T^+HvS#by#D_ZP7pa|kuOifW zqKtLsJO;P@_x<F|}B2vLesYbD!?*S$1Bj?rDtP_4Mc4 x+ux~Q)-jsuHFG9!?*9@d=NA4`>;JF*!Mj)Cxv&0u0ZUM#@^tlcS?83{1OUj@Hsb&Q literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/group_calls_muted@2x.png b/Telegram/Resources/icons/calls/group_calls_muted@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..231ce293f003a034690347989e5fc84064bfdcf0 GIT binary patch literal 1288 zcmV+j1^4=iP)v?>qC(%$Ygo%yqr4UQ^SV zndh13c|K=;oSAzQp63zE-!>(`ZRCFf{{i^w5&S7^g{*mCV1Rg9Sy`d%>};cMPEHO@Pfyd^+Z$I8JRyS)bi;f) z%Iv|z(DL%K63FW6Y5)QOZEbBSdSMmlrV%0>prfOM-rwJqcytK7RXtr@UD4Xwnx9eD z6Z$y5X`6mH0G#yp_68vMbYk;bnKsYQ&onwZN?BQ1!Xe6NgN!FkC-hANL^MDc#MIOj zkI8?IjEq==0)(8{!nW!g)aWy7Px@VPV1R>+ADa5X_dE*xOYb47H1k3(=aNpD)TEDWvokObh$$>@2Xw zvcMGkv$L~&0Q{#ePob_la4}$l;JcH}PDTMB$eEcL>lSN*$T$F3h>QfVLFiv>=w&Pb zf*T(n4@@T=BBKESVOrQSM8*MBRaGh5WE8}g0p#Z95;6jm^ZEHXxj@X%&xd{oY<^^) zT3HQ96Z*k+c6QR^{Ba818i?^3#Yrgy9xjfWJrLh zsHjk6aAaB&+?altgKeJRQw!8sk98J$cz9s8ey7UHN?!Dz8yg$`v$V9-+uPgoii(O9 zdDz)$b8}PCm2JyD6(vK(NXGO;``p|de=+p>`l?(9^$QKoLkvRn<#uov&~J9QTJk$K zu9&dR6MXW%5zoJY+7yR}hq+z<+1A#k7}u|qw&`R#cJM70Hsv%lG^F^j#m3sB0jjI3 zW!tp4xTqL6UFvNM+Xk_;w50fe&p5Hg0)U~z!$aW!_t}Pq2E_p&FcR|5DmDny!j`8O ze8mhNdeI{Qo$l^#0f556!2#vvINp@TyaoC)Ik`r6fIyb(Zvb#--ge}At)0IIyaTm7@nBv8Se$F_*yQ(IbE zsJ6D&&nTk}GOP*H*$LaGypUm!Z?O@h;0^$T_4W01dU^^!&dFtWiUE-t03a}BCMG6G z{&6cV&GGRu6&Du=rju!5NBn}9IshM9932J+2We+#hhAP@tPGGr2fC)?2Dhg}V9Vw* z!}H?+veANq0%~k*3_zd_GFdmQ+|$$JuQPlY`}_N?{qUCyglSKVK(QI;-yNj`tCW~Tu+A^$BV}20b2H(Fg^cn?Y=!mx yv?-Lv7aVHp)22|U*E9-$0Mhs}Q!@4KN8lH~zwXJ4BjYdt0000Z@!1- z{LVe+nKN_V?!7;~58OL*p7YGiGoLeO&b(zAhQatRBN77tNc0)?Uji9Gil&eOlmVn5 zptzlBK^Z^_0*c!?iu_B($Z3+xVYFXZ-0N^`2PL7@%8IhgR#_)Lg0a~ zUs+ihd-CK78yp;DmoH!Tl~$BhTU*OUMn>4q&JJfl3+}-Nwjx~$LjQ2Kx3{et{rU5U zb#!!SS0FDhj|~kCv4evHD-Tr!wn&4tX%7f3$0=RLTbpGcGc12CNH_xrbs**B zDC~pPzTxV2TpAL;?s- zdO>nlf=?lwD&_UJZ{J)t7cN|I>7rId1_+MnLgM|2?|M*ava_>&=i@g)q<~Pk4y4A$ zM!$Zh)IOGu&T9zZHR9$ZNPl{IdYpZCf?)6kJ!w{;NYs)m@}|nv^^HnP===l1 zwhSfqts7AgmPLhxg(UJ3A+_Y{$2Z}lR~TqLsHmuj=joRE`g)ggadFYo@ogI8)vH%V zOG}HTb4GO5lSCSO-a<$DfO|5ZMLMsLY^FScRDbT=Iq?t^!p_c4R~l;D2Q8_&x!GlF zFVpMmCJLptec%Hn@Y$ZfZah#6`u+R2!FN4*8HrL=Rh6jVS06rnFg7cC{7f5RCKZ&s*0bLiqq+;d4+4M0RCm#r2?GzkW^Th1Ng` zTGE|6cgQrnGNxX&o`k%GEkgL<6!>bl@fnxDN?sYgc=5vN2g6yRl9CdaGkoiZ$qp~* zblkd-P&RyU3Vijdi{f4|KzHxnb^2jmQ~eMe@H+ky6(m{z178&oyzU1Bl%Jo^_V)H% zkZ^uScBsJX0F@4;{{DW?Re&#FzPS8>kMK3=fKcGemoHroLDAROCv^y;T-gyk9Y{J~ zH@j2tF$5f>4c3B??)vrXv=+)u9fY(LI2>v16Ad9?uP?}Cxqj2GC@m1>xkeW=PQhLglBYr)7*x{pZi0SyxwAETCe96b&G>0!#=$e*EYO2{&<4 ze{^(|@mW1N@)j)$BV*XWc57=(xiYC5Cpo7jq8vpS9Uv5xo14p?K7Fc$Bn;4*?(XiI zhs<%z4nG_?htAmnhaCbSeE>+&0uoOHj!+|Q0VZge0!Z-y0w@dypFe-jWIL&0AmP;$ z4}%I-huwXgvW*XraJ;Ur&YYG|>&kv1ArDL+E?v6h2?%9-LE`I8%JhHzj~|c#Rb5?e ze(v$L`}OOWHXGO?4TcKHL*%Cu${@XY^F}wHl*2s7;4w{NrV?rte4+=ETX**Mvf zRC{$cUsD6f=71EC-6m`tea__MB;!lH%He{&ES&O2Q%k9ELS*^WA#~C(MrEKy`xyO6 zyR6d!5FgvPz)(7uQ4$ns0qE1GPp+y`I+szB6uO_g*zB+zg1MFu#N%8d;as)rk`|pb z8Umlg8CR}cG1~;6iy7(iilq%8lS~@^r9~f!lvI6-QsS%!O zzP=1vKsE;hV|yk83y{SK#8N=az`(SC2`(bBfEmFCNgn(gJ{_p|l&6bhhzIZ8(ADb> zISAan7n9+kptwZgA$Nd>O`p6&Qoz0wIYg70*p-!`~vOfiwAkUQsK3{;**w>AY^d$e`0gdPMx;H3lDHjzN{2; zJ=BlM@=8LcpVXWgEph9Q8^8N)_dNHQNQA_WAe)D$@;BUn|C4*m?cBI#1%aJ0erM8* z72{3Hj+|J?*1ci(-9G!}@|}IHPL1cD8_V*sf3oS%UcZhjp@Ee>Msr@7s^jC2Gq&5z z^*dQ8qjutSU({ML>m-haY&<+Ny%t4T%}EmdKI;Vst0O{fFQvd(} literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/group_calls_unmuted@2x.png b/Telegram/Resources/icons/calls/group_calls_unmuted@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a3f4b226aadd108746a1069d9f3b8f377f448863 GIT binary patch literal 1073 zcmV-11kU@3P)kSP6n)VI^3J#6nPvfoQ_K{qAL#*^8Im%k3fZ zGO+CI&dfLS?e1M}lMzCYY`<3i{MyPJg?9-2^bU4*AiM)pQ&XZ?EQ-6kJMsSh9yI8f zgDnlgTPShs>FJ@}-Ce5J>l7uh!3VxRayBsh6n}Ph7FBoCJbXQpNKOE&8kSP2L`O$Q zL4zI^#CY0;WCY~7Hjd5b=O=A#ZBbWOmo5-G=3rBTD1Y00Vgj}VY1=yl(q3U3erE@4E0ngqvjb_bunqtJ*@0{} zE6~!GxOtp*U}a@Ryu7>w4LX-{xvYmHBO_XmQn(L~g@3C&QFxfQeo&pCpX(2{%gal{ z-{;31u$3UEOKna&P^nb(0*sH3YaPnz>8TzM4-bpY%}qTHEBSn0z()y>#T>`xIhB7@ zZ)Li-x2G0{YPFhi9sKS>gF6BLtcH%cRu>vvF@dcFIc32v&F&;uRpAEx($bOv?Bhxa zOfDN68%B;6Wb6bcCnq^J9UdMU;8?w+mwI4wLCzLMbDNz2fNpPZH9qdMv$L}tBsf;s z3D}|pHu!}9kpLEtLoUbS;pawRb#>LK>*nT$`uh5s65*M^T^DaB`Nvk!pkwa4OFS@u z#Ra)s>vATL$zWb%~+36RZ*SA!;Gi`*eBg^1E_j6kTjg=>GYv=C zxl1mW6Z`x7qPMr#2*Rge_AlM``&e=nY&p94F7ye1|l#Ny(jSXfvP;mdrrS`}++ zYvSPGz`%1I#AYplX=Y}I9v&X}BDSRi16aUpv$Y)<9GRV+9eR6vi(Nco5bJX6EX3L; zpn!*lhG>0#oh~je=F{fiGfIe8N6C0banszyQt7%{7t09Bkazhu+phEE8Kr zGBAAPyWJT38aRDU`%0DuPHF@mA0KtST-TEx74PR#)<*O5^Mo4~8g$I1(NcQ`JT7oc rsML;7%d8%a9ReN~vn{A!zXN{(+!pb09+=0O00000NkvXXu0mjf^A*}e literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/group_calls_unmuted@3x.png b/Telegram/Resources/icons/calls/group_calls_unmuted@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e351c3996082646a7ae8fccd2f66d279c32498e9 GIT binary patch literal 1563 zcmV+$2ITpPP);5KS0V#%p1!Ms!2q=o}2(o|_1Qf;D zigNA6iA2JwtgLiON=lrfq9V7wr>7_9?(WXHy1H^0OaHGBC@{X&*4DC_nHhF-bHf?X zhBCw;HXRzj+cXO&H#e6}PENA7w>O#!;YBR!pl%vKnFCZ_Ue0!RcL@Z^xlk853_~&l zh~Hn>$;nA_BtoK&kB=F08H8j8(AwHsNVoqh;x}i8O=ZaSR{`qn?UnZV{{Ef~4Gpo1 ziVDBeIED{pGVAH-@#8ha(mR5(v$NUx`8nCo_4RdDRaGVJD9R8+8FC`GW;n>N7f@$s zC)Gc8dV0$8^YfJ*L=0jnLvG#rwF8=-o)-G{`T5COT3V9s9AZ%?XvqD)2XuIN80;6@ z*x2}W$51C|$gLZY_Q@)y5kcGD-VVBy=Q}$)LJXB#2#V;?4ydfGOpwT@5Q0zTxW2v? zLa5wAP(+7zKv`K?f<%5l5qv5KRfo#0%JbWD?SOtODAVFCAjK82fGi*d0Y$MLK^BmL zfTGxrAPY!AKv8T*kOib5peVK@$O2LjP!!t{qze%Li06!sjyg+AOYR=$l*Kg*r)KU{ zZfHnW8>N@>rSw*Ad3pJd7koJ9$%T!7U;iTxC*$MeLL621r*C3nLdb*MKV!V(WVLzm zioMt=MFsa0ji8o0etx>~a>Vt@b#rr5Xp(y63JsJ|+B}V6`;_iMg@uJYr)c%{^@4M6 zZ%^>Gx3@d|MZDmbIS|{{)+Q8FxhV~(DAOz?B^=~1<#AGfK|uj^kyu|}pHRp2XCG*w z44T9G6jCM!5mJfq^*e%&v9U2hgy|25&=53e4eN`dEHyw!M@K;&Y-MGI6oSvhGK>4u zGc*JZS``q&%2NZhu&@x+!CqfqS#fc(&=_Au#!M_;K-|hsKY&{?n4k~ zV)={g{sXPX0p;c8vB$?p0TR~l$QBjoTtJ03(RR?F5n7D{LW7Hoi$Oi^`S9?tR1lWY z=~#j{GBQGHfyM}MP&c)E5b8BIH?yy=FG1MD!vm|SsRU4h3R+8E-x?H?CdP-?(Qakd3)mU%NQ|;MIDu)?Ff@n*{F*T z5IUBVlf$N_rc{0L%ke0_y}fl8nPZt9HY_-YkNOGWR}37WO|cHG z1IQ(lf&XO0JtzaY_>wFj9|e720nuE1uLb0zpbsn{nv3tXfP576!M~1wtBytuF@^vD N002ovPDHLkV1lhG$2disabled() ? 0. : row->checkedRatio(); - p.setPen(anim::pen(st::contactsNameFg, st::contactsNameCheckedFg, nameCheckedRatio)); + p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameCheckedRatio)); name.drawLeftElided(p, namex, _st.item.namePosition.y(), namew, width()); if (!actionSize.isEmpty()) { diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index 65d8994fa..0c3264b35 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -135,7 +135,6 @@ private: // Viewing admins, banned or restricted users list with search. class ParticipantsBoxController : public PeerListController - , private base::Subscriber , public base::has_weak_ptr { public: using Role = ParticipantsRole; diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 91441586b..ad0a7aeea 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -22,14 +22,14 @@ CallSignalBars { callRadius: 6px; callShadow: Shadow { - left: icon {{ "call_shadow_left", windowShadowFg }}; - topLeft: icon {{ "call_shadow_top_left", windowShadowFg }}; - top: icon {{ "call_shadow_top", windowShadowFg }}; - topRight: icon {{ "call_shadow_top_left-flip_horizontal", windowShadowFg }}; - right: icon {{ "call_shadow_left-flip_horizontal", windowShadowFg }}; - bottomRight: icon {{ "call_shadow_top_left-flip_vertical-flip_horizontal", windowShadowFg }}; - bottom: icon {{ "call_shadow_top-flip_vertical", windowShadowFg }}; - bottomLeft: icon {{ "call_shadow_top_left-flip_vertical", windowShadowFg }}; + left: icon {{ "calls/call_shadow_left", windowShadowFg }}; + topLeft: icon {{ "calls/call_shadow_top_left", windowShadowFg }}; + top: icon {{ "calls/call_shadow_top", windowShadowFg }}; + topRight: icon {{ "calls/call_shadow_top_left-flip_horizontal", windowShadowFg }}; + right: icon {{ "calls/call_shadow_left-flip_horizontal", windowShadowFg }}; + bottomRight: icon {{ "calls/call_shadow_top_left-flip_vertical-flip_horizontal", windowShadowFg }}; + bottom: icon {{ "calls/call_shadow_top-flip_vertical", windowShadowFg }}; + bottomLeft: icon {{ "calls/call_shadow_top_left-flip_vertical", windowShadowFg }}; extend: margins(9px, 8px, 9px, 10px); fallback: windowShadowFgFallback; } @@ -72,7 +72,7 @@ callBodyWithPreview: CallBodyLayout { muteSize: 0px; mutePosition: point(90px, 84px); } -callMutedPeerIcon: icon {{ "calls_mute_userpic", callIconFg }}; +callMutedPeerIcon: icon {{ "calls/calls_mute_userpic", callIconFg }}; callOutgoingPreviewMin: size(360px, 120px); callOutgoingPreview: size(540px, 180px); // default, for height == callHeight. @@ -89,7 +89,7 @@ callSignalBarsPadding: margins(8px, 9px, 11px, 5px); callFingerprintTop: 8px; callFingerprintBottom: -16px; -callTooltipMutedIcon: icon{{ "calls_mute_tooltip", videoPlayIconFg }}; +callTooltipMutedIcon: icon{{ "calls/calls_mute_tooltip", videoPlayIconFg }}; callTooltipMutedIconPosition: point(10px, 5px); callTooltipPadding: margins(41px, 7px, 15px, 8px); @@ -114,7 +114,7 @@ callButtonLabel: FlatLabel(defaultFlatLabel) { callAnswer: CallButton { button: IconButton(callButton) { - icon: icon {{ "call_answer", callIconFg }}; + icon: icon {{ "calls/call_answer", callIconFg }}; ripple: RippleAnimation(defaultRippleAnimation) { color: callAnswerRipple; } @@ -127,7 +127,7 @@ callAnswer: CallButton { } callHangup: CallButton { button: IconButton(callButton) { - icon: icon {{ "call_discard", callIconFg }}; + icon: icon {{ "calls/call_discard", callIconFg }}; ripple: RippleAnimation(defaultRippleAnimation) { color: callHangupRipple; } @@ -138,7 +138,7 @@ callHangup: CallButton { } callCancel: CallButton { button: IconButton(callButton) { - icon: icon {{ "call_cancel", callIconFgActive }}; + icon: icon {{ "calls/call_cancel", callIconFgActive }}; ripple: RippleAnimation(defaultRippleAnimation) { color: callIconActiveRipple; } @@ -149,7 +149,7 @@ callCancel: CallButton { } callMicrophoneMute: CallButton { button: IconButton(callButton) { - icon: icon {{ "call_record_active", callIconFg }}; + icon: icon {{ "calls/call_record_active", callIconFg }}; ripple: RippleAnimation(defaultRippleAnimation) { color: callMuteRipple; } @@ -160,7 +160,7 @@ callMicrophoneMute: CallButton { } callMicrophoneUnmute: CallButton(callMicrophoneMute) { button: IconButton(callButton) { - icon: icon {{ "call_record_muted", callIconFgActive }}; + icon: icon {{ "calls/call_record_muted", callIconFgActive }}; ripple: RippleAnimation(defaultRippleAnimation) { color: callIconActiveRipple; } @@ -169,7 +169,7 @@ callMicrophoneUnmute: CallButton(callMicrophoneMute) { } callCameraMute: CallButton(callMicrophoneMute) { button: IconButton(callButton) { - icon: icon {{ "call_camera_active", callIconFg }}; + icon: icon {{ "calls/call_camera_active", callIconFg }}; ripple: RippleAnimation(defaultRippleAnimation) { color: callMuteRipple; } @@ -177,7 +177,7 @@ callCameraMute: CallButton(callMicrophoneMute) { } callCameraUnmute: CallButton(callMicrophoneUnmute) { button: IconButton(callButton) { - icon: icon {{ "call_camera_muted", callIconFgActive }}; + icon: icon {{ "calls/call_camera_muted", callIconFgActive }}; ripple: RippleAnimation(defaultRippleAnimation) { color: callIconActiveRipple; } @@ -223,7 +223,7 @@ callBarMuteToggle: IconButton { width: 41px; height: 38px; - icon: icon {{ "call_record_active", callBarFg }}; + icon: icon {{ "calls/call_record_active", callBarFg }}; iconPosition: point(3px, 2px); ripple: RippleAnimation(defaultRippleAnimation) { @@ -232,11 +232,11 @@ callBarMuteToggle: IconButton { rippleAreaPosition: point(5px, 3px); rippleAreaSize: 32px; } -callBarUnmuteIcon: icon {{ "call_record_muted", callBarFg }}; +callBarUnmuteIcon: icon {{ "calls/call_record_muted", callBarFg }}; callBarRightSkip: 12px; callBarSkip: 10px; callBarHangup: IconButton(callBarMuteToggle) { - icon: icon {{ "call_discard", callBarFg }}; + icon: icon {{ "calls/call_discard", callBarFg }}; iconPosition: point(3px, 1px); } callBarLabel: LabelSimple(defaultLabelSimple) { @@ -254,16 +254,16 @@ callBarInfoLabel: FlatLabel(defaultFlatLabel) { callBarLabelTop: 10px; callArrowPosition: point(-2px, 1px); -callArrowIn: icon {{ "call_arrow_in", callArrowFg }}; -callArrowOut: icon {{ "call_arrow_out", callArrowFg }}; -callArrowMissed: icon {{ "call_arrow_in", callArrowMissedFg }}; +callArrowIn: icon {{ "calls/call_arrow_in", callArrowFg }}; +callArrowOut: icon {{ "calls/call_arrow_out", callArrowFg }}; +callArrowMissed: icon {{ "calls/call_arrow_in", callArrowMissedFg }}; callArrowSkip: 4px; callReDial: IconButton { width: 40px; height: 56px; - icon: icon {{ "call_answer", menuIconFg }}; - iconOver: icon {{ "call_answer", menuIconFgOver }}; + icon: icon {{ "calls/call_answer", menuIconFg }}; + iconOver: icon {{ "calls/call_answer", menuIconFgOver }}; iconPosition: point(-1px, -1px); ripple: defaultRippleAnimation; @@ -272,8 +272,8 @@ callReDial: IconButton { } callCameraReDial: IconButton(callReDial) { - icon: icon {{ "call_camera_active", menuIconFg }}; - iconOver: icon {{ "call_camera_active", menuIconFgOver }}; + icon: icon {{ "calls/call_camera_active", menuIconFg }}; + iconOver: icon {{ "calls/call_camera_active", menuIconFgOver }}; } callRatingPadding: margins(24px, 12px, 24px, 0px); @@ -281,7 +281,7 @@ callRatingStar: IconButton { width: 36px; height: 36px; - icon: icon {{ "call_rating", windowSubTextFg }}; + icon: icon {{ "calls/call_rating", windowSubTextFg }}; iconPosition: point(-1px, -1px); ripple: RippleAnimation(defaultRippleAnimation) { @@ -290,7 +290,7 @@ callRatingStar: IconButton { rippleAreaPosition: point(0px, 0px); rippleAreaSize: 36px; } -callRatingStarFilled: icon {{ "call_rating_filled", lightButtonFg }}; +callRatingStarFilled: icon {{ "calls/call_rating_filled", lightButtonFg }}; callRatingStarTop: 4px; callRatingComment: InputField(defaultInputField) { textMargins: margins(1px, 26px, 1px, 4px); @@ -327,43 +327,43 @@ callTitleButton: IconButton { iconPosition: point(0px, 0px); } callTitleMinimizeIcon: icon { - { "calls_minimize_shadow", windowShadowFg }, - { "calls_minimize_main", callNameFg }, + { "calls/calls_minimize_shadow", windowShadowFg }, + { "calls/calls_minimize_main", callNameFg }, }; callTitleMinimizeIconOver: icon { { size(34px, 30px), callBgButton }, { size(34px, 30px), callMuteRipple }, - { "calls_minimize_shadow", windowShadowFg }, - { "calls_minimize_main", callNameFg }, + { "calls/calls_minimize_shadow", windowShadowFg }, + { "calls/calls_minimize_main", callNameFg }, }; callTitleMaximizeIcon: icon { - { "calls_maximize_shadow", windowShadowFg }, - { "calls_maximize_main", callNameFg }, + { "calls/calls_maximize_shadow", windowShadowFg }, + { "calls/calls_maximize_main", callNameFg }, }; callTitleMaximizeIconOver: icon { { size(34px, 30px), callBgButton }, { size(34px, 30px), callMuteRipple }, - { "calls_maximize_shadow", windowShadowFg }, - { "calls_maximize_main", callNameFg }, + { "calls/calls_maximize_shadow", windowShadowFg }, + { "calls/calls_maximize_main", callNameFg }, }; callTitleRestoreIcon: icon { - { "calls_restore_shadow", windowShadowFg }, - { "calls_restore_main", callNameFg }, + { "calls/calls_restore_shadow", windowShadowFg }, + { "calls/calls_restore_main", callNameFg }, }; callTitleRestoreIconOver: icon { { size(34px, 30px), callBgButton }, { size(34px, 30px), callMuteRipple }, - { "calls_restore_shadow", windowShadowFg }, - { "calls_restore_main", callNameFg }, + { "calls/calls_restore_shadow", windowShadowFg }, + { "calls/calls_restore_main", callNameFg }, }; callTitleCloseIcon: icon { - { "calls_close_shadow", windowShadowFg }, - { "calls_close_main", callNameFg }, + { "calls/calls_close_shadow", windowShadowFg }, + { "calls/calls_close_main", callNameFg }, }; callTitleCloseIconOver: icon { { size(34px, 30px), titleButtonCloseBgOver }, - { "calls_close_shadow", windowShadowFg }, - { "calls_close_main", titleButtonCloseFgOver }, + { "calls/calls_close_shadow", windowShadowFg }, + { "calls/calls_close_main", titleButtonCloseFgOver }, }; callTitle: WindowTitle(defaultWindowTitle) { height: 0px; @@ -394,7 +394,7 @@ callTitle: WindowTitle(defaultWindowTitle) { closeIconActive: callTitleCloseIcon; closeIconActiveOver: callTitleCloseIconOver; } -callTitleShadow: icon {{ "calls_shadow_controls", windowShadowFg }}; +callTitleShadow: icon {{ "calls/calls_shadow_controls", windowShadowFg }}; callErrorToast: Toast(defaultToast) { minWidth: 240px; @@ -402,3 +402,78 @@ callErrorToast: Toast(defaultToast) { groupCallWidth: 380px; groupCallHeight: 580px; + +groupCallRipple: RippleAnimation(defaultRippleAnimation) { + color: groupCallMembersRipple; +} +groupCallMembersList: PeerList(defaultPeerList) { + item: PeerListItem(defaultPeerListItem) { + button: OutlineButton(defaultPeerListButton) { + textBg: groupCallMembersBg; + textBgOver: groupCallMembersBg; + + textFg: groupCallMemberInactiveStatus; + textFgOver: groupCallMemberInactiveStatus; + + font: normalFont; + padding: margins(11px, 5px, 11px, 5px); + + ripple: groupCallRipple; + } + height: 52px; + photoPosition: point(12px, 6px); + namePosition: point(68px, 7px); + statusPosition: point(68px, 27px); + photoSize: 40px; + nameFg: groupCallMembersFg; + nameFgChecked: groupCallMembersFg; + statusFg: groupCallMemberInactiveStatus; + statusFgOver: groupCallMemberInactiveStatus; + statusFgActive: groupCallMemberActiveStatus; + } +} +groupCallMembersHeader: 47px; +groupCallMembersMargin: margins(16px, 16px, 16px, 28px); +groupCallAddMember: IconButton(defaultIconButton) { + width: 36px; + height: 36px; + iconPosition: point(3px, 5px); + icon: icon {{ "info_add_member", groupCallMemberInactiveIcon }}; + iconOver: icon {{ "info_add_member", groupCallMemberInactiveIcon }}; + rippleAreaPosition: point(0px, 0px); + rippleAreaSize: 36px; + ripple: groupCallRipple; +} +groupCallHeaderPosition: point(16px, 16px); +groupCallHeaderLabel: FlatLabel(defaultFlatLabel) { + maxHeight: 18px; + textFg: groupCallMembersFg; + style: TextStyle(defaultTextStyle) { + font: semiboldFont; + linkFont: semiboldFont; + linkFontOver: semiboldFont; + } +} +groupCallAddButtonPosition: point(10px, 7px); + +groupCallInactiveButton: IconButton { + width: 36px; + height: 52px; + + icon: icon {{ "calls/group_calls_muted", groupCallMemberInactiveIcon }}; + iconOver: icon {{ "calls/group_calls_muted", groupCallMemberInactiveIcon }}; + iconPosition: point(-1px, -1px); + + ripple: groupCallRipple; + rippleAreaPosition: point(0px, 8px); + rippleAreaSize: 36px; +} +groupCallActiveButton: IconButton(groupCallInactiveButton) { + icon: icon {{ "calls/group_calls_unmuted", groupCallMemberActiveIcon }}; + iconOver: icon {{ "calls/group_calls_unmuted", groupCallMemberActiveIcon }}; +} +groupCallMutedButton: IconButton(groupCallInactiveButton) { + icon: icon {{ "calls/group_calls_muted", groupCallMemberMutedIcon }}; + iconOver: icon {{ "calls/group_calls_muted", groupCallMemberMutedIcon }}; +} +groupCallMemberButtonSkip: 10px; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp new file mode 100644 index 000000000..d3826d5c5 --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -0,0 +1,528 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "calls/calls_group_members.h" + +#include "calls/calls_group_call.h" +#include "data/data_channel.h" +#include "data/data_user.h" +#include "data/data_changes.h" +#include "data/data_group_call.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" +#include "ui/widgets/popup_menu.h" +#include "ui/text/text_utilities.h" +#include "ui/effects/ripple_animation.h" +#include "main/main_session.h" +#include "lang/lang_keys.h" +#include "styles/style_calls.h" + +namespace Calls { +namespace { + +class MembersController final + : public PeerListController + , public base::has_weak_ptr { +public: + explicit MembersController(not_null call); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + void rowActionClicked(not_null row) override; + base::unique_qptr rowContextMenu( + QWidget *parent, + not_null row) override; + void loadMoreRows() override; + +private: + [[nodiscard]] std::unique_ptr createRow( + not_null user) const; + + void prepareRows(); + + void setupListChangeViewers(); + bool appendRow(not_null user); + bool prependRow(not_null user); + bool removeRow(not_null user); + + const base::weak_ptr _call; + const not_null _channel; + + Ui::BoxPointer _addBox; + rpl::lifetime _lifetime; + +}; + +class Row final : public PeerListRow { +public: + Row(not_null channel, not_null user); + + enum class State { + Active, + Inactive, + Muted, + }; + + void addActionRipple(QPoint point, Fn updateCallback) override; + void stopLastActionRipple() override; + + int nameIconWidth() const override { + return 0; + } + QSize actionSize() const override { + return QSize(_st->width, _st->height); + } + QMargins actionMargins() const override { + return QMargins( + 0, + 0, + st::groupCallMemberButtonSkip, + 0); + } + void paintAction( + Painter &p, + int x, + int y, + int outerWidth, + bool selected, + bool actionSelected) override; + +private: + void refreshStatus() override; + + [[nodiscard]] static State ComputeState( + not_null channel, + not_null user); + [[nodiscard]] static not_null ComputeIconStyle( + State state); + + State _state = State::Inactive; + not_null _st; + + std::unique_ptr _actionRipple; + +}; + +Row::Row(not_null channel, not_null user) +: PeerListRow(user) +, _state(ComputeState(channel, user)) +, _st(ComputeIconStyle(_state)) { + refreshStatus(); +} + +void Row::paintAction( + Painter &p, + int x, + int y, + int outerWidth, + bool selected, + bool actionSelected) { + auto size = actionSize(); + if (_actionRipple) { + _actionRipple->paint( + p, + x + _st->rippleAreaPosition.x(), + y + _st->rippleAreaPosition.y(), + outerWidth); + if (_actionRipple->empty()) { + _actionRipple.reset(); + } + } + _st->icon.paintInCenter( + p, + style::rtlrect(x, y, size.width(), size.height(), outerWidth)); +} + +void Row::refreshStatus() { + setCustomStatus([&] { + switch (_state) { + case State::Inactive: + case State::Muted: return "listening"; + case State::Active: return "speaking"; + } + return ""; + }()); +} + +Row::State Row::ComputeState( + not_null channel, + not_null user) { + const auto call = channel->call(); + if (!call) { + return State::Inactive; + } + const auto &participants = call->participants(); + const auto i = ranges::find( + participants, + user, + &Data::GroupCall::Participant::user); + if (i == end(participants)) { + return State::Inactive; + } + return !i->muted + ? State::Active + : i->canSelfUnmute + ? State::Inactive + : State::Muted; +} + +not_null Row::ComputeIconStyle( + State state) { + switch (state) { + case State::Inactive: return &st::groupCallInactiveButton; + case State::Active: return &st::groupCallActiveButton; + case State::Muted: return &st::groupCallMutedButton; + } + Unexpected("State in Row::ComputeIconStyle."); +} + +void Row::addActionRipple(QPoint point, Fn updateCallback) { + if (!_actionRipple) { + auto mask = Ui::RippleAnimation::ellipseMask( + QSize(_st->rippleAreaSize, _st->rippleAreaSize)); + _actionRipple = std::make_unique( + _st->ripple, + std::move(mask), + std::move(updateCallback)); + } + _actionRipple->add(point - _st->rippleAreaPosition); +} + +void Row::stopLastActionRipple() { + if (_actionRipple) { + _actionRipple->lastStop(); + } +} + +MembersController::MembersController(not_null call) +: _call(call) +, _channel(call->channel()) { + setupListChangeViewers(); +} + +void MembersController::setupListChangeViewers() { + const auto call = _call.get(); + const auto channel = call->channel(); + channel->session().changes().peerUpdates( + channel, + Data::PeerUpdate::Flag::GroupCall + ) | rpl::start_with_next([=] { + prepareRows(); + }, _lifetime); +} + +Main::Session &MembersController::session() const { + return _call->channel()->session(); +} + +void MembersController::prepare() { + delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled); + //delegate()->peerListSetTitle(std::move(title)); + setDescriptionText(tr::lng_contacts_loading(tr::now)); + setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); + + prepareRows(); + delegate()->peerListRefreshRows(); + + loadMoreRows(); +} + +void MembersController::prepareRows() { + const auto real = _channel->call(); + if (!real) { + return; + } + auto foundSelf = false; + auto changed = false; + const auto &participants = real->participants(); + auto count = delegate()->peerListFullRowsCount(); + for (auto i = 0; i != count;) { + auto row = delegate()->peerListRowAt(i); + auto user = row->peer()->asUser(); + if (user->isSelf()) { + foundSelf = true; + } + const auto contains = ranges::contains( + participants, + not_null{ user }, + &Data::GroupCall::Participant::user); + if (contains) { + ++i; + } else { + changed = true; + delegate()->peerListRemoveRow(row); + --count; + } + } + if (!foundSelf) { + if (auto row = createRow(_channel->session().user())) { + changed = true; + delegate()->peerListAppendRow(std::move(row)); + } + } + for (const auto &participant : participants) { + if (auto row = createRow(participant.user)) { + changed = true; + delegate()->peerListAppendRow(std::move(row)); + } + } + if (changed) { + delegate()->peerListRefreshRows(); + } +} + +void MembersController::loadMoreRows() { + if (const auto call = _call.get()) { + if (const auto real = call->channel()->call()) { + real->requestParticipants(); + } + } +} + +void MembersController::rowClicked(not_null row) { + Expects(row->peer()->isUser()); + + const auto user = row->peer()->asUser(); +} + +void MembersController::rowActionClicked( + not_null row) { + Expects(row->peer()->isUser()); + + const auto user = row->peer()->asUser(); +} + +base::unique_qptr MembersController::rowContextMenu( + QWidget *parent, + not_null row) { + Expects(row->peer()->isUser()); + + const auto user = row->peer()->asUser(); + auto result = base::make_unique_q(parent); + //result->addAction( // #TODO calls + // tr::lng_context_view_profile(tr::now), + // crl::guard(this, [=] { _navigation->showPeerInfo(user); })); + return result; +} + +bool MembersController::appendRow(not_null user) { + if (delegate()->peerListFindRow(user->id)) { + return false; + } + delegate()->peerListAppendRow(createRow(user)); + return true; +} + +bool MembersController::prependRow(not_null user) { + if (auto row = delegate()->peerListFindRow(user->id)) { + return false; + } + delegate()->peerListPrependRow(createRow(user)); + return true; +} + +bool MembersController::removeRow(not_null user) { + if (auto row = delegate()->peerListFindRow(user->id)) { + delegate()->peerListRemoveRow(row); + return true; + } + return false; +} + +std::unique_ptr MembersController::createRow( + not_null user) const { + return std::make_unique(_channel, user); +} + +} // namespace + +GroupMembers::GroupMembers( + QWidget *parent, + not_null call) +: RpWidget(parent) +, _call(call) +, _scroll(this, st::defaultSolidScroll) +, _listController(std::make_unique(call)) { + setupHeader(call); + setupList(); + setContent(_list); + _listController->setDelegate(static_cast(this)); + + paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + QPainter(this).fillRect(clip, st::groupCallMembersBg); + }, lifetime()); +} + +int GroupMembers::desiredHeight() const { + auto desired = _header ? _header->height() : 0; + auto count = [this] { + if (const auto call = _call.get()) { + if (const auto real = call->channel()->call()) { + return real->fullCount(); + } + } + return 0; + }(); + desired += qMax(count, _list->fullRowsCount()) + * st::groupCallMembersList.item.height; + return qMax(height(), desired); +} + +void GroupMembers::setupHeader(not_null call) { + _header = object_ptr( + this, + st::groupCallMembersHeader); + auto parent = _header.data(); + + _titleWrap = Ui::CreateChild(parent); + _title = setupTitle(call); + _addMember = Ui::CreateChild( + parent, + st::groupCallAddMember); + setupButtons(); + + widthValue( + ) | rpl::start_with_next([this](int width) { + _header->resizeToWidth(width); + }, _header->lifetime()); +} + +object_ptr GroupMembers::setupTitle( + not_null call) { + const auto channel = call->channel(); + auto count = channel->session().changes().peerFlagsValue( + channel, + Data::PeerUpdate::Flag::GroupCall + ) | rpl::map([=] { + const auto call = channel->call(); + return std::max(call ? call->fullCount() : 0, 1); + }); + auto result = object_ptr( + _titleWrap, + tr::lng_chat_status_members( + lt_count_decimal, + std::move(count) | tr::to_count(), + Ui::Text::Upper + ), + st::groupCallHeaderLabel); + result->setAttribute(Qt::WA_TransparentForMouseEvents); + return result; +} + +void GroupMembers::setupButtons() { + using namespace rpl::mappers; + + _addMember->showOn(rpl::single(true)); + _addMember->addClickHandler([=] { // TODO throttle(ripple duration) + addMember(); + }); +} + +void GroupMembers::setupList() { + auto topSkip = _header ? _header->height() : 0; + _list = _scroll->setOwnedWidget(object_ptr( + this, + _listController.get(), + st::groupCallMembersList)); + + sizeValue( + ) | rpl::start_with_next([=](QSize size) { + _scroll->setGeometry(0, topSkip, size.width(), size.height() - topSkip); + _list->resizeToWidth(size.width()); + }, _list->lifetime()); + + _list->heightValue( + ) | rpl::start_with_next([=](int listHeight) { + auto newHeight = (listHeight > 0) + ? (topSkip + listHeight) + : 0; + resize(width(), newHeight); + }, _list->lifetime()); + _list->moveToLeft(0, topSkip); + _list->show(); +} + +void GroupMembers::resizeEvent(QResizeEvent *e) { + if (_header) { + updateHeaderControlsGeometry(width()); + } +} + +void GroupMembers::updateHeaderControlsGeometry(int newWidth) { + auto availableWidth = newWidth + - st::groupCallAddButtonPosition.x(); + _addMember->moveToLeft( + availableWidth - _addMember->width(), + st::groupCallAddButtonPosition.y(), + newWidth); + if (!_addMember->isHidden()) { + availableWidth -= _addMember->width(); + } + + _titleWrap->resize( + availableWidth - _addMember->width() - st::groupCallHeaderPosition.x(), + _title->height()); + _titleWrap->moveToLeft( + st::groupCallHeaderPosition.x(), + st::groupCallHeaderPosition.y(), + newWidth); + _titleWrap->setAttribute(Qt::WA_TransparentForMouseEvents); + + _title->resizeToWidth(_titleWrap->width()); + _title->moveToLeft(0, 0); +} + +void GroupMembers::addMember() { + // #TODO calls +} + +void GroupMembers::visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) { + setChildVisibleTopBottom(_list, visibleTop, visibleBottom); +} + +void GroupMembers::peerListSetTitle(rpl::producer title) { +} + +void GroupMembers::peerListSetAdditionalTitle(rpl::producer title) { +} + +bool GroupMembers::peerListIsRowChecked(not_null row) { + return false; +} + +void GroupMembers::peerListScrollToTop() { +} + +int GroupMembers::peerListSelectedRowsCount() { + return 0; +} + +std::vector> GroupMembers::peerListCollectSelectedRows() { + return {}; +} + +void GroupMembers::peerListAddSelectedPeerInBunch(not_null peer) { + Unexpected("Item selection in Info::Profile::Members."); +} + +void GroupMembers::peerListAddSelectedRowInBunch(not_null row) { + Unexpected("Item selection in Info::Profile::Members."); +} + +void GroupMembers::peerListFinishSelectedRowsBunch() { +} + +void GroupMembers::peerListSetDescription( + object_ptr description) { + description.destroy(); +} + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_group_members.h b/Telegram/SourceFiles/calls/calls_group_members.h new file mode 100644 index 000000000..ff1c46385 --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_group_members.h @@ -0,0 +1,76 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "boxes/peer_list_box.h" + +namespace Ui { +class ScrollArea; +} // namespace Ui + +namespace Calls { + +class GroupCall; + +class GroupMembers final + : public Ui::RpWidget + , private PeerListContentDelegate { +public: + GroupMembers( + QWidget *parent, + not_null call); + + int desiredHeight() const; + +private: + using ListWidget = PeerListContent; + + void visibleTopBottomUpdated( + int visibleTop, + int visibleBottom) override; + + void resizeEvent(QResizeEvent *e) override; + + // PeerListContentDelegate interface. + void peerListSetTitle(rpl::producer title) override; + void peerListSetAdditionalTitle(rpl::producer title) override; + bool peerListIsRowChecked(not_null row) override; + int peerListSelectedRowsCount() override; + void peerListScrollToTop() override; + std::vector> peerListCollectSelectedRows() override; + void peerListAddSelectedPeerInBunch( + not_null peer) override; + void peerListAddSelectedRowInBunch( + not_null row) override; + void peerListFinishSelectedRowsBunch() override; + void peerListSetDescription( + object_ptr description) override; + + void setupHeader(not_null call); + object_ptr setupTitle(not_null call); + void setupList(); + + void setupButtons(); + + void addMember(); + void showMembersWithSearch(bool withSearch); + void updateHeaderControlsGeometry(int newWidth); + + base::weak_ptr _call; + object_ptr _scroll; + std::unique_ptr _listController; + object_ptr _header = { nullptr }; + ListWidget *_list = { nullptr }; + + Ui::RpWidget *_titleWrap = nullptr; + Ui::FlatLabel *_title = nullptr; + Ui::IconButton *_addMember = nullptr; + +}; + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index 4309519e4..167173a63 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -7,42 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/calls_group_panel.h" -#include "data/data_photo.h" -#include "data/data_session.h" -#include "data/data_user.h" -#include "data/data_file_origin.h" -#include "data/data_photo_media.h" -#include "data/data_cloud_file.h" -#include "data/data_changes.h" -#include "calls/calls_emoji_fingerprint.h" -#include "calls/calls_signal_bars.h" -#include "calls/calls_userpic.h" -#include "calls/calls_video_bubble.h" +#include "calls/calls_group_members.h" #include "ui/widgets/buttons.h" -#include "ui/widgets/labels.h" -#include "ui/widgets/shadow.h" #include "ui/widgets/window.h" #include "ui/effects/ripple_animation.h" -#include "ui/image/image.h" -#include "ui/text/format_values.h" -#include "ui/wrap/fade_wrap.h" -#include "ui/wrap/padding_wrap.h" -#include "ui/platform/ui_platform_utility.h" -#include "ui/toast/toast.h" -#include "ui/empty_userpic.h" -#include "ui/emoji_config.h" #include "core/application.h" -#include "mainwindow.h" #include "lang/lang_keys.h" -#include "main/main_session.h" -#include "apiwrap.h" -#include "platform/platform_specific.h" -#include "base/platform/base_platform_info.h" -#include "window/main_window.h" +#include "base/event_filter.h" #include "app.h" -#include "webrtc/webrtc_video_track.h" #include "styles/style_calls.h" -#include "styles/style_chat.h" #ifdef Q_OS_WIN #include "ui/platform/win/ui_window_title_win.h" @@ -246,9 +219,9 @@ GroupPanel::GroupPanel(not_null call) #ifdef Q_OS_WIN , _controls(std::make_unique( _window.get(), - st::callTitle, - [=](bool maximized) { toggleFullScreen(maximized); })) + st::callTitle)) #endif // Q_OS_WIN +, _members(widget(), call) , _settings(widget(), st::callCancel) , _hangup(widget(), st::callHangup) , _mute(widget(), st::callMicrophoneMute, &st::callMicrophoneUnmute) { @@ -262,6 +235,9 @@ GroupPanel::GroupPanel(not_null call) GroupPanel::~GroupPanel() = default; void GroupPanel::showAndActivate() { + if (_window->isHidden()) { + _window->show(); + } _window->raise(); _window->setWindowState(_window->windowState() | Qt::WindowActive); _window->activateWindow(); @@ -276,17 +252,13 @@ void GroupPanel::initWindow() { _window->setTitle(u" "_q); _window->setTitleStyle(st::callTitle); - _window->events( - ) | rpl::start_with_next([=](not_null e) { - if (e->type() == QEvent::Close) { - handleClose(); - } else if (e->type() == QEvent::KeyPress) { - if ((static_cast(e.get())->key() == Qt::Key_Escape) - && _window->isFullScreen()) { - _window->showNormal(); - } + base::install_event_filter(_window.get(), [=](not_null e) { + if (e->type() == QEvent::Close && handleClose()) { + e->ignore(); + return base::EventFilterResult::Cancel; } - }, _window->lifetime()); + return base::EventFilterResult::Continue; + }); _window->setBodyTitleArea([=](QPoint widgetPoint) { using Flag = Ui::WindowTitleHitTestFlag; @@ -301,31 +273,8 @@ void GroupPanel::initWindow() { const auto inControls = false; return inControls ? Flag::None - : (Flag::Move | Flag::FullScreen); + : (Flag::Move | Flag::Maximize); }); - -#ifdef Q_OS_WIN - // On Windows we replace snap-to-top maximizing with fullscreen. - // - // We have to switch first to showNormal, so that showFullScreen - // will remember correct normal window geometry and next showNormal - // will show it instead of a moving maximized window. - // - // We have to do it in InvokeQueued, otherwise it still captures - // the maximized window geometry and saves it. - // - // I couldn't find a less glitchy way to do that *sigh*. - const auto object = _window->windowHandle(); - const auto signal = &QWindow::windowStateChanged; - QObject::connect(object, signal, [=](Qt::WindowState state) { - if (state == Qt::WindowMaximized) { - InvokeQueued(object, [=] { - _window->showNormal(); - _window->showFullScreen(); - }); - } - }); -#endif // Q_OS_WIN } void GroupPanel::initWidget() { @@ -382,6 +331,11 @@ void GroupPanel::initWithCall(GroupCall *call) { ) | rpl::start_with_next([=](State state) { stateChanged(state); }, _callLifetime); + + _members->desiredHeightValue( + ) | rpl::start_with_next([=] { + updateControlsGeometry(); + }, _members->lifetime()); } void GroupPanel::initLayout() { @@ -412,22 +366,33 @@ void GroupPanel::initGeometry() { updateControlsGeometry(); } -void GroupPanel::toggleFullScreen(bool fullscreen) { - if (fullscreen) { - _window->showFullScreen(); - } else { - _window->showNormal(); - } -} - void GroupPanel::updateControlsGeometry() { if (widget()->size().isEmpty()) { return; } - const auto top = widget()->height() - 2 * _mute->height(); - _mute->move((widget()->width() - _mute->width()) / 2, top); - _settings->moveToLeft(_settings->width(), top); - _hangup->moveToRight(_settings->width(), top); + const auto desiredHeight = _members->desiredHeight(); + const auto membersWidth = widget()->width() + - st::groupCallMembersMargin.left() + - st::groupCallMembersMargin.right(); + const auto muteTop = widget()->height() - 2 * _mute->height(); + const auto buttonsTop = muteTop; +#ifdef Q_OS_WIN + const auto membersTop = st::callTitleButton.height + + st::groupCallMembersMargin.top() / 2; +#else // Q_OS_WIN + const auto membersTop = st::groupCallMembersMargin.top(); +#endif // Q_OS_WIN + const auto availableHeight = buttonsTop + - membersTop + - st::groupCallMembersMargin.bottom(); + _members->setGeometry( + st::groupCallMembersMargin.left(), + membersTop, + membersWidth, + std::min(desiredHeight, availableHeight)); + _mute->move((widget()->width() - _mute->width()) / 2, muteTop); + _settings->moveToLeft(_settings->width(), buttonsTop); + _hangup->moveToRight(_settings->width(), buttonsTop); } void GroupPanel::paint(QRect clip) { @@ -435,14 +400,16 @@ void GroupPanel::paint(QRect clip) { auto region = QRegion(clip); for (const auto rect : region) { - p.fillRect(rect, st::callBgOpaque); + p.fillRect(rect, st::groupCallBg); } } -void GroupPanel::handleClose() { +bool GroupPanel::handleClose() { if (_call) { - _call->hangup(); + _window->hide(); + return true; } + return false; } not_null GroupPanel::widget() const { diff --git a/Telegram/SourceFiles/calls/calls_group_panel.h b/Telegram/SourceFiles/calls/calls_group_panel.h index cec1fc107..54d98a343 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/calls_group_panel.h @@ -29,6 +29,7 @@ class FadeWrap; template class PaddingWrap; class Window; +class ScrollArea; namespace Platform { class TitleControls; } // namespace Platform @@ -44,6 +45,8 @@ namespace Calls { class Userpic; class SignalBars; +class GroupMembers; + class GroupPanel final { public: GroupPanel(not_null call); @@ -67,14 +70,11 @@ private: void initLayout(); void initGeometry(); - void handleClose(); + bool handleClose(); void updateControlsGeometry(); void stateChanged(State state); void showControls(); - void startDurationUpdateTimer(crl::time currentDuration); - - void toggleFullScreen(bool fullscreen); GroupCall *_call = nullptr; not_null _channel; @@ -87,6 +87,8 @@ private: rpl::lifetime _callLifetime; + object_ptr _members; + object_ptr