diff --git a/Telegram/Resources/icons/calls/group_calls_share.png b/Telegram/Resources/icons/calls/group_calls_share.png new file mode 100644 index 000000000..cc2dc5b2c Binary files /dev/null and b/Telegram/Resources/icons/calls/group_calls_share.png differ diff --git a/Telegram/Resources/icons/calls/group_calls_share@2x.png b/Telegram/Resources/icons/calls/group_calls_share@2x.png new file mode 100644 index 000000000..9ce9df041 Binary files /dev/null and b/Telegram/Resources/icons/calls/group_calls_share@2x.png differ diff --git a/Telegram/Resources/icons/calls/group_calls_share@3x.png b/Telegram/Resources/icons/calls/group_calls_share@3x.png new file mode 100644 index 000000000..76abb0e88 Binary files /dev/null and b/Telegram/Resources/icons/calls/group_calls_share@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index b02dac3d2..1b38d3463 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1982,6 +1982,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_inactive" = "listening"; "lng_group_call_raised_hand_status" = "wants to speak"; "lng_group_call_settings" = "Settings"; +"lng_group_call_share_button" = "Share"; "lng_group_call_unmute" = "Unmute"; "lng_group_call_unmute_sub" = "or hold spacebar to talk"; "lng_group_call_you_are_live" = "You are Live"; diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 49c52292d..121ef924e 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -685,13 +685,19 @@ groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInac groupCallMemberInvitedPosition: point(2px, 12px); groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMemberInactiveStatus }}; +groupCallSettingsInner: IconButton(callButton) { + iconPosition: point(-1px, 22px); + icon: icon {{ "calls/call_settings", groupCallIconFg }}; + ripple: RippleAnimation(defaultRippleAnimation) { + color: callMuteRipple; + } +} groupCallSettings: CallButton(callMicrophoneMute) { - button: IconButton(callButton) { - iconPosition: point(-1px, 22px); - icon: icon {{ "calls/call_settings", groupCallIconFg }}; - ripple: RippleAnimation(defaultRippleAnimation) { - color: callMuteRipple; - } + button: groupCallSettingsInner; +} +groupCallShare: CallButton(groupCallSettings) { + button: IconButton(groupCallSettingsInner) { + icon: icon {{ "calls/group_calls_share", groupCallIconFg }}; } } groupCallHangup: CallButton(callHangup) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index f31018aba..b3ea5ca66 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -244,6 +244,9 @@ GroupCall::GroupCall( } else { start(info.scheduleDate); } + if (_scheduleDate) { + saveDefaultJoinAs(_joinAs); + } _mediaDevices->audioInputId( ) | rpl::start_with_next([=](QString id) { @@ -465,6 +468,14 @@ void GroupCall::setJoinAs(not_null as) { } } +void GroupCall::saveDefaultJoinAs(not_null as) { + setJoinAs(as); + _api.request(MTPphone_SaveDefaultGroupCallJoinAs( + _peer->input, + _joinAs->input + )).send(); +} + void GroupCall::rejoin(not_null as) { if (state() != State::Joining && state() != State::Joined @@ -695,11 +706,7 @@ void GroupCall::rejoinAs(Group::JoinInfo info) { .nowJoinAs = info.joinAs, }; if (_scheduleDate) { - setJoinAs(info.joinAs); - _api.request(MTPphone_SaveDefaultGroupCallJoinAs( - _peer->input, - _joinAs->input - )).send(); + saveDefaultJoinAs(info.joinAs); } else { setState(State::Joining); rejoin(info.joinAs); diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index b070e5cdb..6792f7a31 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -256,6 +256,7 @@ private: void rejoin(); void rejoin(not_null as); void setJoinAs(not_null as); + void saveDefaultJoinAs(not_null as); void subscribeToReal(not_null real); void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data); diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index 932de26a1..c0f29e4f4 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -175,13 +175,13 @@ private: })) | rpl::flatten_latest(); if (tillTomorrow > 0) { - return std::move(full); + return full; } else if (tillToday > 0) { - return std::move(tomorrowAndAfter); + return tomorrowAndAfter; } else if (tillAfter > 0) { - return std::move(todayAndAfter); + return todayAndAfter; } else { - return std::move(exact); + return exact; } }) | rpl::flatten_latest(); } @@ -381,7 +381,6 @@ Panel::Panel(not_null call) _window->body(), st::groupCallTitle)) #endif // !Q_OS_MAC -, _settings(widget(), st::groupCallSettings) , _mute(std::make_unique( widget(), Core::App().appDeactivatedValue(), @@ -395,7 +394,6 @@ Panel::Panel(not_null call) })) , _hangup(widget(), st::groupCallHangup) { _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox); - _settings->setColorOverrides(_mute->colorOverrides()); _layerBg->setHideByBackgroundClick(true); SubscribeToMigration( @@ -591,12 +589,11 @@ void Panel::initControls() { _call->setMutedAndUpdate(newState); }, _mute->lifetime()); - _hangup->setClickedCallback([=] { endCall(); }); - _settings->setClickedCallback([=] { - _layerBg->showBox(Box(SettingsBox, _call)); - }); + initShareAction(); + refreshLeftButton(); + + _hangup->setClickedCallback([=] { endCall(); }); - _settings->setText(tr::lng_group_call_settings()); const auto scheduleDate = _call->scheduleDate(); _hangup->setText(scheduleDate ? tr::lng_group_call_close() @@ -606,12 +603,27 @@ void Panel::initControls() { ) | rpl::map([=](not_null real) { return real->scheduleDateValue(); }) | rpl::flatten_latest(); + setupScheduledLabels(rpl::single( scheduleDate ) | rpl::then(rpl::duplicate(changes))); - std::move(changes) | rpl::filter([](TimeId date) { + + auto started = std::move(changes) | rpl::filter([](TimeId date) { return (date == 0); - }) | rpl::take(1) | rpl::start_with_next([=] { + }) | rpl::take(1); + + rpl::merge( + rpl::duplicate(started) | rpl::to_empty, + _peer->session().changes().peerFlagsValue( + _peer, + Data::PeerUpdate::Flag::Username + ) | rpl::skip(1) | rpl::to_empty + ) | rpl::start_with_next([=] { + refreshLeftButton(); + updateControlsGeometry(); + }, _callLifetime); + + std::move(started) | rpl::start_with_next([=] { _hangup->setText(tr::lng_group_call_leave()); setupMembers(); }, _callLifetime); @@ -640,6 +652,53 @@ void Panel::initControls() { }, _callLifetime); } +void Panel::refreshLeftButton() { + const auto share = _call->scheduleDate() + && _peer->isBroadcast() + && _peer->asChannel()->hasUsername(); + if ((share && _share) || (!share && _settings)) { + return; + } + if (share) { + _settings.destroy(); + _share.create(widget(), st::groupCallShare); + _share->setClickedCallback(_shareLinkCallback); + _share->setText(tr::lng_group_call_share_button()); + } else { + _share.destroy(); + _settings.create(widget(), st::groupCallSettings); + _settings->setClickedCallback([=] { + _layerBg->showBox(Box(SettingsBox, _call)); + }); + _settings->setText(tr::lng_group_call_settings()); + } + const auto raw = _share ? _share.data() : _settings.data(); + raw->show(); + raw->setColorOverrides(_mute->colorOverrides()); +} + +void Panel::initShareAction() { + const auto showBox = [=](object_ptr next) { + _layerBg->showBox(std::move(next)); + }; + const auto showToast = [=](QString text) { + Ui::ShowMultilineToast({ + .parentOverride = widget(), + .text = { text }, + }); + }; + auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction( + _peer, + showBox, + showToast); + _shareLinkCallback = [=, callback = std::move(shareLinkCallback)] { + if (_call->lookupReal()) { + callback(); + } + }; + widget()->lifetime().add(std::move(shareLinkLifetime)); +} + void Panel::setupRealMuteButtonState(not_null real) { using namespace rpl::mappers; rpl::combine( @@ -792,26 +851,10 @@ void Panel::setupMembers() { kickParticipant(participantPeer); }, _callLifetime); - const auto showBox = [=](object_ptr next) { - _layerBg->showBox(std::move(next)); - }; - const auto showToast = [=](QString text) { - Ui::ShowMultilineToast({ - .parentOverride = widget(), - .text = { text }, - }); - }; - auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction( - _peer, - showBox, - showToast); - auto shareLink = std::move(shareLinkCallback); - _members->lifetime().add(std::move(shareLinkLifetime)); - _members->addMembersRequests( ) | rpl::start_with_next([=] { if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) { - shareLink(); + _shareLinkCallback(); } else { addMembers(); } @@ -1304,18 +1347,24 @@ QRect Panel::computeTitleRect() const { } void Panel::updateControlsGeometry() { - if (widget()->size().isEmpty()) { + if (widget()->size().isEmpty() || (!_settings && !_share)) { return; } const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; const auto buttonsTop = widget()->height() - st::groupCallButtonBottomSkip; const auto muteSize = _mute->innerSize().width(); const auto fullWidth = muteSize - + 2 * _settings->width() + + 2 * (_settings ? _settings : _share)->width() + 2 * st::groupCallButtonSkip; _mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop }); - _settings->moveToLeft((widget()->width() - fullWidth) / 2, buttonsTop); - _hangup->moveToRight((widget()->width() - fullWidth) / 2, buttonsTop); + const auto leftButtonLeft = (widget()->width() - fullWidth) / 2; + if (_settings) { + _settings->moveToLeft(leftButtonLeft, buttonsTop); + } + if (_share) { + _share->moveToLeft(leftButtonLeft, buttonsTop); + } + _hangup->moveToRight(leftButtonLeft, buttonsTop); updateMembersGeometry(); refreshTitle(); diff --git a/Telegram/SourceFiles/calls/calls_group_panel.h b/Telegram/SourceFiles/calls/calls_group_panel.h index c05ba7be9..71d68042c 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/calls_group_panel.h @@ -74,6 +74,7 @@ private: void initWindow(); void initWidget(); void initControls(); + void initShareAction(); void initLayout(); void initGeometry(); void setupScheduledLabels(rpl::producer date); @@ -89,6 +90,7 @@ private: void updateControlsGeometry(); void updateMembersGeometry(); void showControls(); + void refreshLeftButton(); void endCall(); @@ -131,9 +133,11 @@ private: object_ptr _startsWhen = { nullptr }; ChooseJoinAsProcess _joinAsProcess; - object_ptr _settings; + object_ptr _settings = { nullptr }; + object_ptr _share = { nullptr }; std::unique_ptr _mute; object_ptr _hangup; + Fn _shareLinkCallback; rpl::lifetime _peerLifetime; diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 79e0bfdc1..9ae8bbcae 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -676,7 +676,7 @@ std::pair, rpl::lifetime> ShareInviteLinkAction( return true; }; auto callback = [=] { - const auto real = peer->groupCall(); + const auto real = peer->migrateToOrMe()->groupCall(); if (shareReady() || state->generatingLink || !real) { return; } @@ -701,11 +701,11 @@ std::pair, rpl::lifetime> ShareInviteLinkAction( state->linkSpeakerRequestId = peer->session().api().request( MTPphone_ExportGroupCallInvite( MTP_flags(Flag::f_can_self_unmute), - real->input() - )).done([=](const MTPphone_ExportedGroupCallInvite &result) { + real->input()) + ).done([=](const MTPphone_ExportedGroupCallInvite &result) { state->linkSpeakerRequestId = 0; result.match([&]( - const MTPDphone_exportedGroupCallInvite &data) { + const MTPDphone_exportedGroupCallInvite &data) { state->linkSpeaker = qs(data.vlink()); shareReady(); });