From c6b2967da0ff05ad2f24e4386a318296101316b9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 10 Apr 2025 13:38:13 +0400 Subject: [PATCH] Implement "Create New Call" interface. --- Telegram/Resources/langs/lang.strings | 4 + Telegram/SourceFiles/calls/calls.style | 55 ++++-- .../calls/calls_box_controller.cpp | 151 +++++++++++++++- .../SourceFiles/calls/calls_box_controller.h | 2 + Telegram/SourceFiles/calls/calls_instance.cpp | 24 +-- Telegram/SourceFiles/calls/calls_instance.h | 4 +- .../calls/group/calls_group_call.cpp | 28 +-- .../calls/group/calls_group_call.h | 4 +- .../calls/group/calls_group_common.cpp | 20 +-- .../group/calls_group_invite_controller.cpp | 165 ++++++++++++++++-- .../group/calls_group_invite_controller.h | 4 + .../SourceFiles/window/window_main_menu.cpp | 141 +-------------- 12 files changed, 375 insertions(+), 227 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 22ee39de43..f676ad892b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4932,6 +4932,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_confcall_already_joined_many#one" = "{user}, {other} and **{count}** other person already joined this call."; "lng_confcall_already_joined_many#other" = "{user}, {other} and **{count}** other people already joined this call."; "lng_confcall_join_button" = "Join Group Call"; +"lng_confcall_create_call" = "Create New Call"; +"lng_confcall_create_call_description#one" = "You can add up to {count} participant to a call."; +"lng_confcall_create_call_description#other" = "You can add up to {count} participants to a call."; +"lng_confcall_create_title" = "New Call"; "lng_confcall_create_link" = "Create Call Link"; "lng_confcall_create_link_description" = "You can create a link that will allow your friends on Telegram to join the call."; "lng_confcall_link_revoke" = "Revoke link"; diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index b3b6595030..f7b2e78eb7 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -1505,6 +1505,42 @@ groupCallCalendarColors: CalendarColors { titleTextColor: groupCallMembersFg; } +createCallInviteLink: SettingsButton(defaultSettingsButton) { + textFg: windowActiveTextFg; + textFgOver: windowActiveTextFg; + textBg: windowBg; + textBgOver: windowBgOver; + + style: TextStyle(defaultTextStyle) { + font: font(14px semibold); + } + + height: 20px; + padding: margins(74px, 8px, 8px, 9px); +} +createCallInviteLinkIcon: icon {{ "info/edit/group_manage_links", windowActiveTextFg }}; +createCallInviteLinkIconPosition: point(23px, 2px); +createCallVideo: IconButton { + width: 36px; + height: 52px; + + icon: icon {{ "info/info_media_video", menuIconFg }}; + iconOver: icon {{ "info/info_media_video", menuIconFgOver }}; + iconPosition: point(-1px, -1px); + + ripple: defaultRippleAnimation; + rippleAreaPosition: point(0px, 8px); + rippleAreaSize: 36px; +} +createCallVideoActive: icon {{ "info/info_media_video", windowActiveTextFg }}; +createCallVideoMargins: margins(0px, 0px, 10px, 0px); +createCallAudio: IconButton(createCallVideo) { + icon: icon {{ "menu/phone", menuIconFg }}; + iconOver: icon {{ "menu/phone", menuIconFgOver }}; +} +createCallAudioActive: icon {{ "menu/phone", windowActiveTextFg }}; +createCallAudioMargins: margins(0px, 0px, 4px, 0px); + confcallLinkButton: RoundButton(defaultActiveButton) { height: 42px; textTop: 12px; @@ -1572,26 +1608,17 @@ confcallInviteParticipants: FlatLabel(defaultFlatLabel) { textFg: callNameFg; } confcallInviteParticipantsPadding: margins(8px, 3px, 12px, 2px); -confcallInviteVideo: IconButton { - width: 36px; - height: 52px; - +confcallInviteVideo: IconButton(createCallVideo) { icon: icon {{ "info/info_media_video", groupCallMemberInactiveIcon }}; iconOver: icon {{ "info/info_media_video", groupCallMemberInactiveIcon }}; - iconPosition: point(-1px, -1px); - ripple: groupCallRipple; - rippleAreaPosition: point(0px, 8px); - rippleAreaSize: 36px; } confcallInviteVideoActive: icon {{ "info/info_media_video", groupCallActiveFg }}; -confcallInviteVideoMargins: margins(0px, 0px, 10px, 0px); confcallInviteAudio: IconButton(confcallInviteVideo) { icon: icon {{ "menu/phone", groupCallMemberInactiveIcon }}; iconOver: icon {{ "menu/phone", groupCallMemberInactiveIcon }}; } confcallInviteAudioActive: icon {{ "menu/phone", groupCallActiveFg }}; -confcallInviteAudioMargins: margins(0px, 0px, 4px, 0px); groupCallLinkBox: Box(confcallLinkBox) { bg: groupCallMembersBg; @@ -1610,23 +1637,17 @@ groupCallLinkPreview: InputField(defaultInputField) { style: defaultTextStyle; heightMin: 35px; } -groupCallInviteLink: SettingsButton(defaultSettingsButton) { +groupCallInviteLink: SettingsButton(createCallInviteLink) { textFg: mediaviewTextLinkFg; textFgOver: mediaviewTextLinkFg; textBg: groupCallMembersBg; textBgOver: groupCallMembersBgOver; - style: TextStyle(defaultTextStyle) { - font: font(14px semibold); - } - - height: 20px; padding: margins(63px, 8px, 8px, 9px); ripple: groupCallRipple; } groupCallInviteLinkIcon: icon {{ "info/edit/group_manage_links", mediaviewTextLinkFg }}; -groupCallInviteLinkIconPosition: point(23px, 2px); confcallLinkMenu: IconButton(boxTitleClose) { icon: icon {{ "title_menu_dots", boxTitleCloseFg }}; diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 1f3337569d..372aa37c53 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -9,17 +9,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/effects/ripple_animation.h" +#include "ui/layers/generic_box.h" +#include "ui/text/text_utilities.h" +#include "ui/widgets/menu/menu_add_action_callback.h" +#include "ui/widgets/menu/menu_add_action_callback_factory.h" #include "ui/widgets/labels.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/popup_menu.h" +#include "ui/wrap/slide_wrap.h" #include "ui/painter.h" +#include "ui/vertical_list.h" #include "core/application.h" +#include "calls/group/calls_group_common.h" +#include "calls/group/calls_group_invite_controller.h" #include "calls/calls_instance.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_item_helpers.h" #include "mainwidget.h" #include "window/window_session_controller.h" +#include "main/main_app_config.h" #include "main/main_session.h" #include "data/data_session.h" #include "data/data_changes.h" @@ -32,6 +41,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "api/api_updates.h" #include "apiwrap.h" +#include "info/profile/info_profile_icon.h" +#include "settings/settings_calls.h" +#include "styles/style_info.h" // infoTopBarMenu #include "styles/style_layers.h" // st::boxLabel. #include "styles/style_calls.h" #include "styles/style_boxes.h" @@ -150,7 +162,7 @@ void GroupCallRow::rightActionStopLastRipple() { namespace GroupCalls { -ListController::ListController(not_null window) +ListController::ListController(not_null<::Window::SessionController*> window) : _window(window) { setStyleOverrides(&st::peerListSingleRow); } @@ -227,7 +239,7 @@ void ListController::rowClicked(not_null row) { crl::on_main(window, [=, peer = row->peer()] { window->showPeerHistory( peer, - Window::SectionShow::Way::ClearStack); + ::Window::SectionShow::Way::ClearStack); }); } @@ -470,7 +482,7 @@ void BoxController::Row::rightActionStopLastRipple() { } } -BoxController::BoxController(not_null window) +BoxController::BoxController(not_null<::Window::SessionController*> window) : _window(window) , _api(&_window->session().mtp()) { } @@ -591,7 +603,7 @@ void BoxController::rowClicked(not_null row) { crl::on_main(window, [=, peer = row->peer()] { window->showPeerHistory( peer, - Window::SectionShow::Way::ClearStack, + ::Window::SectionShow::Way::ClearStack, itemId); }); } @@ -698,7 +710,7 @@ std::unique_ptr BoxController::createRow( void ClearCallsBox( not_null box, - not_null window) { + not_null<::Window::SessionController*> window) { const auto weak = Ui::MakeWeak(box); box->addRow( object_ptr( @@ -756,4 +768,133 @@ void ClearCallsBox( box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } +[[nodiscard]] not_null AddCreateCallButton( + not_null container, + not_null<::Window::SessionController*> controller, + Fn done) { + const auto result = container->add(object_ptr( + container, + tr::lng_confcall_create_call(), + st::inviteViaLinkButton), QMargins()); + Ui::AddSkip(container); + Ui::AddDividerText( + container, + tr::lng_confcall_create_call_description( + lt_count, + rpl::single(controller->session().appConfig().confcallSizeLimit() + * 1.), + Ui::Text::WithEntities)); + + const auto icon = Ui::CreateChild( + result, + st::inviteViaLinkIcon, + QPoint()); + result->heightValue( + ) | rpl::start_with_next([=](int height) { + icon->moveToLeft( + st::inviteViaLinkIconPosition.x(), + (height - st::inviteViaLinkIcon.height()) / 2); + }, icon->lifetime()); + + result->setClickedCallback([=] { + controller->show(Group::PrepareCreateCallBox(controller, done)); + }); + + return result; +} + +void ShowCallsBox(not_null<::Window::SessionController*> window) { + struct State { + State(not_null<::Window::SessionController*> window) + : callsController(window) + , groupCallsController(window) { + } + Calls::BoxController callsController; + PeerListContentDelegateSimple callsDelegate; + + Calls::GroupCalls::ListController groupCallsController; + PeerListContentDelegateSimple groupCallsDelegate; + + base::unique_qptr menu; + }; + + window->show(Box([=](not_null box) { + const auto state = box->lifetime().make_state(window); + + const auto groupCalls = box->addRow( + object_ptr>( + box, + object_ptr(box)), + {}); + groupCalls->hide(anim::type::instant); + groupCalls->toggleOn(state->groupCallsController.shownValue()); + + Ui::AddSubsectionTitle( + groupCalls->entity(), + tr::lng_call_box_groupcalls_subtitle()); + state->groupCallsDelegate.setContent(groupCalls->entity()->add( + object_ptr(box, &state->groupCallsController), + {})); + state->groupCallsController.setDelegate(&state->groupCallsDelegate); + Ui::AddSkip(groupCalls->entity()); + Ui::AddDivider(groupCalls->entity()); + Ui::AddSkip(groupCalls->entity()); + + const auto button = AddCreateCallButton( + box->verticalLayout(), + window, + crl::guard(box, [=] { box->closeBox(); })); + button->events( + ) | rpl::filter([=](not_null e) { + return (e->type() == QEvent::Enter); + }) | rpl::start_with_next([=] { + state->callsDelegate.peerListMouseLeftGeometry(); + }, button->lifetime()); + + const auto content = box->addRow( + object_ptr(box, &state->callsController), + {}); + state->callsDelegate.setContent(content); + state->callsController.setDelegate(&state->callsDelegate); + + box->setWidth(state->callsController.contentWidth()); + state->callsController.boxHeightValue( + ) | rpl::start_with_next([=](int height) { + box->setMinHeight(height); + }, box->lifetime()); + box->setTitle(tr::lng_call_box_title()); + box->addButton(tr::lng_close(), [=] { + box->closeBox(); + }); + const auto menuButton = box->addTopButton(st::infoTopBarMenu); + menuButton->setClickedCallback([=] { + state->menu = base::make_unique_q( + menuButton, + st::popupMenuWithIcons); + const auto showSettings = [=] { + window->showSettings( + Settings::Calls::Id(), + ::Window::SectionShow(anim::type::instant)); + }; + const auto clearAll = crl::guard(box, [=] { + box->uiShow()->showBox(Box(Calls::ClearCallsBox, window)); + }); + state->menu->addAction( + tr::lng_settings_section_call_settings(tr::now), + showSettings, + &st::menuIconSettings); + if (state->callsDelegate.peerListFullRowsCount() > 0) { + Ui::Menu::CreateAddActionCallback(state->menu)({ + .text = tr::lng_call_box_clear_all(tr::now), + .handler = clearAll, + .icon = &st::menuIconDeleteAttention, + .isAttention = true, + }); + } + state->menu->popup(QCursor::pos()); + return true; + }); + })); +} + } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_box_controller.h b/Telegram/SourceFiles/calls/calls_box_controller.h index 05b943b607..9c183f1088 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.h +++ b/Telegram/SourceFiles/calls/calls_box_controller.h @@ -81,4 +81,6 @@ void ClearCallsBox( not_null box, not_null<::Window::SessionController*> window); +void ShowCallsBox(not_null<::Window::SessionController*> window); + } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 145869b109..b3c0cf3c4b 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -238,14 +238,14 @@ void Instance::startOrJoinGroupCall( } void Instance::startOrJoinConferenceCall(StartConferenceInfo args) { - Expects(args.call || (args.migrating && args.show)); + Expects(args.call || args.show); const auto migrationInfo = (args.migrating && args.call && _currentCallPanel) ? _currentCallPanel->migrationInfo() : ConferencePanelMigration(); - if (args.call && !args.migrating) { + if (!args.migrating) { destroyCurrentCall(); } @@ -270,17 +270,17 @@ void Instance::startOrJoinConferenceCall(StartConferenceInfo args) { destroyCurrentCall(args.call.get(), args.linkSlug); } } else { - if (const auto was = base::take(_migratingGroupCall)) { + if (const auto was = base::take(_startingGroupCall)) { destroyGroupCall(was.get()); } - _migratingGroupCall = std::move(call); + _startingGroupCall = std::move(call); } } -void Instance::migratedConferenceReady( +void Instance::startedConferenceReady( not_null call, StartConferenceInfo args) { - if (_migratingGroupCall.get() != call) { + if (_startingGroupCall.get() != call) { return; } const auto migrationInfo = _currentCallPanel @@ -289,7 +289,7 @@ void Instance::migratedConferenceReady( _currentGroupCallPanel = std::make_unique( call, migrationInfo); - _currentGroupCall = std::move(_migratingGroupCall); + _currentGroupCall = std::move(_startingGroupCall); _currentGroupCallChanges.fire_copy(call); const auto real = call->conferenceCall().get(); const auto link = real->conferenceInviteLink(); @@ -461,8 +461,8 @@ void Instance::destroyGroupCall(not_null call) { LOG(("Calls::Instance doesn't prevent quit any more.")); } Core::App().quitPreventFinished(); - } else if (_migratingGroupCall.get() == call) { - base::take(_migratingGroupCall); + } else if (_startingGroupCall.get() == call) { + base::take(_startingGroupCall); } } @@ -695,7 +695,7 @@ void Instance::handleGroupCallUpdate( const MTPUpdate &update) { const auto groupCall = _currentGroupCall ? _currentGroupCall.get() - : _migratingGroupCall.get(); + : _startingGroupCall.get(); if (groupCall && (&groupCall->peer()->session() == session)) { update.match([&](const MTPDupdateGroupCall &data) { groupCall->handlePossibleCreateOrJoinResponse(data); @@ -744,7 +744,7 @@ void Instance::applyGroupCallUpdateChecked( const MTPUpdate &update) { const auto groupCall = _currentGroupCall ? _currentGroupCall.get() - : _migratingGroupCall.get(); + : _startingGroupCall.get(); if (groupCall && (&groupCall->peer()->session() == session)) { groupCall->handleUpdate(update); } @@ -798,7 +798,7 @@ void Instance::destroyCurrentCall( } } } - base::take(_migratingGroupCall); + base::take(_startingGroupCall); } bool Instance::hasVisiblePanel(Main::Session *session) const { diff --git a/Telegram/SourceFiles/calls/calls_instance.h b/Telegram/SourceFiles/calls/calls_instance.h index e42bf32af4..90b0d2f259 100644 --- a/Telegram/SourceFiles/calls/calls_instance.h +++ b/Telegram/SourceFiles/calls/calls_instance.h @@ -86,7 +86,7 @@ public: not_null peer, StartGroupCallArgs args); void startOrJoinConferenceCall(StartConferenceInfo args); - void migratedConferenceReady( + void startedConferenceReady( not_null call, StartConferenceInfo args); void showStartWithRtmp( @@ -203,7 +203,7 @@ private: std::unique_ptr _currentCallPanel; std::unique_ptr _currentGroupCall; - std::unique_ptr _migratingGroupCall; + std::unique_ptr _startingGroupCall; rpl::event_stream _currentGroupCallChanges; std::unique_ptr _currentGroupCallPanel; diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 68bf1aa85e..19f29d16cc 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -662,7 +662,7 @@ GroupCall::GroupCall( if (!canManage() && real->joinMuted()) { _muted = MuteState::ForceMuted; } - } else if (!conference.migrating) { + } else if (!conference.migrating && !conference.show) { _peer->session().changes().peerFlagsValue( _peer, Data::PeerUpdate::Flag::GroupCall @@ -682,18 +682,18 @@ GroupCall::GroupCall( setupMediaDevices(); setupOutgoingVideo(); - if (_conferenceCall || conference.migrating) { + if (_conferenceCall || conference.migrating || conference.show) { setupConference(); } - if (conference.migrating) { + if (conference.migrating || (conference.show && !_conferenceCall)) { if (!conference.muted) { setMuted(MuteState::Active); } - _migratedConferenceInfo = std::make_shared( + _startConferenceInfo = std::make_shared( std::move(conference)); } - if (_id || (!_conferenceCall && _migratedConferenceInfo)) { + if (_id || (!_conferenceCall && _startConferenceInfo)) { initialJoin(); } else { start(join.scheduleDate, join.rtmp); @@ -703,7 +703,7 @@ GroupCall::GroupCall( } } -void GroupCall::processMigration(StartConferenceInfo conference) { +void GroupCall::processConferenceStart(StartConferenceInfo conference) { if (!conference.videoCapture) { return; } @@ -1185,7 +1185,7 @@ bool GroupCall::rtmp() const { } bool GroupCall::conference() const { - return _conferenceCall || _migratedConferenceInfo; + return _conferenceCall || _startConferenceInfo; } bool GroupCall::listenersHidden() const { @@ -1552,7 +1552,7 @@ void GroupCall::rejoin(not_null as) { }; LOG(("Call Info: Join payload received, joining with ssrc: %1." ).arg(_joinState.payload.ssrc)); - if (!_conferenceCall && _migratedConferenceInfo) { + if (!_conferenceCall && _startConferenceInfo) { startConference(); } else if (_conferenceCall && !_conferenceCall->blockchainMayBeEmpty() @@ -1655,7 +1655,7 @@ void GroupCall::refreshLastBlockAndJoin() { } void GroupCall::startConference() { - Expects(_e2e != nullptr && _migratedConferenceInfo != nullptr); + Expects(_e2e != nullptr && _startConferenceInfo != nullptr); const auto joinBlock = _e2e->makeJoinBlock().data; Assert(!joinBlock.isEmpty()); @@ -1706,7 +1706,7 @@ void GroupCall::joinDone( MuteState wasMuteState, bool wasVideoStopped, bool justCreated) { - Expects(!justCreated || _migratedConferenceInfo != nullptr); + Expects(!justCreated || _startConferenceInfo != nullptr); _serverTimeMs = serverTimeMs; _serverTimeMsGotAt = crl::now(); @@ -1728,9 +1728,9 @@ void GroupCall::joinDone( setupConferenceCall(); _conferenceLinkSlug = Group::ExtractConferenceSlug( _conferenceCall->conferenceInviteLink()); - Core::App().calls().migratedConferenceReady( + Core::App().calls().startedConferenceReady( this, - *_migratedConferenceInfo); + *_startConferenceInfo); } applyQueuedSelfUpdates(); @@ -1757,8 +1757,8 @@ void GroupCall::joinDone( sendOutboundBlock(base::take(_pendingOutboundBlock)); } } - if (const auto once = base::take(_migratedConferenceInfo)) { - processMigration(*once); + if (const auto once = base::take(_startConferenceInfo)) { + processConferenceStart(*once); } for (const auto &callback : base::take(_rejoinedCallbacks)) { callback(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index 236290a189..6773391e18 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -635,7 +635,7 @@ private: void markTrackPaused(const VideoEndpoint &endpoint, bool paused); void markTrackShown(const VideoEndpoint &endpoint, bool shown); - void processMigration(StartConferenceInfo conference); + void processConferenceStart(StartConferenceInfo conference); void inviteToConference( InviteRequest request, Fn()> resultAddress, @@ -650,7 +650,7 @@ private: std::shared_ptr _conferenceCall; std::shared_ptr _e2e; QByteArray _pendingOutboundBlock; - std::shared_ptr _migratedConferenceInfo; + std::shared_ptr _startConferenceInfo; not_null _peer; // Can change in legacy group migration. rpl::event_stream _peerStream; diff --git a/Telegram/SourceFiles/calls/group/calls_group_common.cpp b/Telegram/SourceFiles/calls/group/calls_group_common.cpp index 1b98ad2306..79c79e169a 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_common.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_common.cpp @@ -436,8 +436,6 @@ void ShowConferenceCallLinkBox( void MakeConferenceCall(ConferenceFactoryArgs &&args) { const auto show = std::move(args.show); const auto finished = std::move(args.finished); - const auto joining = args.joining; - const auto info = std::move(args.info); const auto session = &show->session(); const auto fail = [=](QString error) { show->showToast(error); @@ -464,20 +462,10 @@ void MakeConferenceCall(ConferenceFactoryArgs &&args) { fail(u"Call link not found!"_q); return; } - if (joining) { - if (auto slug = ExtractConferenceSlug(link); !slug.isEmpty()) { - auto copy = info; - copy.call = call; - copy.linkSlug = std::move(slug); - Core::App().calls().startOrJoinConferenceCall( - std::move(copy)); - } - } else { - Calls::Group::ShowConferenceCallLinkBox( - show, - call, - { .initial = true }); - } + Calls::Group::ShowConferenceCallLinkBox( + show, + call, + { .initial = true }); if (const auto onstack = finished) { finished(true); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp b/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp index d3313b10b0..14c55d2aa6 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp @@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/group/calls_group_common.h" #include "calls/group/calls_group_menu.h" #include "calls/calls_call.h" +#include "calls/calls_instance.h" +#include "core/application.h" #include "boxes/peer_lists_box.h" #include "data/data_user.h" #include "data/data_channel.h" @@ -29,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/painter.h" #include "apiwrap.h" #include "lang/lang_keys.h" +#include "window/window_session_controller.h" #include "styles/style_boxes.h" // membersMarginTop #include "styles/style_calls.h" #include "styles/style_dialogs.h" // searchedBarHeight @@ -65,9 +68,18 @@ namespace { return result; } +struct ConfInviteStyles { + const style::IconButton *video = nullptr; + const style::icon *videoActive = nullptr; + const style::IconButton *audio = nullptr; + const style::icon *audioActive = nullptr; + const style::SettingsButton *inviteViaLink = nullptr; + const style::icon *inviteViaLinkIcon = nullptr; +}; + class ConfInviteRow final : public PeerListRow { public: - using PeerListRow::PeerListRow; + ConfInviteRow(not_null user, const ConfInviteStyles &st); void setAlreadyIn(bool alreadyIn); void setVideo(bool video); @@ -88,6 +100,9 @@ public: int selectedElement) override; private: + [[nodiscard]] const style::IconButton &buttonSt(int element) const; + + const ConfInviteStyles &_st; std::unique_ptr _videoRipple; std::unique_ptr _audioRipple; bool _alreadyIn = false; @@ -99,6 +114,7 @@ class ConfInviteController final : public ContactsBoxController { public: ConfInviteController( not_null session, + ConfInviteStyles st, base::flat_set> alreadyIn, Fn shareLink); @@ -119,6 +135,7 @@ private: [[nodiscard]] int fullCount() const; void toggleRowSelected(not_null row, bool video); + const ConfInviteStyles _st; const base::flat_set> _alreadyIn; const Fn _shareLink; rpl::variable _hasSelected; @@ -127,6 +144,33 @@ private: }; +[[nodiscard]] ConfInviteStyles ConfInviteDarkStyles() { + return { + .video = &st::confcallInviteVideo, + .videoActive = &st::confcallInviteVideoActive, + .audio = &st::confcallInviteAudio, + .audioActive = &st::confcallInviteAudioActive, + .inviteViaLink = &st::groupCallInviteLink, + .inviteViaLinkIcon = &st::groupCallInviteLinkIcon, + }; +} + +[[nodiscard]] ConfInviteStyles ConfInviteDefaultStyles() { + return { + .video = &st::createCallVideo, + .videoActive = &st::createCallVideoActive, + .audio = &st::createCallAudio, + .audioActive = &st::createCallAudioActive, + .inviteViaLink = &st::createCallInviteLink, + .inviteViaLinkIcon = &st::createCallInviteLinkIcon, + }; +} + +ConfInviteRow::ConfInviteRow(not_null user, const ConfInviteStyles &st) +: PeerListRow(user) +, _st(st) { +} + void ConfInviteRow::setAlreadyIn(bool alreadyIn) { _alreadyIn = alreadyIn; setDisabledState(alreadyIn ? State::DisabledChecked : State::Active); @@ -136,6 +180,12 @@ void ConfInviteRow::setVideo(bool video) { _video = video; } +const style::IconButton &ConfInviteRow::buttonSt(int element) const { + return (element == 1) + ? (_st.video ? *_st.video : st::createCallVideo) + : (_st.audio ? *_st.audio : st::createCallAudio); +} + int ConfInviteRow::elementsCount() const { return _alreadyIn ? 0 : 2; } @@ -144,13 +194,11 @@ QRect ConfInviteRow::elementGeometry(int element, int outerWidth) const { if (_alreadyIn || (element != 1 && element != 2)) { return QRect(); } - const auto &st = (element == 1) - ? st::confcallInviteVideo - : st::confcallInviteAudio; + const auto &st = buttonSt(element); const auto size = QSize(st.width, st.height); const auto margins = (element == 1) - ? st::confcallInviteVideoMargins - : st::confcallInviteAudioMargins; + ? st::createCallVideoMargins + : st::createCallAudioMargins; const auto right = margins.right(); const auto top = margins.top(); const auto side = (element == 1) @@ -178,9 +226,7 @@ void ConfInviteRow::elementAddRipple( return; } auto &ripple = (element == 1) ? _videoRipple : _audioRipple; - const auto &st = (element == 1) - ? st::confcallInviteVideo - : st::confcallInviteAudio; + const auto &st = buttonSt(element); if (!ripple) { auto mask = Ui::RippleAnimation::EllipseMask(QSize( st.rippleAreaSize, @@ -211,9 +257,7 @@ void ConfInviteRow::elementsPaint( return; } const auto paintElement = [&](int element) { - const auto &st = (element == 1) - ? st::confcallInviteVideo - : st::confcallInviteAudio; + const auto &st = buttonSt(element); auto &ripple = (element == 1) ? _videoRipple : _audioRipple; const auto active = checked() && ((element == 1) ? _video : !_video); const auto geometry = elementGeometry(element, outerWidth); @@ -230,8 +274,12 @@ void ConfInviteRow::elementsPaint( const auto selected = (element == selectedElement); const auto &icon = active ? (element == 1 - ? st::confcallInviteVideoActive - : st::confcallInviteAudioActive) + ? (_st.videoActive + ? *_st.videoActive + : st::createCallVideoActive) + : (_st.audioActive + ? *_st.audioActive + : st::createCallAudioActive)) : (selected ? st.iconOver : st.icon); icon.paintInCenter(p, geometry); }; @@ -241,9 +289,11 @@ void ConfInviteRow::elementsPaint( ConfInviteController::ConfInviteController( not_null session, + ConfInviteStyles st, base::flat_set> alreadyIn, Fn shareLink) : ContactsBoxController(session) +, _st(st) , _alreadyIn(std::move(alreadyIn)) , _shareLink(std::move(shareLink)) { } @@ -272,7 +322,7 @@ std::unique_ptr ConfInviteController::createRow( || user->isInaccessible()) { return nullptr; } - auto result = std::make_unique(user); + auto result = std::make_unique(user, _st); if (_alreadyIn.contains(user)) { result->setAlreadyIn(true); } @@ -283,7 +333,9 @@ std::unique_ptr ConfInviteController::createRow( } int ConfInviteController::fullCount() const { - return _alreadyIn.size() + delegate()->peerListSelectedRowsCount(); + return _alreadyIn.size() + + delegate()->peerListSelectedRowsCount() + + (_alreadyIn.contains(session().user()) ? 1 : 0); } void ConfInviteController::rowClicked(not_null row) { @@ -336,17 +388,21 @@ void ConfInviteController::prepareViewHook() { object_ptr( nullptr, tr::lng_profile_add_via_link(), - st::groupCallInviteLink), + (_st.inviteViaLink + ? *_st.inviteViaLink + : st::createCallInviteLink)), style::margins(0, st::membersMarginTop, 0, 0)); const auto icon = Ui::CreateChild( button->entity(), - st::groupCallInviteLinkIcon, + (_st.inviteViaLinkIcon + ? *_st.inviteViaLinkIcon + : st::createCallInviteLinkIcon), QPoint()); button->entity()->heightValue( ) | rpl::start_with_next([=](int height) { icon->moveToLeft( - st::groupCallInviteLinkIconPosition.x(), + st::createCallInviteLinkIconPosition.x(), (height - st::groupCallInviteLinkIcon.height()) / 2); }, icon->lifetime()); @@ -504,6 +560,7 @@ object_ptr PrepareInviteBox( }; auto controller = std::make_unique( &real->session(), + ConfInviteDarkStyles(), alreadyIn, shareLink); const auto raw = controller.get(); @@ -674,6 +731,7 @@ object_ptr PrepareInviteBox( auto alreadyIn = base::flat_set>{ user }; auto controller = std::make_unique( &user->session(), + ConfInviteDarkStyles(), alreadyIn, shareLink); const auto raw = controller.get(); @@ -699,4 +757,73 @@ object_ptr PrepareInviteBox( return Box(std::move(controller), initBox); } +object_ptr PrepareCreateCallBox( + not_null<::Window::SessionController*> window, + Fn created) { + struct State { + bool creatingLink = false; + QPointer box; + }; + const auto state = std::make_shared(); + const auto finished = [=](bool ok) { + if (!ok) { + state->creatingLink = false; + } else { + if (const auto strong = state->box.data()) { + strong->closeBox(); + } + if (const auto onstack = created) { + onstack(); + } + } + }; + const auto shareLink = [=] { + if (state->creatingLink) { + return; + } + state->creatingLink = true; + MakeConferenceCall({ + .show = window->uiShow(), + .finished = finished, + }); + }; + auto controller = std::make_unique( + &window->session(), + ConfInviteDefaultStyles(), + base::flat_set>(), + shareLink); + const auto raw = controller.get(); + const auto initBox = [=](not_null box) { + box->setTitle(tr::lng_confcall_create_title()); + + const auto create = [=] { + auto selected = raw->requests(box->collectSelectedRows()); + if (selected.size() != 1) { + Core::App().calls().startOrJoinConferenceCall({ + .show = window->uiShow(), + .invite = std::move(selected), + }); + } else { + const auto &invite = selected.front(); + Core::App().calls().startOutgoingCall( + invite.user, + invite.video); + } + finished(true); + }; + box->addButton( + rpl::conditional( + raw->hasSelectedValue(), + tr::lng_group_call_confcall_add(), + tr::lng_create_group_create()), + create); + box->addButton(tr::lng_close(), [=] { + box->closeBox(); + }); + }; + auto result = Box(std::move(controller), initBox); + state->box = result.data(); + return result; +} + } // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/group/calls_group_invite_controller.h b/Telegram/SourceFiles/calls/group/calls_group_invite_controller.h index 4c1ba17d6f..72779f6d6e 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_invite_controller.h +++ b/Telegram/SourceFiles/calls/group/calls_group_invite_controller.h @@ -87,4 +87,8 @@ private: Fn)> inviteUsers, Fn shareLink); +[[nodiscard]] object_ptr PrepareCreateCallBox( + not_null<::Window::SessionController*> window, + Fn created = nullptr); + } // namespace Calls::Group diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 97256293bb..7e7f796831 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -98,145 +98,6 @@ constexpr auto kPlayStatusLimit = 2; || (now.month() == 1 && now.day() == 1); } -[[nodiscard]] not_null AddCreateCallLinkButton( - not_null container, - not_null controller, - Fn done) { - const auto result = container->add(object_ptr( - container, - tr::lng_confcall_create_link(), - st::inviteViaLinkButton), QMargins()); - Ui::AddSkip(container); - Ui::AddDividerText( - container, - tr::lng_confcall_create_link_description(Ui::Text::WithEntities)); - - const auto icon = Ui::CreateChild( - result, - st::inviteViaLinkIcon, - QPoint()); - result->heightValue( - ) | rpl::start_with_next([=](int height) { - icon->moveToLeft( - st::inviteViaLinkIconPosition.x(), - (height - st::inviteViaLinkIcon.height()) / 2); - }, icon->lifetime()); - - const auto creating = std::make_shared(); - result->setClickedCallback([=] { - if (*creating) { - return; - } - *creating = true; - const auto finished = [=](bool ok) { - if (!ok) { - *creating = false; - } else if (const auto onstack = done) { - onstack(); - } - }; - Calls::Group::MakeConferenceCall({ - .show = controller->uiShow(), - .finished = finished, - }); - }); - return result; -} - -void ShowCallsBox(not_null window) { - struct State { - State(not_null window) - : callsController(window) - , groupCallsController(window) { - } - Calls::BoxController callsController; - PeerListContentDelegateSimple callsDelegate; - - Calls::GroupCalls::ListController groupCallsController; - PeerListContentDelegateSimple groupCallsDelegate; - - base::unique_qptr menu; - }; - - window->show(Box([=](not_null box) { - const auto state = box->lifetime().make_state(window); - - const auto groupCalls = box->addRow( - object_ptr>( - box, - object_ptr(box)), - {}); - groupCalls->hide(anim::type::instant); - groupCalls->toggleOn(state->groupCallsController.shownValue()); - - Ui::AddSubsectionTitle( - groupCalls->entity(), - tr::lng_call_box_groupcalls_subtitle()); - state->groupCallsDelegate.setContent(groupCalls->entity()->add( - object_ptr(box, &state->groupCallsController), - {})); - state->groupCallsController.setDelegate(&state->groupCallsDelegate); - Ui::AddSkip(groupCalls->entity()); - Ui::AddDivider(groupCalls->entity()); - Ui::AddSkip(groupCalls->entity()); - - const auto button = AddCreateCallLinkButton( - box->verticalLayout(), - window, - crl::guard(box, [=] { box->closeBox(); })); - button->events( - ) | rpl::filter([=](not_null e) { - return (e->type() == QEvent::Enter); - }) | rpl::start_with_next([=] { - state->callsDelegate.peerListMouseLeftGeometry(); - }, button->lifetime()); - - const auto content = box->addRow( - object_ptr(box, &state->callsController), - {}); - state->callsDelegate.setContent(content); - state->callsController.setDelegate(&state->callsDelegate); - - box->setWidth(state->callsController.contentWidth()); - state->callsController.boxHeightValue( - ) | rpl::start_with_next([=](int height) { - box->setMinHeight(height); - }, box->lifetime()); - box->setTitle(tr::lng_call_box_title()); - box->addButton(tr::lng_close(), [=] { - box->closeBox(); - }); - const auto menuButton = box->addTopButton(st::infoTopBarMenu); - menuButton->setClickedCallback([=] { - state->menu = base::make_unique_q( - menuButton, - st::popupMenuWithIcons); - const auto showSettings = [=] { - window->showSettings( - Settings::Calls::Id(), - Window::SectionShow(anim::type::instant)); - }; - const auto clearAll = crl::guard(box, [=] { - box->uiShow()->showBox(Box(Calls::ClearCallsBox, window)); - }); - state->menu->addAction( - tr::lng_settings_section_call_settings(tr::now), - showSettings, - &st::menuIconSettings); - if (state->callsDelegate.peerListFullRowsCount() > 0) { - Ui::Menu::CreateAddActionCallback(state->menu)({ - .text = tr::lng_call_box_clear_all(tr::now), - .handler = clearAll, - .icon = &st::menuIconDeleteAttention, - .isAttention = true, - }); - } - state->menu->popup(QCursor::pos()); - return true; - }); - })); -} - [[nodiscard]] rpl::producer SetStatusLabel( not_null session) { const auto self = session->user(); @@ -835,7 +696,7 @@ void MainMenu::setupMenu() { tr::lng_menu_calls(), { &st::menuIconPhone } )->setClickedCallback([=] { - ShowCallsBox(controller); + ::Calls::ShowCallsBox(controller); }); addAction( tr::lng_saved_messages(),