diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index e7ae258356..520df38eed 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4755,6 +4755,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_also_end_channel" = "End live stream"; "lng_group_call_settings_title" = "Settings"; "lng_group_call_invite" = "Invite Members"; +"lng_group_call_invite_conf" = "Add People"; "lng_group_call_invited_status" = "invited"; "lng_group_call_muted_by_me_status" = "muted for you"; "lng_group_call_invite_title" = "Invite members"; diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index ea9eddd0b8..d74c67f79c 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -804,6 +804,7 @@ groupCallAddMember: SettingsButton(defaultSettingsButton) { ripple: groupCallRipple; } groupCallAddMemberIcon: icon {{ "info/info_add_member", groupCallMemberInactiveIcon, point(0px, 3px) }}; +groupCallShareLinkIcon: icon {{ "menu/links_profile", groupCallMemberInactiveIcon, point(4px, 3px) }}; groupCallSubtitleLabel: FlatLabel(defaultFlatLabel) { maxHeight: 18px; textFg: groupCallMemberNotJoinedStatus; @@ -1489,12 +1490,15 @@ confcallLinkButton: RoundButton(defaultActiveButton) { textTop: 12px; style: semiboldTextStyle; } -confcallLinkBox: Box(defaultBox) { +confcallLinkBoxInitial: Box(defaultBox) { buttonPadding: margins(12px, 11px, 24px, 96px); buttonHeight: 42px; button: confcallLinkButton; shadowIgnoreTopSkip: true; } +confcallLinkBox: Box(confcallLinkBoxInitial) { + buttonPadding: margins(12px, 11px, 24px, 24px); +} confcallLinkCopyButton: RoundButton(confcallLinkButton) { icon: icon {{ "info/edit/links_copy", activeButtonFg }}; iconOver: icon {{ "info/edit/links_copy", activeButtonFgOver }}; @@ -1516,3 +1520,21 @@ confcallLinkFooterOr: FlatLabel(confcallLinkCenteredText) { confcallLinkFooterOrTop: 12px; confcallLinkFooterOrSkip: 8px; confcallLinkFooterOrLineTop: 9px; + +groupCallLinkBox: Box(confcallLinkBox) { + bg: groupCallMembersBg; + title: FlatLabel(boxTitle) { + textFg: groupCallMembersFg; + } + titleAdditionalFg: groupCallMemberNotJoinedStatus; +} +groupCallLinkCenteredText: FlatLabel(confcallLinkCenteredText) { + textFg: groupCallMembersFg; +} +groupCallLinkPreview: InputField(defaultInputField) { + textBg: groupCallMembersBgOver; + textFg: groupCallMembersFg; + textMargins: margins(12px, 8px, 30px, 5px); + style: defaultTextStyle; + heightMin: 35px; +} diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index d171165f20..2a67b56673 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -1153,13 +1153,17 @@ void GroupCall::setRtmpInfo(const Calls::Group::RtmpInfo &value) { } Data::GroupCall *GroupCall::lookupReal() const { - if (_conferenceCall) { - return _conferenceCall.get(); + if (const auto conference = _conferenceCall.get()) { + return conference; } const auto real = _peer->groupCall(); return (real && real->id() == _id) ? real : nullptr; } +std::shared_ptr GroupCall::conferenceCall() const { + return _conferenceCall; +} + rpl::producer> GroupCall::real() const { if (const auto real = lookupReal()) { return rpl::single(not_null{ real }); diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index 4925eccb57..a1534fa72f 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -256,6 +256,7 @@ public: void setRtmpInfo(const Group::RtmpInfo &value); [[nodiscard]] Data::GroupCall *lookupReal() const; + [[nodiscard]] std::shared_ptr conferenceCall() const; [[nodiscard]] rpl::producer> real() const; [[nodiscard]] rpl::producer emojiHashValue() const; diff --git a/Telegram/SourceFiles/calls/group/calls_group_common.cpp b/Telegram/SourceFiles/calls/group/calls_group_common.cpp index 9dad51c406..110e08d76c 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_common.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_common.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/group/calls_group_common.h" +#include "apiwrap.h" #include "base/platform/base_platform_info.h" #include "boxes/share_box.h" #include "core/local_url_handlers.h" @@ -19,8 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/vertical_list.h" #include "lang/lang_keys.h" +#include "main/main_session.h" #include "window/window_session_controller.h" #include "styles/style_layers.h" +#include "styles/style_media_view.h" #include "styles/style_calls.h" #include "styles/style_chat.h" @@ -86,16 +89,34 @@ void ConferenceCallJoinConfirm( }); } +ConferenceCallLinkStyleOverrides DarkConferenceCallLinkStyle() { + return { + .box = &st::groupCallLinkBox, + .close = &st::storiesStealthBoxClose, + .centerLabel = &st::groupCallLinkCenteredText, + .linkPreview = &st::groupCallLinkPreview, + .shareBox = std::make_shared( + DarkShareBoxStyle()), + }; +} + void ShowConferenceCallLinkBox( - not_null controller, + std::shared_ptr show, std::shared_ptr call, const QString &link, - bool initial) { - controller->show(Box([=](not_null box) { - box->setStyle(st::confcallLinkBox); + ConferenceCallLinkArgs &&args) { + const auto st = args.st; + const auto initial = args.initial; + const auto weakWindow = args.weakWindow; + show->showBox(Box([=](not_null box) { + box->setStyle(st.box + ? *st.box + : initial + ? st::confcallLinkBoxInitial + : st::confcallLinkBox); box->setWidth(st::boxWideWidth); box->setNoContentMargin(true); - box->addTopButton(st::boxTitleClose, [=] { + box->addTopButton(st.close ? *st.close : st::boxTitleClose, [=] { box->closeBox(); }); @@ -108,19 +129,24 @@ void ShowConferenceCallLinkBox( object_ptr( box, tr::lng_confcall_link_title(), - st::boxTitle)), + st.box ? st.box->title : st::boxTitle)), st::boxRowPadding + st::confcallLinkTitlePadding); box->addRow( object_ptr( box, tr::lng_confcall_link_about(), - st::confcallLinkCenteredText), + (st.centerLabel + ? *st.centerLabel + : st::confcallLinkCenteredText)), st::boxRowPadding )->setTryMakeSimilarLines(true); Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 2); const auto preview = box->addRow( - Info::BotStarRef::MakeLinkLabel(box, link)); + Info::BotStarRef::MakeLinkLabel( + box, + link, + st.linkPreview)); Ui::AddSkip(box->verticalLayout()); const auto copyCallback = [=] { @@ -128,7 +154,10 @@ void ShowConferenceCallLinkBox( box->uiShow()->showToast(tr::lng_username_copied(tr::now)); }; const auto shareCallback = [=] { - FastShareLink(controller, link); + FastShareLink( + show, + link, + st.shareBox ? *st.shareBox : ShareBoxStyleOverrides()); }; preview->setClickedCallback(copyCallback); [[maybe_unused]] const auto share = box->addButton( @@ -155,6 +184,10 @@ void ShowConferenceCallLinkBox( share->moveToRight(padding.right(), share->y(), width); }, box->lifetime()); + if (!initial) { + return; + } + const auto sep = Ui::CreateChild( copy->parentWidget(), tr::lng_confcall_link_or(), @@ -180,14 +213,18 @@ void ShowConferenceCallLinkBox( rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)), [](QString v) { return Ui::Text::Link(v); }), Ui::Text::WithEntities), - st::confcallLinkCenteredText); + (st.centerLabel + ? *st.centerLabel + : st::confcallLinkCenteredText)); footer->setTryMakeSimilarLines(true); footer->setClickHandlerFilter([=](const auto &...) { const auto local = Core::TryConvertUrlToLocal(link); - controller->resolveConferenceCall( - local, - crl::guard(box, [=](bool ok) { if (ok) box->closeBox(); }), - true); + if (const auto controller = weakWindow.get()) { + controller->resolveConferenceCall( + local, + crl::guard(box, [=](bool ok) { if (ok) box->closeBox(); }), + true); + } return false; }); copy->geometryValue() | rpl::start_with_next([=](QRect geometry) { @@ -209,4 +246,33 @@ void ShowConferenceCallLinkBox( })); } +void ExportConferenceCallLink( + std::shared_ptr show, + std::shared_ptr call, + ConferenceCallLinkArgs &&args) { + const auto session = &show->session(); + const auto finished = std::move(args.finished); + + using Flag = MTPphone_ExportGroupCallInvite::Flag; + session->api().request(MTPphone_ExportGroupCallInvite( + MTP_flags(Flag::f_can_self_unmute), + call->input() + )).done([=](const MTPphone_ExportedGroupCallInvite &result) { + const auto link = qs(result.data().vlink()); + Calls::Group::ShowConferenceCallLinkBox( + show, + call, + link, + base::duplicate(args)); + if (const auto onstack = finished) { + finished(true); + } + }).fail([=](const MTP::Error &error) { + show->showToast(error.type()); + if (const auto onstack = finished) { + finished(false); + } + }).send(); +} + } // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/group/calls_group_common.h b/Telegram/SourceFiles/calls/group/calls_group_common.h index f2716f16bf..1591fe5a2a 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_common.h +++ b/Telegram/SourceFiles/calls/group/calls_group_common.h @@ -8,13 +8,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/object_ptr.h" +#include "base/weak_ptr.h" class UserData; +struct ShareBoxStyleOverrides; + +namespace style { +struct Box; +struct FlatLabel; +struct IconButton; +struct InputField; +} // namespace style namespace Data { class GroupCall; } // namespace Data +namespace Main { +class SessionShow; +} // namespace Main + namespace Ui { class Show; class GenericBox; @@ -118,10 +131,30 @@ void ConferenceCallJoinConfirm( std::shared_ptr call, Fn join); +struct ConferenceCallLinkStyleOverrides { + const style::Box *box = nullptr; + const style::IconButton *close = nullptr; + const style::FlatLabel *centerLabel = nullptr; + const style::InputField *linkPreview = nullptr; + std::shared_ptr shareBox; +}; +[[nodiscard]] ConferenceCallLinkStyleOverrides DarkConferenceCallLinkStyle(); + +struct ConferenceCallLinkArgs { + bool initial = false; + Fn finished; + base::weak_ptr weakWindow = nullptr; + ConferenceCallLinkStyleOverrides st; +}; void ShowConferenceCallLinkBox( - not_null controller, + std::shared_ptr show, std::shared_ptr call, const QString &link, - bool initial = false); + ConferenceCallLinkArgs &&args); + +void ExportConferenceCallLink( + std::shared_ptr show, + std::shared_ptr call, + ConferenceCallLinkArgs &&args); } // namespace Calls::Group diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index 928c3b1f5d..3416b4a014 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -1615,6 +1615,7 @@ rpl::producer Members::desiredHeightValue() const { return rpl::combine( heightValue(), _addMemberButton.value(), + _shareLinkButton.value(), _listController->fullCountValue(), _mode.value() ) | rpl::map([=] { @@ -1626,8 +1627,11 @@ void Members::setupAddMember(not_null call) { using namespace rpl::mappers; const auto peer = call->peer(); + const auto conference = call->conference(); const auto canAddByPeer = [=](not_null peer) { - if (peer->isBroadcast()) { + if (conference) { + return rpl::single(true) | rpl::type_erased(); + } else if (peer->isBroadcast()) { return rpl::single(false) | rpl::type_erased(); } return rpl::combine( @@ -1638,6 +1642,9 @@ void Members::setupAddMember(not_null call) { }) | rpl::type_erased(); }; const auto canInviteByLinkByPeer = [=](not_null peer) { + if (conference) { + return rpl::single(true) | rpl::type_erased(); + } const auto channel = peer->asChannel(); if (!channel) { return rpl::single(false) | rpl::type_erased(); @@ -1672,11 +1679,18 @@ void Members::setupAddMember(not_null call) { _addMemberButton = nullptr; updateControlsGeometry(); } + if (const auto old = _shareLinkButton.current()) { + delete old; + _shareLinkButton = nullptr; + updateControlsGeometry(); + } return; } auto addMember = Settings::CreateButtonWithIcon( _layout.get(), - tr::lng_group_call_invite(), + (conference + ? tr::lng_group_call_invite_conf() + : tr::lng_group_call_invite()), st::groupCallAddMember, { .icon = &st::groupCallAddMemberIcon }); addMember->clicks( @@ -1688,6 +1702,21 @@ void Members::setupAddMember(not_null call) { delete _addMemberButton.current(); _addMemberButton = addMember.data(); _layout->insert(3, std::move(addMember)); + if (conference) { + auto shareLink = Settings::CreateButtonWithIcon( + _layout.get(), + tr::lng_group_invite_share(), + st::groupCallAddMember, + { .icon = &st::groupCallShareLinkIcon }); + shareLink->clicks() | rpl::to_empty | rpl::start_to_stream( + _shareLinkRequests, + shareLink->lifetime()); + shareLink->show(); + shareLink->resizeToWidth(_layout->width()); + delete _shareLinkButton.current(); + _shareLinkButton = shareLink.data(); + _layout->insert(4, std::move(shareLink)); + } }, lifetime()); updateControlsGeometry(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.h b/Telegram/SourceFiles/calls/group/calls_group_members.h index 47cb059143..355f45f5f1 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.h +++ b/Telegram/SourceFiles/calls/group/calls_group_members.h @@ -59,6 +59,9 @@ public: [[nodiscard]] rpl::producer<> addMembersRequests() const { return _addMemberRequests.events(); } + [[nodiscard]] rpl::producer<> shareLinkRequests() const { + return _shareLinkRequests.events(); + } [[nodiscard]] MembersRow *lookupRow(not_null peer) const; [[nodiscard]] not_null rtmpFakeRow( @@ -106,10 +109,12 @@ private: const not_null _videoWrap; std::unique_ptr _viewport; rpl::variable _addMemberButton = nullptr; + rpl::variable _shareLinkButton = nullptr; RpWidget *_topSkip = nullptr; RpWidget *_bottomSkip = nullptr; ListWidget *_list = nullptr; rpl::event_stream<> _addMemberRequests; + rpl::event_stream<> _shareLinkRequests; mutable std::unique_ptr _rtmpFakeRow; diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index e50397b40f..8dced0375a 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -934,16 +934,12 @@ void Panel::setupMembers() { _members->toggleMuteRequests( ) | rpl::start_with_next([=](MuteRequest request) { - if (_call) { - _call->toggleMute(request); - } + _call->toggleMute(request); }, _callLifetime); _members->changeVolumeRequests( ) | rpl::start_with_next([=](VolumeRequest request) { - if (_call) { - _call->changeVolume(request); - } + _call->changeVolume(request); }, _callLifetime); _members->kickParticipantRequests( @@ -964,6 +960,21 @@ void Panel::setupMembers() { } }, _callLifetime); + const auto exporting = std::make_shared(); + _members->shareLinkRequests( + ) | rpl::start_with_next([=] { + Expects(_call->conference()); + + if (*exporting) { + return; + } + *exporting = true; + ExportConferenceCallLink(uiShow(), _call->conferenceCall(), { + .st = DarkConferenceCallLinkStyle(), + .finished = [=](bool) { *exporting = false; }, + }); + }, _callLifetime); + _call->videoEndpointLargeValue( ) | rpl::start_with_next([=](const VideoEndpoint &large) { if (large && mode() != PanelMode::Wide) { diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp index 50149d8a63..26130b96f0 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp @@ -390,14 +390,16 @@ void AddFullWidthButtonFooter( object_ptr MakeLinkLabel( not_null parent, - const QString &link) { + const QString &link, + const style::InputField *stOverride) { + const auto &st = stOverride ? *stOverride : st::dialogsFilter; const auto text = link.startsWith(u"https://"_q) ? link.mid(8) : link.startsWith(u"http://"_q) ? link.mid(7) : link; - const auto margins = st::dialogsFilter.textMargins; - const auto height = st::dialogsFilter.heightMin; + const auto margins = st.textMargins; + const auto height = st.heightMin; const auto skip = margins.left(); auto result = object_ptr(parent); @@ -408,12 +410,12 @@ object_ptr MakeLinkLabel( auto p = QPainter(raw); auto hq = PainterHighQualityEnabler(p); p.setPen(Qt::NoPen); - p.setBrush(st::dialogsFilter.textBg); + p.setBrush(st.textBg); const auto radius = st::roundRadiusLarge; p.drawRoundedRect(0, 0, raw->width(), height, radius, radius); - const auto font = st::dialogsFilter.style.font; - p.setPen(st::dialogsFilter.textFg); + const auto font = st.style.font; + p.setPen(st.textFg); p.setFont(font); const auto available = raw->width() - skip * 2; p.drawText( @@ -1065,4 +1067,4 @@ object_ptr CreateLinkHeaderIcon( return CreateLinkIcon(parent, session, usersCount); } -} // namespace Info::BotStarRef \ No newline at end of file +} // namespace Info::BotStarRef diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h index 46e643241d..d69864bf15 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h @@ -21,6 +21,7 @@ class Show; namespace style { struct RoundButton; +struct InputField; } // namespace style namespace Main { @@ -107,7 +108,8 @@ void FinishProgram( [[nodiscard]] object_ptr MakeLinkLabel( not_null parent, - const QString &link); + const QString &link, + const style::InputField *stOverride = nullptr); [[nodiscard]] object_ptr CreateLinkHeaderIcon( not_null parent, not_null session, diff --git a/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style b/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style index 1558fb259b..d70bc257b7 100644 --- a/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style +++ b/Telegram/SourceFiles/info/channel_statistics/boosts/giveaway/giveaway.style @@ -259,3 +259,6 @@ darkGiftTableMessage: FlatLabel(giveawayGiftMessage) { textFg: groupCallMembersFg; palette: darkGiftPalette; } +darkGiftCodeLink: FlatLabel(giveawayGiftCodeLink) { + textFg: mediaviewMenuFg; +} diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 3606e9cf87..361ed511e0 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -123,7 +123,7 @@ constexpr auto kPlayStatusLimit = 2; (height - st::inviteViaLinkIcon.height()) / 2); }, icon->lifetime()); - const auto creating = result->lifetime().make_state(); + const auto creating = std::make_shared(); result->setClickedCallback([=] { if (*creating) { return; @@ -143,25 +143,19 @@ constexpr auto kPlayStatusLimit = 2; false, // rtmp true); // conference call->processFullCall(result); - using Flag = MTPphone_ExportGroupCallInvite::Flag; - session->api().request(MTPphone_ExportGroupCallInvite( - MTP_flags(Flag::f_can_self_unmute), - MTP_inputGroupCall(data.vid(), data.vaccess_hash()) - )).done(crl::guard(controller, [=]( - const MTPphone_ExportedGroupCallInvite &result) { - const auto link = qs(result.data().vlink()); - Calls::Group::ShowConferenceCallLinkBox( - controller, - call, - link, - true); - if (const auto onstack = done) { + const auto finished = [=](bool ok) { + if (!ok) { + *creating = 0; + } else if (const auto onstack = done) { onstack(); } - })).fail(crl::guard(controller, [=](const MTP::Error &error) { - show->showToast(error.type()); - *creating = 0; - })).send(); + }; + const auto show = controller->uiShow(); + Calls::Group::ExportConferenceCallLink(show, call, { + .initial = true, + .finished = finished, + .weakWindow = controller, + }); }); })).fail(crl::guard(controller, [=](const MTP::Error &error) { show->showToast(error.type());