Allow discarding group call when leaving.

This commit is contained in:
John Preston 2020-11-28 17:07:36 +03:00
parent e66a72876c
commit abb81c764e
14 changed files with 204 additions and 36 deletions

View file

@ -270,6 +270,8 @@ PRIVATE
calls/calls_group_members.h
calls/calls_group_panel.cpp
calls/calls_group_panel.h
calls/calls_group_settings.cpp
calls/calls_group_settings.h
calls/calls_emoji_fingerprint.cpp
calls/calls_emoji_fingerprint.h
calls/calls_instance.cpp

View file

@ -1824,6 +1824,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?";
"lng_group_call_also_end" = "End voice chat";
"lng_group_call_settings_title" = "Settings";
"lng_group_call_new_muted" = "Mute new members";
"lng_group_call_speakers" = "Speakers";
"lng_group_call_microphone" = "Microphone";
"lng_group_call_share" = "Share Invite Link";

View file

@ -81,7 +81,7 @@ void GroupCall::setState(State state) {
void GroupCall::start() {
const auto randomId = rand_value<int32>();
_api.request(MTPphone_CreateGroupCall(
_createRequestId = _api.request(MTPphone_CreateGroupCall(
_channel->inputChannel,
MTP_int(randomId)
)).done([=](const MTPUpdates &result) {
@ -155,7 +155,9 @@ void GroupCall::rejoin() {
MTP_dataJSON(MTP_bytes(json))
)).done([=](const MTPUpdates &updates) {
_mySsrc = ssrc;
setState(State::Joined);
setState(_instanceConnected
? State::Joined
: State::Connecting);
applySelfInCallLocally();
if (_muted.current() != muted) {
@ -212,6 +214,24 @@ void GroupCall::hangup() {
finish(FinishType::Ended);
}
void GroupCall::discard() {
if (!_id) {
_api.request(_createRequestId).cancel();
hangup();
return;
}
_api.request(MTPphone_DiscardGroupCall(
inputCall()
)).done([=](const MTPUpdates &result) {
// Here 'this' could be destroyed by updates, so we set Ended after
// updates being handled, but in a guarded way.
crl::on_main(this, [=] { hangup(); });
_channel->session().api().applyUpdates(result);
}).fail([=](const RPCError &error) {
hangup();
}).send();
}
void GroupCall::finish(FinishType type) {
Expects(type != FinishType::None);
@ -330,7 +350,7 @@ void GroupCall::handleUpdate(const MTPGroupCall &call) {
void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
const auto state = _state.current();
if (state != State::Joined) {
if (state != State::Joined && state != State::Connecting) {
return;
}
@ -368,7 +388,8 @@ void GroupCall::createAndStartController() {
tgcalls::GroupInstanceDescriptor descriptor = {
.config = tgcalls::GroupConfig{
},
.networkStateUpdated = [=](bool) {
.networkStateUpdated = [=](bool connected) {
crl::on_main(weak, [=] { setInstanceConnected(connected); });
},
.audioLevelsUpdated = [=](const AudioLevels &data) {
crl::on_main(weak, [=] { audioLevelsUpdated(data); });
@ -432,6 +453,18 @@ void GroupCall::audioLevelsUpdated(
}
}
void GroupCall::setInstanceConnected(bool connected) {
if (_instanceConnected == connected) {
return;
}
_instanceConnected = connected;
if (state() == State::Connecting && connected) {
setState(State::Joined);
} else if (state() == State::Joined && !connected) {
setState(State::Connecting);
}
}
void GroupCall::sendMutedUpdate() {
_api.request(_updateMuteRequestId).cancel();
_updateMuteRequestId = _api.request(MTPphone_EditGroupCallMember(
@ -446,7 +479,8 @@ void GroupCall::sendMutedUpdate() {
}).fail([=](const RPCError &error) {
_updateMuteRequestId = 0;
if (error.type() == u"GROUP_CALL_FORBIDDEN"_q
&& _state.current() == State::Joined) {
&& (_state.current() == State::Joined
|| _state.current() == State::Connecting)) {
setState(State::Joining);
rejoin();
}
@ -475,7 +509,8 @@ void GroupCall::toggleMute(not_null<UserData*> user, bool mute) {
_channel->session().api().applyUpdates(result);
}).fail([=](const RPCError &error) {
if (error.type() == u"GROUP_CALL_FORBIDDEN"_q
&& _state.current() == State::Joined) {
&& (_state.current() == State::Joined
|| _state.current() == State::Connecting)) {
setState(State::Joining);
rejoin();
}

View file

@ -57,6 +57,7 @@ public:
void start();
void hangup();
void discard();
void join(const MTPInputGroupCall &inputCall);
void handleUpdate(const MTPGroupCall &call);
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
@ -68,13 +69,11 @@ public:
[[nodiscard]] rpl::producer<MuteState> mutedValue() const {
return _muted.value();
}
[[nodiscard]] bool joined() const {
return (_state.current() == State::Joined);
}
enum State {
Creating,
Joining,
Connecting,
Joined,
FailedHangingUp,
Failed,
@ -123,6 +122,7 @@ private:
void myLevelUpdated(float level);
void audioLevelsUpdated(
const std::vector<std::pair<std::uint32_t, float>> &data);
void setInstanceConnected(bool connected);
[[nodiscard]] MTPInputGroupCall inputCall() const;
@ -130,6 +130,7 @@ private:
const not_null<ChannelData*> _channel;
MTP::Sender _api;
rpl::variable<State> _state = State::Creating;
bool _instanceConnected = false;
rpl::variable<MuteState> _muted = MuteState::Muted;
bool _acceptFields = false;
@ -137,6 +138,7 @@ private:
uint64 _id = 0;
uint64 _accessHash = 0;
uint32 _mySsrc = 0;
mtpRequestId _createRequestId = 0;
mtpRequestId _updateMuteRequestId = 0;
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;

View file

@ -27,7 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Calls {
namespace {
constexpr auto kLevelThreshold = 0.01;
constexpr auto kLevelThreshold = 0.2;
constexpr auto kLevelActiveTimeout = crl::time(1000);
struct UpdateLevelResult {

View file

@ -8,17 +8,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_group_panel.h"
#include "calls/calls_group_members.h"
#include "calls/calls_group_settings.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/window.h"
#include "ui/widgets/call_button.h"
#include "ui/widgets/call_mute_button.h"
#include "ui/widgets/checkbox.h"
#include "ui/layers/layer_manager.h"
#include "boxes/confirm_box.h"
#include "ui/layers/generic_box.h"
#include "core/application.h"
#include "lang/lang_keys.h"
#include "data/data_channel.h"
#include "base/event_filter.h"
#include "app.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
#ifdef Q_OS_WIN
#include "ui/platform/win/ui_window_title_win.h"
@ -30,6 +34,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Calls {
void LeaveGroupCallBox(
not_null<Ui::GenericBox*> box,
not_null<GroupCall*> call,
bool discardChecked,
BoxContext context) {
box->setTitle(tr::lng_group_call_leave_title());
box->addRow(object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_group_call_leave_sure(),
st::boxLabel));
const auto discard = call->channel()->canManageCall()
? box->addRow(object_ptr<Ui::Checkbox>(
box.get(),
tr::lng_group_call_end(),
discardChecked,
st::defaultBoxCheckbox),
style::margins(
st::boxRowPadding.left(),
st::boxRowPadding.left(),
st::boxRowPadding.right(),
st::boxRowPadding.bottom()))
: nullptr;
const auto weak = base::make_weak(call.get());
box->addButton(tr::lng_group_call_leave(), [=] {
const auto discardCall = (discard && discard->checked());
box->closeBox();
if (!weak) {
return;
} else if (discardCall) {
call->discard();
} else {
call->hangup();
}
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
GroupPanel::GroupPanel(not_null<GroupCall*> call)
: _call(call)
, _channel(call->channel())
@ -96,7 +138,10 @@ void GroupPanel::initWindow() {
return Flag::None | Flag(0);
}
#endif // Q_OS_WIN
const auto inControls = false;
const auto inControls = _settings->geometry().contains(widgetPoint)
|| _mute->innerGeometry().contains(widgetPoint)
|| _hangup->geometry().contains(widgetPoint)
|| _members->geometry().contains(widgetPoint);
return inControls
? Flag::None
: (Flag::Move | Flag::Maximize);
@ -117,6 +162,21 @@ void GroupPanel::initWidget() {
}, widget()->lifetime());
}
void GroupPanel::copyShareLink() {
}
void GroupPanel::hangup(bool discardCallChecked) {
if (!_call) {
return;
}
_layerBg->showBox(Box(
LeaveGroupCallBox,
_call,
discardCallChecked,
BoxContext::GroupCallPanel));
}
void GroupPanel::initControls() {
_mute->clicks(
) | rpl::filter([=](Qt::MouseButton button) {
@ -129,13 +189,12 @@ void GroupPanel::initControls() {
}
}, _mute->lifetime());
_hangup->setClickedCallback([=] {
_layerBg->showBox(Box<ConfirmBox>(
tr::lng_group_call_leave_sure(tr::now),
tr::lng_group_call_leave(tr::now),
[=] { if (_call) _call->hangup(); }));
});
_hangup->setClickedCallback([=] { hangup(false); });
_settings->setClickedCallback([=] {
_layerBg->showBox(Box(
GroupCallSettingsBox,
[=] { copyShareLink(); },
[=] { hangup(true); }));
});
_settings->setText(tr::lng_menu_settings());
@ -176,7 +235,9 @@ void GroupPanel::initWithCall(GroupCall *call) {
rpl::combine(
_call->mutedValue(),
_call->stateValue() | rpl::map(
_1 == State::Creating || _1 == State::Joining
_1 == State::Creating
|| _1 == State::Joining
|| _1 == State::Connecting
)
) | rpl::start_with_next([=](MuteState mute, bool connecting) {
_mute->setState(Ui::CallMuteButtonState{

View file

@ -32,6 +32,7 @@ template <typename Widget>
class PaddingWrap;
class Window;
class ScrollArea;
class GenericBox;
class LayerManager;
namespace Platform {
class TitleControls;
@ -50,6 +51,17 @@ class SignalBars;
class GroupMembers;
enum class BoxContext {
GroupCallPanel,
MainWindow,
};
void LeaveGroupCallBox(
not_null<Ui::GenericBox*> box,
not_null<GroupCall*> call,
bool discardChecked,
BoxContext context);
class GroupPanel final {
public:
GroupPanel(not_null<GroupCall*> call);
@ -77,6 +89,9 @@ private:
void updateControlsGeometry();
void showControls();
void copyShareLink();
void hangup(bool discardCallChecked);
[[nodiscard]] int computeMembersListTop() const;
[[nodiscard]] std::optional<QRect> computeTitleRect() const;
void refreshTitle();

View file

@ -0,0 +1,34 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "calls/calls_group_settings.h"
#include "ui/widgets/checkbox.h"
#include "lang/lang_keys.h"
#include "styles/style_layers.h"
namespace Calls {
void GroupCallSettingsBox(
not_null<Ui::GenericBox*> box,
Fn<void()> copyShareLink,
Fn<void()> discard) {
box->setTitle(tr::lng_group_call_settings_title());
//box->addRow(object_ptr<Ui::Checkbox>(
// box.get(),
// tr::lng_group_call_new_muted(),
// newMuted
// ))
box->addButton(tr::lng_settings_save(), [=] {
box->closeBox();
});
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
}
} // namespace Calls

View file

@ -0,0 +1,19 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/layers/generic_box.h"
namespace Calls {
void GroupCallSettingsBox(
not_null<Ui::GenericBox*> box,
Fn<void()> copyShareLink,
Fn<void()> discard);
} // namespace Calls

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/layers/generic_box.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/text/format_values.h"
#include "lang/lang_keys.h"
@ -16,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_call.h"
#include "calls/calls_instance.h"
#include "calls/calls_signal_bars.h"
#include "calls/calls_group_panel.h" // LeaveGroupCallBox.
#include "data/data_user.h"
#include "data/data_channel.h"
#include "data/data_changes.h"
@ -174,7 +176,11 @@ void TopBar::initControls() {
if (const auto call = _call.get()) {
call->hangup();
} else if (const auto group = _groupCall.get()) {
group->hangup();
Ui::show(Box(
LeaveGroupCallBox,
group,
false,
BoxContext::MainWindow));
}
});
_updateDurationTimer.setCallback([this] { updateDurationText(); });

View file

@ -86,9 +86,6 @@ void GroupCall::requestParticipants() {
}).fail([=](const RPCError &error) {
_fullCount = _participants.size();
_allReceived = true;
_channel->session().changes().peerUpdated(
_channel,
PeerUpdate::Flag::GroupCall);
_participantsRequestId = 0;
}).send();
}
@ -136,13 +133,13 @@ void GroupCall::applyCall(const MTPGroupCall &call, bool force) {
_version = data.vversion().v;
_fullCount = data.vparticipants_count().v;
}, [&](const MTPDgroupCallDiscarded &data) {
const auto changed = (_duration != data.vduration().v)
|| !_finished;
if (!force && !changed) {
return;
}
_finished = true;
_duration = data.vduration().v;
const auto id = _id;
const auto channel = _channel;
crl::on_main(&channel->session(), [=] {
if (channel->call() && channel->call()->id() == id) {
channel->clearCall();
}
});
});
}

View file

@ -53,8 +53,6 @@ public:
[[nodiscard]] rpl::producer<int> fullCountValue() const;
void reload();
[[nodiscard]] bool finished() const;
[[nodiscard]] int duration() const;
private:
void applyCall(const MTPGroupCall &call, bool force);
@ -80,8 +78,6 @@ private:
rpl::event_stream<ParticipantUpdate> _participantUpdates;
rpl::event_stream<> _participantsSliceAdded;
int _duration = 0;
bool _finished = false;
bool _allReceived = false;
};

View file

@ -976,7 +976,7 @@ void MainWidget::setCurrentGroupCall(Calls::GroupCall *call) {
_currentGroupCall->stateValue(
) | rpl::start_with_next([=](Calls::GroupCall::State state) {
using State = Calls::GroupCall::State;
if (state == State::Joined) {
if (state == State::Joined || state == State::Connecting) {
createCallTopBar();
} else {
destroyCallTopBar();

@ -1 +1 @@
Subproject commit 962c8d89e52a8c92bc7987eba4cfaef77a3a9ebf
Subproject commit 41bae1eba67801eb8768887100c17b7e84e7daf7