diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2cc4daa882..ce2879d696 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1951,7 +1951,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_add_to_group_all" = "Those users aren't members of «{group}» yet. Add them to the group?"; "lng_group_call_invite_members" = "Group members"; "lng_group_call_invite_search_results" = "Search results"; -"lng_group_call_new_muted" = "Mute new members"; +"lng_group_call_new_muted" = "Mute new participants"; "lng_group_call_speakers" = "Speakers"; "lng_group_call_microphone" = "Microphone"; "lng_group_call_push_to_talk" = "Push to Talk Shortcut"; @@ -1961,6 +1961,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_ptt_delay_s" = "{amount}s"; "lng_group_call_ptt_delay" = "Push to Talk release delay: {delay}"; "lng_group_call_share" = "Share Invite Link"; +"lng_group_call_share_listener" = "Mute joined by this link"; "lng_group_call_end" = "End Voice Chat"; "lng_group_call_join" = "Join"; "lng_group_call_invite_done_user" = "You invited {user} to the voice chat."; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 2b5ff08add..6d60d8cf1c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -905,11 +905,12 @@ void ShareInviteLinkBox(not_null peer, const QString &link) { return peer->canWrite(); }; *box = Ui::show( - Box( - App::wnd()->sessionController(), - std::move(copyCallback), - std::move(submitCallback), - std::move(filterCallback)), + Box(ShareBox::Descriptor{ + .session = &peer->session(), + .copyCallback = std::move(copyCallback), + .submitCallback = std::move(submitCallback), + .filterCallback = [](auto peer) { return peer->canWrite(); }, + .navigation = App::wnd()->sessionController() }), Ui::LayerOption::KeepOther); } diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 1b24f3b068..9c99c85dab 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -46,7 +46,7 @@ class ShareBox::Inner final : public Ui::RpWidget, private base::Subscriber { public: Inner( QWidget *parent, - not_null navigation, + not_null session, ShareBox::FilterCallback &&filterCallback); void setPeerSelectedChangedCallback( @@ -121,7 +121,7 @@ private: void refresh(); - const not_null _navigation; + const not_null _session; float64 _columnSkip = 0.; float64 _rowWidthReal = 0.; @@ -153,17 +153,9 @@ private: }; -ShareBox::ShareBox( - QWidget*, - not_null navigation, - CopyCallback &©Callback, - SubmitCallback &&submitCallback, - FilterCallback &&filterCallback) -: _navigation(navigation) -, _api(&_navigation->session().mtp()) -, _copyCallback(std::move(copyCallback)) -, _submitCallback(std::move(submitCallback)) -, _filterCallback(std::move(filterCallback)) +ShareBox::ShareBox(QWidget*, Descriptor &&descriptor) +: _descriptor(std::move(descriptor)) +, _api(&_descriptor.session->mtp()) , _select( this, st::defaultMultiSelect, @@ -202,11 +194,21 @@ void ShareBox::prepareCommentField() { field->setInstantReplacesEnabled( Core::App().settings().replaceEmojiValue()); field->setMarkdownReplacesEnabled(rpl::single(true)); - field->setEditLinkCallback( - DefaultEditLinkCallback(_navigation->parentController(), field)); + if (_descriptor.initEditLink) { + _descriptor.initEditLink(field); + } else if (_descriptor.navigation) { + field->setEditLinkCallback( + DefaultEditLinkCallback( + _descriptor.navigation->parentController(), + field)); + } field->setSubmitSettings(Core::App().settings().sendSubmitWay()); - InitSpellchecker(_navigation->parentController(), field); + if (_descriptor.initSpellchecker) { + _descriptor.initSpellchecker(field); + } else if (_descriptor.navigation) { + InitSpellchecker(_descriptor.navigation->parentController(), field); + } Ui::SendPendingMoveResizeEvents(_comment); } @@ -221,8 +223,8 @@ void ShareBox::prepare() { _inner = setInnerWidget( object_ptr( this, - _navigation, - std::move(_filterCallback)), + _descriptor.session, + std::move(_descriptor.filterCallback)), getTopScrollSkip(), getBottomScrollSkip()); @@ -234,7 +236,7 @@ void ShareBox::prepare() { applyFilterUpdate(query); }); _select->setItemRemovedCallback([=](uint64 itemId) { - if (const auto peer = _navigation->session().data().peerLoaded(itemId)) { + if (const auto peer = _descriptor.session->data().peerLoaded(itemId)) { _inner->peerUnselected(peer); selectedChanged(); update(); @@ -271,7 +273,7 @@ void ShareBox::prepare() { Ui::Emoji::SuggestionsController::Init( getDelegate()->outerContainer(), _comment->entity(), - &_navigation->session()); + _descriptor.session); _select->raise(); } @@ -351,8 +353,8 @@ void ShareBox::peopleDone( switch (result.type()) { case mtpc_contacts_found: { auto &found = result.c_contacts_found(); - _navigation->session().data().processUsers(found.vusers()); - _navigation->session().data().processChats(found.vchats()); + _descriptor.session->data().processUsers(found.vusers()); + _descriptor.session->data().processChats(found.vchats()); _inner->peopleReceived( query, found.vmy_results().v, @@ -429,7 +431,7 @@ void ShareBox::createButtons() { [=] { return sendMenuType(); }, [=] { submitSilent(); }, [=] { submitScheduled(); }); - } else if (_copyCallback) { + } else if (_descriptor.copyCallback) { addButton(tr::lng_share_copy_link(), [=] { copyLink(); }); } addButton(tr::lng_cancel(), [=] { closeBox(); }); @@ -463,8 +465,8 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) { } void ShareBox::submit(Api::SendOptions options) { - if (_submitCallback) { - _submitCallback( + if (const auto onstack = _descriptor.submitCallback) { + onstack( _inner->selected(), _comment->entity()->getTextWithAppliedMarkdown(), options); @@ -485,8 +487,8 @@ void ShareBox::submitScheduled() { } void ShareBox::copyLink() { - if (_copyCallback) { - _copyCallback(); + if (const auto onstack = _descriptor.copyCallback) { + onstack(); } } @@ -522,10 +524,10 @@ void ShareBox::scrollAnimationCallback() { ShareBox::Inner::Inner( QWidget *parent, - not_null navigation, + not_null session, ShareBox::FilterCallback &&filterCallback) : RpWidget(parent) -, _navigation(navigation) +, _session(session) , _filterCallback(std::move(filterCallback)) , _chatsIndexed( std::make_unique( @@ -534,7 +536,7 @@ ShareBox::Inner::Inner( _rowHeight = st::shareRowHeight; setAttribute(Qt::WA_OpaquePaintEvent); - const auto self = _navigation->session().user(); + const auto self = session->user(); if (_filterCallback(self)) { _chatsIndexed->addToEnd(self->owner().history(self)); } @@ -548,30 +550,30 @@ ShareBox::Inner::Inner( } } }; - addList(_navigation->session().data().chatsList()->indexed()); + addList(_session->data().chatsList()->indexed()); const auto id = Data::Folder::kId; - if (const auto folder = _navigation->session().data().folderLoaded(id)) { + if (const auto folder = _session->data().folderLoaded(id)) { addList(folder->chatsList()->indexed()); } - addList(_navigation->session().data().contactsNoChatsList()); + addList(_session->data().contactsNoChatsList()); _filter = qsl("a"); updateFilter(); - _navigation->session().changes().peerUpdates( + _session->changes().peerUpdates( Data::PeerUpdate::Flag::Photo ) | rpl::start_with_next([=](const Data::PeerUpdate &update) { updateChat(update.peer); }, lifetime()); - _navigation->session().changes().realtimeNameUpdates( + _session->changes().realtimeNameUpdates( ) | rpl::start_with_next([=](const Data::NameUpdate &update) { _chatsIndexed->peerNameChanged( update.peer, update.oldFirstLetters); }, lifetime()); - _navigation->session().downloaderTaskFinished( + _session->downloaderTaskFinished( ) | rpl::start_with_next([=] { update(); }, lifetime()); @@ -1030,8 +1032,8 @@ void ShareBox::Inner::peopleReceived( d_byUsernameFiltered.reserve(already + my.size() + people.size()); const auto feedList = [&](const QVector &list) { for (const auto &data : list) { - if (const auto peer = _navigation->session().data().peerLoaded(peerFromMTP(data))) { - const auto history = _navigation->session().data().historyLoaded(peer); + if (const auto peer = _session->data().peerLoaded(peerFromMTP(data))) { + const auto history = _session->data().historyLoaded(peer); if (!_filterCallback(peer)) { continue; } else if (history && _chatsIndexed->getRow(history)) { diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h index 67e19bff66..c680c32b72 100644 --- a/Telegram/SourceFiles/boxes/share_box.h +++ b/Telegram/SourceFiles/boxes/share_box.h @@ -60,12 +60,16 @@ public: Api::SendOptions)>; using FilterCallback = Fn; - ShareBox( - QWidget*, - not_null navigation, - CopyCallback &©Callback, - SubmitCallback &&submitCallback, - FilterCallback &&filterCallback); + struct Descriptor { + not_null session; + CopyCallback copyCallback; + SubmitCallback submitCallback; + FilterCallback filterCallback; + Window::SessionNavigation *navigation = nullptr; + Fn)> initSpellchecker; + Fn)> initEditLink; + }; + ShareBox(QWidget*, Descriptor &&descriptor); protected: void prepare() override; @@ -104,13 +108,9 @@ private: mtpRequestId requestId); void peopleFail(const RPCError &error, mtpRequestId requestId); - const not_null _navigation; + Descriptor _descriptor; MTP::Sender _api; - CopyCallback _copyCallback; - SubmitCallback _submitCallback; - FilterCallback _filterCallback; - object_ptr _select; object_ptr> _comment; diff --git a/Telegram/SourceFiles/calls/calls_group_menu.cpp b/Telegram/SourceFiles/calls/calls_group_menu.cpp index 729762275e..e1633572fb 100644 --- a/Telegram/SourceFiles/calls/calls_group_menu.cpp +++ b/Telegram/SourceFiles/calls/calls_group_menu.cpp @@ -579,7 +579,9 @@ void ConfirmBox( text, st::groupCallBoxLabel), st::boxPadding); - box->addButton(std::move(button), callback); + if (callback) { + box->addButton(std::move(button), callback); + } box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 42d7c9993c..6155320ad8 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -20,6 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" #include "lang/lang_keys.h" +#include "boxes/share_box.h" +#include "history/history_message.h" // GetErrorTextForSending. +#include "data/data_histories.h" +#include "data/data_session.h" #include "base/timer_rpl.h" #include "base/event_filter.h" #include "base/global_shortcuts.h" @@ -86,6 +90,97 @@ void SaveCallJoinMuted( QString::number(delay / 1000., 'f', 2)); } +object_ptr ShareInviteLinkBox( + not_null peer, + const QString &linkSpeaker, + const QString &linkListener, + Fn showToast) { + const auto session = &peer->session(); + const auto sending = std::make_shared(); + const auto box = std::make_shared>(); + + const auto currentLink = [=] { + return linkListener; + }; + auto copyCallback = [=] { + QGuiApplication::clipboard()->setText(currentLink()); + showToast(tr::lng_group_invite_copied(tr::now)); + }; + auto submitCallback = [=]( + std::vector> &&result, + TextWithTags &&comment, + Api::SendOptions options) { + if (*sending || result.empty()) { + return; + } + + const auto error = [&] { + for (const auto peer : result) { + const auto error = GetErrorTextForSending( + peer, + {}, + comment); + if (!error.isEmpty()) { + return std::make_pair(error, peer); + } + } + return std::make_pair(QString(), result.front()); + }(); + if (!error.first.isEmpty()) { + auto text = TextWithEntities(); + if (result.size() > 1) { + text.append( + Ui::Text::Bold(error.second->name) + ).append("\n\n"); + } + text.append(error.first); + if (const auto weak = *box) { + //weak->getDelegate()->show( + // Box(ConfirmBox, text, nullptr, nullptr)); + } + return; + } + + *sending = true; + const auto link = currentLink(); + if (!comment.text.isEmpty()) { + comment.text = link + "\n" + comment.text; + const auto add = link.size() + 1; + for (auto &tag : comment.tags) { + tag.offset += add; + } + } else { + comment.text = link; + } + const auto owner = &peer->owner(); + auto &api = peer->session().api(); + auto &histories = owner->histories(); + const auto requestType = Data::Histories::RequestType::Send; + for (const auto peer : result) { + const auto history = owner->history(peer); + auto message = ApiWrap::MessageToSend(history); + message.textWithTags = comment; + message.action.options = options; + message.action.clearDraft = false; + api.sendMessage(std::move(message)); + } + if (*box) { + (*box)->closeBox(); + } + showToast(tr::lng_share_done(tr::now)); + }; + auto filterCallback = [](PeerData *peer) { + return peer->canWrite(); + }; + auto result = Box(ShareBox::Descriptor{ + .session = &peer->session(), + .copyCallback = std::move(copyCallback), + .submitCallback = std::move(submitCallback), + .filterCallback = std::move(filterCallback) }); + *box = result.data(); + return result; +} + } // namespace void SettingsBox( @@ -104,6 +199,8 @@ void SettingsBox( float micLevel = 0.; Ui::Animations::Simple micLevelAnimation; base::Timer levelUpdateTimer; + QString linkSpeaker; + QString linkListener; bool generatingLink = false; }; const auto state = box->lifetime().make_state(); @@ -408,50 +505,108 @@ void SettingsBox( //AddDivider(layout); //AddSkip(layout); - const auto lookupLink = [=] { - if (const auto group = peer->asMegagroup()) { - return group->hasUsername() - ? group->session().createInternalLinkFull(group->username) - : group->inviteLink(); - } else if (const auto chat = peer->asChat()) { - return chat->inviteLink(); - } - return QString(); - }; - const auto canCreateLink = [&] { - if (const auto chat = peer->asChat()) { - return chat->canHaveInviteLink(); - } else if (const auto group = peer->asMegagroup()) { - return group->canHaveInviteLink(); - } - return false; - }; - if (!lookupLink().isEmpty() || canCreateLink()) { - const auto copyLink = [=] { - const auto link = lookupLink(); - if (link.isEmpty()) { + auto shareLink = Fn(); + if (peer->isChannel() + && peer->asChannel()->hasUsername() + && peer->canManageGroupCall() + && goodReal) { + const auto input = real->input(); + const auto shareReady = [=] { + if (state->linkSpeaker.isEmpty() + || state->linkListener.isEmpty()) { return false; } - QGuiApplication::clipboard()->setText(link); - if (weakBox) { + const auto showToast = crl::guard(box, [=](QString text) { Ui::Toast::Show( box->getDelegate()->outerContainer(), - tr::lng_create_channel_link_copied(tr::now)); - } + text); + }); + box->getDelegate()->show(ShareInviteLinkBox( + peer, + state->linkSpeaker, + state->linkListener, + showToast)); return true; }; + shareLink = [=] { + if (shareReady() || state->generatingLink) { + return; + } + state->generatingLink = true; + // #TODO calls cancel requests on box close + peer->session().api().request(MTPphone_ExportGroupCallInvite( + MTP_flags(0), + input + )).done(crl::guard(box, [=]( + const MTPphone_ExportedGroupCallInvite &result) { + result.match([&]( + const MTPDphone_exportedGroupCallInvite &data) { + state->linkListener = qs(data.vlink()); + shareReady(); + }); + })).send(); + peer->session().api().request(MTPphone_ExportGroupCallInvite( + MTP_flags( + MTPphone_ExportGroupCallInvite::Flag::f_can_self_unmute), + input + )).done(crl::guard(box, [=]( + const MTPphone_ExportedGroupCallInvite &result) { + result.match([&]( + const MTPDphone_exportedGroupCallInvite &data) { + state->linkSpeaker = qs(data.vlink()); + shareReady(); + }); + })).send(); + }; + } else { + const auto lookupLink = [=] { + if (const auto group = peer->asMegagroup()) { + return group->hasUsername() + ? group->session().createInternalLinkFull(group->username) + : group->inviteLink(); + } else if (const auto chat = peer->asChat()) { + return chat->inviteLink(); + } + return QString(); + }; + const auto canCreateLink = [&] { + if (const auto chat = peer->asChat()) { + return chat->canHaveInviteLink(); + } else if (const auto group = peer->asMegagroup()) { + return group->canHaveInviteLink(); + } + return false; + }; + if (!lookupLink().isEmpty() || canCreateLink()) { + const auto copyLink = [=] { + const auto link = lookupLink(); + if (link.isEmpty()) { + return false; + } + QGuiApplication::clipboard()->setText(link); + if (weakBox) { + Ui::Toast::Show( + box->getDelegate()->outerContainer(), + tr::lng_create_channel_link_copied(tr::now)); + } + return true; + }; + shareLink = [=] { + if (!copyLink() && !state->generatingLink) { + state->generatingLink = true; + peer->session().api().inviteLinks().create( + peer, + crl::guard(layout, [=](auto&&) { copyLink(); })); + } + }; + } + } + if (shareLink) { AddButton( layout, tr::lng_group_call_share(), st::groupCallSettingsButton - )->addClickHandler([=] { - if (!copyLink() && !state->generatingLink) { - state->generatingLink = true; - peer->session().api().inviteLinks().create( - peer, - crl::guard(layout, [=](auto&&) { copyLink(); })); - } - }); + )->addClickHandler(std::move(shareLink)); } if (peer->canManageGroupCall()) { diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index b51ada9e9d..070b8e4c7f 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -350,11 +350,12 @@ void FastShareMessage(not_null item) { auto copyLinkCallback = canCopyLink ? Fn(std::move(copyCallback)) : Fn(); - Ui::show(Box( - App::wnd()->sessionController(), - std::move(copyLinkCallback), - std::move(submitCallback), - std::move(filterCallback))); + Ui::show(Box(ShareBox::Descriptor{ + .session = session, + .copyCallback = std::move(copyLinkCallback), + .submitCallback = std::move(submitCallback), + .filterCallback = std::move(filterCallback), + .navigation = App::wnd()->sessionController() })); } Fn HistoryDependentItemCallback(