Join other calls with confirmation.

This commit is contained in:
John Preston 2020-12-08 19:09:13 +04:00
parent 529c12ea3a
commit 546881c720
11 changed files with 119 additions and 58 deletions

View file

@ -1777,6 +1777,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_call_error_audio_io" = "There seems to be a problem with audio playback on your computer. Please make sure that your computer's speakers and microphone are working and try again."; "lng_call_error_audio_io" = "There seems to be a problem with audio playback on your computer. Please make sure that your computer's speakers and microphone are working and try again.";
"lng_call_bar_hangup" = "End call"; "lng_call_bar_hangup" = "End call";
"lng_call_leave_to_other_sure" = "Do you want to end your active call and join a voice chat in this group?";
"lng_call_box_title" = "Calls"; "lng_call_box_title" = "Calls";
"lng_call_box_about" = "You haven't made any Telegram calls yet."; "lng_call_box_about" = "You haven't made any Telegram calls yet.";
@ -1824,6 +1825,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_leave" = "Leave"; "lng_group_call_leave" = "Leave";
"lng_group_call_leave_title" = "Leave voice chat"; "lng_group_call_leave_title" = "Leave voice chat";
"lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?"; "lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?";
"lng_group_call_leave_to_other_sure" = "Do you want to leave your active voice chat and join a voice chat in this group?";
"lng_group_call_also_end" = "End voice chat"; "lng_group_call_also_end" = "End voice chat";
"lng_group_call_settings_title" = "Settings"; "lng_group_call_settings_title" = "Settings";
"lng_group_call_invite_title" = "Invite members"; "lng_group_call_invite_title" = "Invite members";

View file

@ -1036,7 +1036,12 @@ void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|| (_videoOutgoing->state() != Webrtc::VideoState::Inactive)) || (_videoOutgoing->state() != Webrtc::VideoState::Inactive))
? MTPphone_DiscardCall::Flag::f_video ? MTPphone_DiscardCall::Flag::f_video
: MTPphone_DiscardCall::Flag(0); : MTPphone_DiscardCall::Flag(0);
_api.request(MTPphone_DiscardCall(
// We want to discard request still being sent and processed even if
// the call is already destroyed.
const auto session = &_user->session();
const auto weak = base::make_weak(this);
session->api().request(MTPphone_DiscardCall( // We send 'discard' here.
MTP_flags(flags), MTP_flags(flags),
MTP_inputPhoneCall( MTP_inputPhoneCall(
MTP_long(_id), MTP_long(_id),
@ -1047,11 +1052,11 @@ void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
// Here 'this' could be destroyed by updates, so we set Ended after // Here 'this' could be destroyed by updates, so we set Ended after
// updates being handled, but in a guarded way. // updates being handled, but in a guarded way.
crl::on_main(this, [=] { setState(finalState); }); crl::on_main(weak, [=] { setState(finalState); });
_user->session().api().applyUpdates(result); session->api().applyUpdates(result);
}).fail([this, finalState](const RPCError &error) { }).fail(crl::guard(weak, [this, finalState](const RPCError &error) {
setState(finalState); setState(finalState);
}).send(); })).send();
} }
void Call::setStateQueued(State state) { void Call::setStateQueued(State state) {

View file

@ -330,17 +330,22 @@ void GroupCall::finish(FinishType type) {
} }
setState(hangupState); setState(hangupState);
_api.request(MTPphone_LeaveGroupCall(
// We want to leave request still being sent and processed even if
// the call is already destroyed.
const auto session = &_channel->session();
const auto weak = base::make_weak(this);
session->api().request(MTPphone_LeaveGroupCall(
inputCall(), inputCall(),
MTP_int(_mySsrc) MTP_int(_mySsrc)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
// Here 'this' could be destroyed by updates, so we set Ended after // Here 'this' could be destroyed by updates, so we set Ended after
// updates being handled, but in a guarded way. // updates being handled, but in a guarded way.
crl::on_main(this, [=] { setState(finalState); }); crl::on_main(weak, [=] { setState(finalState); });
_channel->session().api().applyUpdates(result); session->api().applyUpdates(result);
}).fail([=](const RPCError &error) { }).fail(crl::guard(weak, [=](const RPCError &error) {
setState(finalState); setState(finalState);
}).send(); })).send();
} }
void GroupCall::setMuted(MuteState mute) { void GroupCall::setMuted(MuteState mute) {

View file

@ -58,23 +58,13 @@ void Instance::startOutgoingCall(not_null<UserData*> user, bool video) {
}), video); }), video);
} }
void Instance::startGroupCall(not_null<ChannelData*> channel) { void Instance::startOrJoinGroupCall(not_null<ChannelData*> channel) {
if (activateCurrentCall()) { destroyCurrentCall();
return;
}
requestPermissionsOrFail(crl::guard(this, [=] { requestPermissionsOrFail(crl::guard(this, [=] {
createGroupCall(channel, MTP_inputGroupCall(MTPlong(), MTPlong())); const auto call = channel->call();
}), false); createGroupCall(
} channel,
call ? call->input() : MTP_inputGroupCall(MTPlong(), MTPlong()));
void Instance::joinGroupCall(
not_null<ChannelData*> channel,
const MTPInputGroupCall &call) {
if (activateCurrentCall()) {
return;
}
requestPermissionsOrFail(crl::guard(this, [=] {
createGroupCall(channel, call);
}), false); }), false);
} }
@ -432,7 +422,11 @@ void Instance::handleSignalingData(
} }
bool Instance::inCall() const { bool Instance::inCall() const {
return (_currentCall && _currentCall->state() != Call::State::Busy); if (!_currentCall) {
return false;
}
const auto state = _currentCall->state();
return (state != Call::State::Busy);
} }
bool Instance::inGroupCall() const { bool Instance::inGroupCall() const {
@ -446,6 +440,21 @@ bool Instance::inGroupCall() const {
&& (state != GroupCall::State::Failed); && (state != GroupCall::State::Failed);
} }
void Instance::destroyCurrentCall() {
if (const auto current = currentCall()) {
current->hangup();
if (const auto still = currentCall()) {
destroyCall(still);
}
}
if (const auto current = currentGroupCall()) {
current->hangup();
if (const auto still = currentGroupCall()) {
destroyGroupCall(still);
}
}
}
bool Instance::activateCurrentCall() { bool Instance::activateCurrentCall() {
if (inCall()) { if (inCall()) {
_currentCallPanel->showAndActivate(); _currentCallPanel->showAndActivate();

View file

@ -40,10 +40,7 @@ public:
~Instance(); ~Instance();
void startOutgoingCall(not_null<UserData*> user, bool video); void startOutgoingCall(not_null<UserData*> user, bool video);
void startGroupCall(not_null<ChannelData*> channel); void startOrJoinGroupCall(not_null<ChannelData*> channel);
void joinGroupCall(
not_null<ChannelData*> channel,
const MTPInputGroupCall &call);
void handleUpdate( void handleUpdate(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPUpdate &update); const MTPUpdate &update);
@ -53,6 +50,9 @@ public:
[[nodiscard]] rpl::producer<Call*> currentCallValue() const; [[nodiscard]] rpl::producer<Call*> currentCallValue() const;
[[nodiscard]] GroupCall *currentGroupCall() const; [[nodiscard]] GroupCall *currentGroupCall() const;
[[nodiscard]] rpl::producer<GroupCall*> currentGroupCallValue() const; [[nodiscard]] rpl::producer<GroupCall*> currentGroupCallValue() const;
[[nodiscard]] bool inCall() const;
[[nodiscard]] bool inGroupCall() const;
bool activateCurrentCall();
std::shared_ptr<tgcalls::VideoCaptureInterface> getVideoCapture() override; std::shared_ptr<tgcalls::VideoCaptureInterface> getVideoCapture() override;
void setCurrentAudioDevice(bool input, const QString &deviceId); void setCurrentAudioDevice(bool input, const QString &deviceId);
@ -98,9 +98,7 @@ private:
void refreshServerConfig(not_null<Main::Session*> session); void refreshServerConfig(not_null<Main::Session*> session);
bytes::const_span updateDhConfig(const MTPmessages_DhConfig &data); bytes::const_span updateDhConfig(const MTPmessages_DhConfig &data);
bool activateCurrentCall(); void destroyCurrentCall();
[[nodiscard]] bool inCall() const;
[[nodiscard]] bool inGroupCall() const;
void handleCallUpdate( void handleCallUpdate(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPPhoneCall &call); const MTPPhoneCall &call);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_domain.h" // Core::App().domain().activate().
#include "apiwrap.h" #include "apiwrap.h"
#include "layout.h" #include "layout.h"
#include "history/history.h" #include "history/history.h"
@ -75,6 +76,24 @@ constexpr auto kPinnedMessageTextLimit = 16;
}); });
} }
[[nodiscard]] ClickHandlerPtr ChannelCallClickHandler(
not_null<ChannelData*> megagroup,
uint64 callId) {
return std::make_shared<LambdaClickHandler>([=] {
const auto call = megagroup->call();
if (call && call->id() == callId) {
const auto &windows = megagroup->session().windows();
if (windows.empty()) {
Core::App().domain().activate(&megagroup->session().account());
if (windows.empty()) {
return;
}
}
windows.front()->startOrJoinGroupCall(megagroup);
}
});
}
} // namespace } // namespace
void HistoryService::setMessageByAction(const MTPmessageAction &action) { void HistoryService::setMessageByAction(const MTPmessageAction &action) {
@ -518,12 +537,7 @@ HistoryService::PreparedText HistoryService::prepareStartedCallText(
const auto channel = history()->peer->asChannel(); const auto channel = history()->peer->asChannel();
auto chatText = tr::lng_action_group_call_started_chat(tr::now); auto chatText = tr::lng_action_group_call_started_chat(tr::now);
if (channel && linkCallId) { if (channel && linkCallId) {
result.links.push_back(std::make_shared<LambdaClickHandler>([=] { result.links.push_back(ChannelCallClickHandler(channel, linkCallId));
const auto call = channel->call();
if (call && call->id() == linkCallId) {
Core::App().calls().joinGroupCall(channel, call->input());
}
}));
chatText = textcmdLink(2, chatText); chatText = textcmdLink(2, chatText);
} }
result.text = tr::lng_action_group_call_started( result.text = tr::lng_action_group_call_started(
@ -545,12 +559,7 @@ HistoryService::PreparedText HistoryService::prepareInvitedToCallText(
result.links.push_back(fromLink()); result.links.push_back(fromLink());
auto linkIndex = 1; auto linkIndex = 1;
if (channel && linkCallId) { if (channel && linkCallId) {
result.links.push_back(std::make_shared<LambdaClickHandler>([=] { result.links.push_back(ChannelCallClickHandler(channel, linkCallId));
const auto call = channel->call();
if (call && call->id() == linkCallId) {
Core::App().calls().joinGroupCall(channel, call->input());
}
}));
chatText = textcmdLink(++linkIndex, chatText); chatText = textcmdLink(++linkIndex, chatText);
} }
if (users.size() == 1) { if (users.size() == 1) {

View file

@ -5441,8 +5441,8 @@ void HistoryWidget::setupGroupCallTracker() {
.text = tr::lng_group_call_no_anonymous(tr::now), .text = tr::lng_group_call_no_anonymous(tr::now),
}); });
return; return;
} else if (const auto call = channel->call()) { } else if (channel->call()) {
Core::App().calls().joinGroupCall(channel, call->input()); controller()->startOrJoinGroupCall(channel);
} }
}, _groupCallBar->lifetime()); }, _groupCallBar->lifetime());

View file

@ -221,18 +221,10 @@ void TopBarWidget::call() {
void TopBarWidget::groupCall() { void TopBarWidget::groupCall() {
if (const auto peer = _activeChat.key.peer()) { if (const auto peer = _activeChat.key.peer()) {
if (const auto megagroup = peer->asMegagroup()) { if (const auto megagroup = peer->asMegagroup()) {
if (megagroup->amAnonymous()) { _controller->startOrJoinGroupCall(megagroup);
Ui::ShowMultilineToast({
.text = tr::lng_group_call_no_anonymous(tr::now),
});
} else if (const auto call = megagroup->call()) {
Core::App().calls().joinGroupCall(megagroup, call->input());
} else {
Core::App().calls().startGroupCall(megagroup);
}
} else if (const auto chat = peer->asChat()) { } else if (const auto chat = peer->asChat()) {
const auto start = [=](not_null<ChannelData*> megagroup) { const auto start = [=](not_null<ChannelData*> megagroup) {
Core::App().calls().startGroupCall(megagroup); _controller->startOrJoinGroupCall(megagroup);
}; };
peer->session().api().migrateChat(chat, crl::guard(this, start)); peer->session().api().migrateChat(chat, crl::guard(this, start));
} }

View file

@ -97,6 +97,7 @@ private:
void call(); void call();
void groupCall(); void groupCall();
void startGroupCall(not_null<ChannelData*> megagroup, bool confirmed);
void search(); void search();
void showMenu(); void showMenu();
void toggleInfoSection(); void toggleInfoSection();

View file

@ -38,6 +38,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/delayed_activation.h" #include "ui/delayed_activation.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/toasts/common_toasts.h"
#include "calls/calls_instance.h" // Core::App().calls().inCall().
#include "boxes/calendar_box.h" #include "boxes/calendar_box.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "mainwidget.h" #include "mainwidget.h"
@ -930,6 +932,40 @@ void SessionController::closeThirdSection() {
} }
} }
void SessionController::startOrJoinGroupCall(
not_null<ChannelData*> megagroup,
bool confirmedLeaveOther) {
if (megagroup->amAnonymous()) {
Ui::ShowMultilineToast({
.text = tr::lng_group_call_no_anonymous(tr::now),
});
return;
}
auto &calls = Core::App().calls();
const auto confirm = [&](QString text, QString button) {
Ui::show(Box<ConfirmBox>(text, button, crl::guard(this, [=] {
Ui::hideLayer();
startOrJoinGroupCall(megagroup, true);
})));
};
if (!confirmedLeaveOther && calls.inCall()) {
// Do you want to leave your active voice chat to join a voice chat in this group?
confirm(
tr::lng_call_leave_to_other_sure(tr::now),
tr::lng_call_bar_hangup(tr::now));
} else if (!confirmedLeaveOther && calls.inGroupCall()) {
if (calls.currentGroupCall()->channel() == megagroup) {
calls.activateCurrentCall();
} else {
confirm(
tr::lng_group_call_leave_to_other_sure(tr::now),
tr::lng_group_call_leave(tr::now));
}
} else {
calls.startOrJoinGroupCall(megagroup);
}
}
void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) {
const auto currentPeerDate = [&] { const auto currentPeerDate = [&] {
if (const auto history = chat.history()) { if (const auto history = chat.history()) {

View file

@ -296,6 +296,10 @@ public:
void resizeForThirdSection(); void resizeForThirdSection();
void closeThirdSection(); void closeThirdSection();
void startOrJoinGroupCall(
not_null<ChannelData*> megagroup,
bool confirmedLeaveOther = false);
void showSection( void showSection(
SectionMemento &&memento, SectionMemento &&memento,
const SectionShow &params = SectionShow()) override; const SectionShow &params = SectionShow()) override;