Start voice chat link sharing.

This commit is contained in:
John Preston 2021-03-10 20:38:15 +04:00
parent cd8c9a58df
commit 6786d44b69
7 changed files with 257 additions and 95 deletions

View File

@ -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.";

View File

@ -905,11 +905,12 @@ void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) {
return peer->canWrite();
};
*box = Ui::show(
Box<ShareBox>(
App::wnd()->sessionController(),
std::move(copyCallback),
std::move(submitCallback),
std::move(filterCallback)),
Box<ShareBox>(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);
}

View File

@ -46,7 +46,7 @@ class ShareBox::Inner final : public Ui::RpWidget, private base::Subscriber {
public:
Inner(
QWidget *parent,
not_null<Window::SessionNavigation*> navigation,
not_null<Main::Session*> session,
ShareBox::FilterCallback &&filterCallback);
void setPeerSelectedChangedCallback(
@ -121,7 +121,7 @@ private:
void refresh();
const not_null<Window::SessionNavigation*> _navigation;
const not_null<Main::Session*> _session;
float64 _columnSkip = 0.;
float64 _rowWidthReal = 0.;
@ -153,17 +153,9 @@ private:
};
ShareBox::ShareBox(
QWidget*,
not_null<Window::SessionNavigation*> navigation,
CopyCallback &&copyCallback,
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<Inner>(
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<Window::SessionNavigation*> navigation,
not_null<Main::Session*> session,
ShareBox::FilterCallback &&filterCallback)
: RpWidget(parent)
, _navigation(navigation)
, _session(session)
, _filterCallback(std::move(filterCallback))
, _chatsIndexed(
std::make_unique<Dialogs::IndexedList>(
@ -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<MTPPeer> &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)) {

View File

@ -60,12 +60,16 @@ public:
Api::SendOptions)>;
using FilterCallback = Fn<bool(PeerData*)>;
ShareBox(
QWidget*,
not_null<Window::SessionNavigation*> navigation,
CopyCallback &&copyCallback,
SubmitCallback &&submitCallback,
FilterCallback &&filterCallback);
struct Descriptor {
not_null<Main::Session*> session;
CopyCallback copyCallback;
SubmitCallback submitCallback;
FilterCallback filterCallback;
Window::SessionNavigation *navigation = nullptr;
Fn<void(not_null<Ui::InputField*>)> initSpellchecker;
Fn<void(not_null<Ui::InputField*>)> 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<Window::SessionNavigation*> _navigation;
Descriptor _descriptor;
MTP::Sender _api;
CopyCallback _copyCallback;
SubmitCallback _submitCallback;
FilterCallback _filterCallback;
object_ptr<Ui::MultiSelect> _select;
object_ptr<Ui::SlideWrap<Ui::InputField>> _comment;

View File

@ -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(); });
}

View File

@ -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<ShareBox> ShareInviteLinkBox(
not_null<PeerData*> peer,
const QString &linkSpeaker,
const QString &linkListener,
Fn<void(QString)> showToast) {
const auto session = &peer->session();
const auto sending = std::make_shared<bool>();
const auto box = std::make_shared<QPointer<ShareBox>>();
const auto currentLink = [=] {
return linkListener;
};
auto copyCallback = [=] {
QGuiApplication::clipboard()->setText(currentLink());
showToast(tr::lng_group_invite_copied(tr::now));
};
auto submitCallback = [=](
std::vector<not_null<PeerData*>> &&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>(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<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<void()>();
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()) {

View File

@ -350,11 +350,12 @@ void FastShareMessage(not_null<HistoryItem*> item) {
auto copyLinkCallback = canCopyLink
? Fn<void()>(std::move(copyCallback))
: Fn<void()>();
Ui::show(Box<ShareBox>(
App::wnd()->sessionController(),
std::move(copyLinkCallback),
std::move(submitCallback),
std::move(filterCallback)));
Ui::show(Box<ShareBox>(ShareBox::Descriptor{
.session = session,
.copyCallback = std::move(copyLinkCallback),
.submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback),
.navigation = App::wnd()->sessionController() }));
}
Fn<void(ChannelData*, MsgId)> HistoryDependentItemCallback(