Allow to rejoin with changing of 'join_as'.

This commit is contained in:
John Preston 2021-03-05 19:21:11 +04:00
parent 4d093f78e2
commit b670ca2a51
13 changed files with 235 additions and 123 deletions

View file

@ -287,7 +287,7 @@ updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
updateMessageID#4e90bfd6 id:int random_id:long = Update; updateMessageID#4e90bfd6 id:int random_id:long = Update;
updateDeleteMessages#a20db0e5 messages:Vector<int> pts:int pts_count:int = Update; updateDeleteMessages#a20db0e5 messages:Vector<int> pts:int pts_count:int = Update;
updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update; updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update;
updateChatUserTyping#9a65ea1f chat_id:int user_id:int action:SendMessageAction = Update; updateChatUserTyping#86cadb6c chat_id:int from_id:Peer action:SendMessageAction = Update;
updateChatParticipants#7761198 participants:ChatParticipants = Update; updateChatParticipants#7761198 participants:ChatParticipants = Update;
updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update; updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update; updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update;
@ -364,7 +364,7 @@ updateChannelMessageForwards#6e8a84df channel_id:int id:int forwards:int = Updat
updateReadChannelDiscussionInbox#1cc7de54 flags:# channel_id:int top_msg_id:int read_max_id:int broadcast_id:flags.0?int broadcast_post:flags.0?int = Update; updateReadChannelDiscussionInbox#1cc7de54 flags:# channel_id:int top_msg_id:int read_max_id:int broadcast_id:flags.0?int broadcast_post:flags.0?int = Update;
updateReadChannelDiscussionOutbox#4638a26c channel_id:int top_msg_id:int read_max_id:int = Update; updateReadChannelDiscussionOutbox#4638a26c channel_id:int top_msg_id:int read_max_id:int = Update;
updatePeerBlocked#246a4b22 peer_id:Peer blocked:Bool = Update; updatePeerBlocked#246a4b22 peer_id:Peer blocked:Bool = Update;
updateChannelUserTyping#ff2abe9f flags:# channel_id:int top_msg_id:flags.0?int user_id:int action:SendMessageAction = Update; updateChannelUserTyping#6b171718 flags:# channel_id:int top_msg_id:flags.0?int from_id:Peer action:SendMessageAction = Update;
updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vector<int> pts:int pts_count:int = Update; updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vector<int> pts:int pts_count:int = Update;
updatePinnedChannelMessages#8588878b flags:# pinned:flags.0?true channel_id:int messages:Vector<int> pts:int pts_count:int = Update; updatePinnedChannelMessages#8588878b flags:# pinned:flags.0?true channel_id:int messages:Vector<int> pts:int pts_count:int = Update;
updateChat#1330a196 chat_id:int = Update; updateChat#1330a196 chat_id:int = Update;
@ -1207,7 +1207,7 @@ groupCall#c0c2052e flags:# join_muted:flags.1?true can_change_join_muted:flags.2
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
groupCallParticipant#7c48057b flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:flags.12?int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long = GroupCallParticipant; groupCallParticipant#19adba89 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long = GroupCallParticipant;
phone.groupCall#9e727aad call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string chats:Vector<Chat> users:Vector<User> = phone.GroupCall; phone.groupCall#9e727aad call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string chats:Vector<Chat> users:Vector<User> = phone.GroupCall;
@ -1611,14 +1611,14 @@ phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall durati
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates; phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool; phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
phone.createGroupCall#7c068f5 peer:InputPeer join_as:InputPeer random_id:int = Updates; phone.createGroupCall#bd3dabe0 peer:InputPeer random_id:int = Updates;
phone.joinGroupCall#3633a5b0 flags:# muted:flags.0?true call:InputGroupCall join_as:InputPeer params:DataJSON = Updates; phone.joinGroupCall#3633a5b0 flags:# muted:flags.0?true call:InputGroupCall join_as:InputPeer params:DataJSON = Updates;
phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates; phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates; phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates;
phone.discardGroupCall#7a777135 call:InputGroupCall = Updates; phone.discardGroupCall#7a777135 call:InputGroupCall = Updates;
phone.toggleGroupCallSettings#74bbb43d flags:# call:InputGroupCall join_muted:flags.0?Bool = Updates; phone.toggleGroupCallSettings#74bbb43d flags:# call:InputGroupCall join_muted:flags.0?Bool = Updates;
phone.getGroupCall#c7cb017 call:InputGroupCall = phone.GroupCall; phone.getGroupCall#c7cb017 call:InputGroupCall = phone.GroupCall;
phone.getGroupParticipants#c9f1d285 call:InputGroupCall ids:Vector<int> sources:Vector<int> offset:string limit:int = phone.GroupParticipants; phone.getGroupParticipants#c558d8ab call:InputGroupCall ids:Vector<InputPeer> sources:Vector<int> offset:string limit:int = phone.GroupParticipants;
phone.checkGroupCall#b74a7bea call:InputGroupCall source:int = Bool; phone.checkGroupCall#b74a7bea call:InputGroupCall source:int = Bool;
phone.toggleGroupCallRecord#c02a66d7 flags:# start:flags.0?true call:InputGroupCall title:flags.1?string = Updates; phone.toggleGroupCallRecord#c02a66d7 flags:# start:flags.0?true call:InputGroupCall title:flags.1?string = Updates;
phone.editGroupCallParticipant#d975eb80 flags:# muted:flags.0?true call:InputGroupCall participant:InputPeer volume:flags.1?int raise_hand:flags.2?Bool = Updates; phone.editGroupCallParticipant#d975eb80 flags:# muted:flags.0?true call:InputGroupCall participant:InputPeer volume:flags.1?int raise_hand:flags.2?Bool = Updates;

View file

@ -240,16 +240,16 @@ Updates::Updates(not_null<Main::Session*> session)
) | rpl::filter([](not_null<PeerData*> peer) { ) | rpl::filter([](not_null<PeerData*> peer) {
return peer->isChat() || peer->isMegagroup(); return peer->isChat() || peer->isMegagroup();
}) | rpl::start_with_next([=](not_null<PeerData*> peer) { }) | rpl::start_with_next([=](not_null<PeerData*> peer) {
if (const auto users = _pendingSpeakingCallMembers.take(peer)) { if (const auto list = _pendingSpeakingCallParticipants.take(peer)) {
if (const auto call = peer->groupCall()) { if (const auto call = peer->groupCall()) {
for (const auto [userId, when] : *users) { for (const auto [participantPeerId, when] : *list) {
call->applyActiveUpdate( call->applyActiveUpdate(
userId, participantPeerId,
Data::LastSpokeTimes{ Data::LastSpokeTimes{
.anything = when, .anything = when,
.voice = when .voice = when
}, },
peer->owner().userLoaded(userId)); peer->owner().peerLoaded(participantPeerId));
} }
} }
} }
@ -915,16 +915,16 @@ bool Updates::isQuitPrevent() {
void Updates::handleSendActionUpdate( void Updates::handleSendActionUpdate(
PeerId peerId, PeerId peerId,
MsgId rootId, MsgId rootId,
UserId userId, PeerId fromId,
const MTPSendMessageAction &action) { const MTPSendMessageAction &action) {
const auto history = session().data().historyLoaded(peerId); const auto history = session().data().historyLoaded(peerId);
if (!history) { if (!history) {
return; return;
} }
const auto peer = history->peer; const auto peer = history->peer;
const auto user = (userId == session().userId()) const auto from = (fromId == session().userPeerId())
? session().user().get() ? session().user().get()
: session().data().userLoaded(userId); : session().data().peerLoaded(fromId);
const auto isSpeakingInCall = (action.type() const auto isSpeakingInCall = (action.type()
== mtpc_speakingInGroupCallAction); == mtpc_speakingInGroupCallAction);
if (isSpeakingInCall) { if (isSpeakingInCall) {
@ -935,9 +935,9 @@ void Updates::handleSendActionUpdate(
const auto now = crl::now(); const auto now = crl::now();
if (call) { if (call) {
call->applyActiveUpdate( call->applyActiveUpdate(
userId, fromId,
Data::LastSpokeTimes{ .anything = now, .voice = now }, Data::LastSpokeTimes{ .anything = now, .voice = now },
user); from);
} else { } else {
const auto chat = peer->asChat(); const auto chat = peer->asChat();
const auto channel = peer->asChannel(); const auto channel = peer->asChannel();
@ -945,13 +945,15 @@ void Updates::handleSendActionUpdate(
? (chat->flags() & MTPDchat::Flag::f_call_active) ? (chat->flags() & MTPDchat::Flag::f_call_active)
: (channel->flags() & MTPDchannel::Flag::f_call_active); : (channel->flags() & MTPDchannel::Flag::f_call_active);
if (active) { if (active) {
_pendingSpeakingCallMembers.emplace( _pendingSpeakingCallParticipants.emplace(
peer).first->second[userId] = now; peer).first->second[fromId] = now;
session().api().requestFullPeer(peer); if (peerIsUser(fromId)) {
session().api().requestFullPeer(peer);
}
} }
} }
} }
if (!user || user->isSelf()) { if (!from || !from->isUser() || from->isSelf()) {
return; return;
} }
const auto when = requestingDifference() const auto when = requestingDifference()
@ -960,7 +962,7 @@ void Updates::handleSendActionUpdate(
session().data().registerSendAction( session().data().registerSendAction(
history, history,
rootId, rootId,
user, from->asUser(),
action, action,
when); when);
} }
@ -1642,19 +1644,21 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChatUserTyping: { case mtpc_updateChatUserTyping: {
auto &d = update.c_updateChatUserTyping(); auto &d = update.c_updateChatUserTyping();
const auto fromId = peerFromMTP(d.vfrom_id());
handleSendActionUpdate( handleSendActionUpdate(
peerFromChat(d.vchat_id()), peerFromChat(d.vchat_id()),
0, 0,
d.vuser_id().v, fromId,
d.vaction()); d.vaction());
} break; } break;
case mtpc_updateChannelUserTyping: { case mtpc_updateChannelUserTyping: {
const auto &d = update.c_updateChannelUserTyping(); const auto &d = update.c_updateChannelUserTyping();
const auto fromId = peerFromMTP(d.vfrom_id());
handleSendActionUpdate( handleSendActionUpdate(
peerFromChannel(d.vchannel_id()), peerFromChannel(d.vchannel_id()),
d.vtop_msg_id().value_or_empty(), d.vtop_msg_id().value_or_empty(),
d.vuser_id().v, fromId,
d.vaction()); d.vaction());
} break; } break;

View file

@ -125,7 +125,7 @@ private:
void handleSendActionUpdate( void handleSendActionUpdate(
PeerId peerId, PeerId peerId,
MsgId rootId, MsgId rootId,
UserId userId, PeerId fromId,
const MTPSendMessageAction &action); const MTPSendMessageAction &action);
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
@ -168,7 +168,7 @@ private:
base::flat_map<int, ActiveChatTracker> _activeChats; base::flat_map<int, ActiveChatTracker> _activeChats;
base::flat_map< base::flat_map<
not_null<PeerData*>, not_null<PeerData*>,
base::flat_map<UserId, crl::time>> _pendingSpeakingCallMembers; base::flat_map<PeerId, crl::time>> _pendingSpeakingCallParticipants;
mtpRequestId _onlineRequest = 0; mtpRequestId _onlineRequest = 0;
base::Timer _idleFinishTimer; base::Timer _idleFinishTimer;

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "calls/calls_choose_join_as.h" #include "calls/calls_choose_join_as.h"
#include "calls/calls_group_common.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -18,8 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "boxes/peer_list_box.h" #include "boxes/peer_list_box.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_calls.h"
namespace Calls { namespace Calls::Group {
namespace { namespace {
using Context = ChooseJoinAsProcess::Context; using Context = ChooseJoinAsProcess::Context;
@ -107,9 +109,8 @@ not_null<PeerData*> ListController::selected() const {
void ChooseJoinAsBox( void ChooseJoinAsBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
Context context, Context context,
std::vector<not_null<PeerData*>> list, JoinInfo info,
not_null<PeerData*> selected, Fn<void(JoinInfo)> done) {
Fn<void(not_null<PeerData*>)> done) {
box->setTitle([&] { box->setTitle([&] {
switch (context) { switch (context) {
case Context::Create: return tr::lng_group_call_start_as_header(); case Context::Create: return tr::lng_group_call_start_as_header();
@ -121,23 +122,31 @@ void ChooseJoinAsBox(
box->addRow(object_ptr<Ui::FlatLabel>( box->addRow(object_ptr<Ui::FlatLabel>(
box, box,
tr::lng_group_call_join_as_about(), tr::lng_group_call_join_as_about(),
st::confirmPhoneAboutLabel)); (context == Context::Switch
? st::groupCallBoxLabel
: st::confirmPhoneAboutLabel)));
auto &lifetime = box->lifetime(); auto &lifetime = box->lifetime();
const auto delegate = lifetime.make_state< const auto delegate = lifetime.make_state<
PeerListContentDelegateSimple PeerListContentDelegateSimple
>(); >();
const auto controller = lifetime.make_state<ListController>( const auto controller = lifetime.make_state<ListController>(
std::move(list), info.possibleJoinAs,
selected); info.joinAs);
//controller->setStyleOverrides(); if (context == Context::Switch) {
controller->setStyleOverrides(
&st::groupCallInviteMembersList,
&st::groupCallMultiSelect);
}
const auto content = box->addRow( const auto content = box->addRow(
object_ptr<PeerListContent>(box, controller), object_ptr<PeerListContent>(box, controller),
style::margins()); style::margins());
delegate->setContent(content); delegate->setContent(content);
controller->setDelegate(delegate); controller->setDelegate(delegate);
box->addButton(tr::lng_continue(), [=] { box->addButton(tr::lng_continue(), [=] {
done(controller->selected()); auto copy = info;
copy.joinAs = controller->selected();
done(std::move(copy));
}); });
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
} }
@ -153,13 +162,16 @@ ChooseJoinAsProcess::~ChooseJoinAsProcess() {
void ChooseJoinAsProcess::start( void ChooseJoinAsProcess::start(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Context context, Context context,
Fn<void(not_null<PeerData*>, not_null<PeerData*>)> done) { Fn<void(object_ptr<Ui::BoxContent>)> showBox,
Fn<void(JoinInfo)> done,
PeerData *currentJoinAs) {
Expects(done != nullptr); Expects(done != nullptr);
const auto session = &peer->session(); const auto session = &peer->session();
if (_request) { if (_request) {
const auto already = _request->peer; const auto already = _request->peer;
_request->context = context; _request->context = context;
_request->showBox = std::move(showBox);
_request->done = std::move(done); _request->done = std::move(done);
if (already == peer) { if (already == peer) {
return; return;
@ -173,6 +185,7 @@ void ChooseJoinAsProcess::start(
_request = std::make_unique<ChannelsListRequest>( _request = std::make_unique<ChannelsListRequest>(
ChannelsListRequest{ ChannelsListRequest{
.peer = peer, .peer = peer,
.showBox = std::move(showBox),
.done = std::move(done), .done = std::move(done),
.context = context }); .context = context });
session->account().sessionChanges( session->account().sessionChanges(
@ -180,12 +193,12 @@ void ChooseJoinAsProcess::start(
_request = nullptr; _request = nullptr;
}, _request->lifetime); }, _request->lifetime);
const auto finish = [=](not_null<PeerData*> joinAs) { const auto finish = [=](JoinInfo info) {
const auto peer = _request->peer; const auto peer = _request->peer;
const auto done = std::move(_request->done); const auto done = std::move(_request->done);
const auto box = _request->box; const auto box = _request->box;
_request = nullptr; _request = nullptr;
done(peer, joinAs); done(std::move(info));
if (const auto strong = box.data()) { if (const auto strong = box.data()) {
strong->closeBox(); strong->closeBox();
} }
@ -200,8 +213,9 @@ void ChooseJoinAsProcess::start(
}); });
const auto peer = _request->peer; const auto peer = _request->peer;
const auto self = peer->session().user(); const auto self = peer->session().user();
auto info = JoinInfo{ .peer = peer, .joinAs = self };
if (chats.size() == 1) { if (chats.size() == 1) {
finish(self); finish(info);
return; return;
} }
auto list = std::vector<not_null<PeerData*>>(); auto list = std::vector<not_null<PeerData*>>();
@ -210,8 +224,8 @@ void ChooseJoinAsProcess::start(
for (const auto &chat : chats) { for (const auto &chat : chats) {
list.push_back(session->data().processChat(chat)); list.push_back(session->data().processChat(chat));
} }
const auto selectedId = peer->groupCallDefaultJoinAs(); const auto selected = [&]() -> PeerData* {
const auto selected = [&]() -> not_null<PeerData*> { const auto selectedId = peer->groupCallDefaultJoinAs();
if (!selectedId) { if (!selectedId) {
return self; return self;
} }
@ -220,22 +234,27 @@ void ChooseJoinAsProcess::start(
? not_null(loaded) ? not_null(loaded)
: self; : self;
}(); }();
_request->box = Ui::show(
Box(
ChooseJoinAsBox,
_request->context,
std::move(list),
selected,
crl::guard(&_request->guard, finish)),
Ui::LayerOption::KeepOther);
_request->box->boxClosing( info.joinAs = currentJoinAs ? currentJoinAs : selected;
info.possibleJoinAs = std::move(list);
auto box = Box(
ChooseJoinAsBox,
context,
std::move(info),
crl::guard(&_request->guard, finish));
box->boxClosing(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
_request = nullptr; _request = nullptr;
}, _request->lifetime); }, _request->lifetime);
_request->box = box.data();
_request->showBox(std::move(box));
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
finish(session->user()); finish({
.peer = _request->peer,
.joinAs = _request->peer->session().user(),
});
}).send(); }).send();
} }
} // namespace Calls } // namespace Calls::Group

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
#include "base/object_ptr.h"
class PeerData; class PeerData;
@ -15,7 +16,9 @@ namespace Ui {
class BoxContent; class BoxContent;
} // namespace Ui } // namespace Ui
namespace Calls { namespace Calls::Group {
struct JoinInfo;
class ChooseJoinAsProcess final { class ChooseJoinAsProcess final {
public: public:
@ -31,12 +34,15 @@ public:
void start( void start(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Context context, Context context,
Fn<void(not_null<PeerData*> peer, not_null<PeerData*> joinAs)> done); Fn<void(object_ptr<Ui::BoxContent>)> showBox,
Fn<void(JoinInfo)> done,
PeerData *currentJoinAs = nullptr);
private: private:
struct ChannelsListRequest { struct ChannelsListRequest {
not_null<PeerData*> peer; not_null<PeerData*> peer;
Fn<void(not_null<PeerData*>, not_null<PeerData*>)> done; Fn<void(object_ptr<Ui::BoxContent>)> showBox;
Fn<void(JoinInfo)> done;
base::has_weak_ptr guard; base::has_weak_ptr guard;
QPointer<Ui::BoxContent> box; QPointer<Ui::BoxContent> box;
rpl::lifetime lifetime; rpl::lifetime lifetime;
@ -47,4 +53,4 @@ private:
}; };
} // namespace Calls } // namespace Calls::Group

View file

@ -102,14 +102,14 @@ constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000);
GroupCall::GroupCall( GroupCall::GroupCall(
not_null<Delegate*> delegate, not_null<Delegate*> delegate,
not_null<PeerData*> peer, Group::JoinInfo info,
const MTPInputGroupCall &inputCall, const MTPInputGroupCall &inputCall)
not_null<PeerData*> joinAs)
: _delegate(delegate) : _delegate(delegate)
, _peer(peer) , _peer(info.peer)
, _history(peer->owner().history(peer)) , _history(_peer->owner().history(_peer))
, _joinAs(joinAs) , _api(&_peer->session().mtp())
, _api(&peer->session().mtp()) , _joinAs(info.joinAs)
, _possibleJoinAs(std::move(info.possibleJoinAs))
, _lastSpokeCheckTimer([=] { checkLastSpoke(); }) , _lastSpokeCheckTimer([=] { checkLastSpoke(); })
, _checkJoinedTimer([=] { checkJoined(); }) , _checkJoinedTimer([=] { checkJoined(); })
, _pushToTalkCancelTimer([=] { pushToTalkCancel(); }) , _pushToTalkCancelTimer([=] { pushToTalkCancel(); })
@ -247,7 +247,6 @@ void GroupCall::playConnectingSoundOnce() {
void GroupCall::start() { void GroupCall::start() {
_createRequestId = _api.request(MTPphone_CreateGroupCall( _createRequestId = _api.request(MTPphone_CreateGroupCall(
_peer->input, _peer->input,
_joinAs->input,
MTP_int(openssl::RandomValue<int32>()) MTP_int(openssl::RandomValue<int32>())
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
_acceptFields = true; _acceptFields = true;
@ -499,6 +498,21 @@ void GroupCall::discard() {
}).send(); }).send();
} }
void GroupCall::rejoinAs(Group::JoinInfo info) {
_possibleJoinAs = std::move(info.possibleJoinAs);
if (info.joinAs == _joinAs) {
return;
}
const auto event = Group::RejoinEvent{
.wasJoinAs = _joinAs,
.nowJoinAs = info.joinAs,
};
_joinAs = info.joinAs;
setState(State::Joining);
rejoin();
_rejoinEvents.fire_copy(event);
}
void GroupCall::finish(FinishType type) { void GroupCall::finish(FinishType type) {
Expects(type != FinishType::None); Expects(type != FinishType::None);
@ -667,15 +681,15 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
handleOtherParticipants(data); handleOtherParticipants(data);
return; return;
} }
if (data.is_left() && data.vsource().value_or_empty() == _mySsrc) { if (data.is_left() && data.vsource().v == _mySsrc) {
// I was removed from the call, rejoin. // I was removed from the call, rejoin.
LOG(("Call Info: Rejoin after got 'left' with my ssrc.")); LOG(("Call Info: Rejoin after got 'left' with my ssrc."));
setState(State::Joining); setState(State::Joining);
rejoin(); rejoin();
} else if (!data.is_left() && data.vsource().value_or_empty() != _mySsrc) { } else if (!data.is_left() && data.vsource().v != _mySsrc) {
// I joined from another device, hangup. // I joined from another device, hangup.
LOG(("Call Info: Hangup after '!left' with ssrc %1, my %2." LOG(("Call Info: Hangup after '!left' with ssrc %1, my %2."
).arg(data.vsource().value_or_empty() ).arg(data.vsource().v
).arg(_mySsrc)); ).arg(_mySsrc));
_mySsrc = 0; _mySsrc = 0;
hangup(); hangup();

View file

@ -39,6 +39,8 @@ namespace Group {
struct MuteRequest; struct MuteRequest;
struct VolumeRequest; struct VolumeRequest;
struct ParticipantState; struct ParticipantState;
struct JoinInfo;
struct RejoinEvent;
} // namespace Group } // namespace Group
enum class MuteState { enum class MuteState {
@ -88,9 +90,8 @@ public:
GroupCall( GroupCall(
not_null<Delegate*> delegate, not_null<Delegate*> delegate,
not_null<PeerData*> peer, Group::JoinInfo info,
const MTPInputGroupCall &inputCall, const MTPInputGroupCall &inputCall);
not_null<PeerData*> joinAs);
~GroupCall(); ~GroupCall();
[[nodiscard]] uint64 id() const { [[nodiscard]] uint64 id() const {
@ -102,10 +103,15 @@ public:
[[nodiscard]] not_null<PeerData*> joinAs() const { [[nodiscard]] not_null<PeerData*> joinAs() const {
return _joinAs; return _joinAs;
} }
[[nodiscard]] auto possibleJoinAs() const
-> const std::vector<not_null<PeerData*>>& {
return _possibleJoinAs;
}
void start(); void start();
void hangup(); void hangup();
void discard(); void discard();
void rejoinAs(Group::JoinInfo info);
void join(const MTPInputGroupCall &inputCall); void join(const MTPInputGroupCall &inputCall);
void handleUpdate(const MTPGroupCall &call); void handleUpdate(const MTPGroupCall &call);
void handleUpdate(const MTPDupdateGroupCallParticipants &data); void handleUpdate(const MTPDupdateGroupCallParticipants &data);
@ -142,6 +148,9 @@ public:
[[nodiscard]] rpl::producer<LevelUpdate> levelUpdates() const { [[nodiscard]] rpl::producer<LevelUpdate> levelUpdates() const {
return _levelUpdates.events(); return _levelUpdates.events();
} }
[[nodiscard]] rpl::producer<Group::RejoinEvent> rejoinEvents() const {
return _rejoinEvents.events();
}
static constexpr auto kSpeakLevelThreshold = 0.2; static constexpr auto kSpeakLevelThreshold = 0.2;
void setCurrentAudioDevice(bool input, const QString &deviceId); void setCurrentAudioDevice(bool input, const QString &deviceId);
@ -211,11 +220,13 @@ private:
const not_null<Delegate*> _delegate; const not_null<Delegate*> _delegate;
not_null<PeerData*> _peer; // Can change in legacy group migration. not_null<PeerData*> _peer; // Can change in legacy group migration.
not_null<History*> _history; // Can change in legacy group migration. not_null<History*> _history; // Can change in legacy group migration.
not_null<PeerData*> _joinAs;
MTP::Sender _api; MTP::Sender _api;
rpl::variable<State> _state = State::Creating; rpl::variable<State> _state = State::Creating;
bool _instanceConnected = false; bool _instanceConnected = false;
not_null<PeerData*> _joinAs;
std::vector<not_null<PeerData*>> _possibleJoinAs;
rpl::variable<MuteState> _muted = MuteState::Muted; rpl::variable<MuteState> _muted = MuteState::Muted;
bool _acceptFields = false; bool _acceptFields = false;
@ -230,6 +241,7 @@ private:
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance; std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;
rpl::event_stream<LevelUpdate> _levelUpdates; rpl::event_stream<LevelUpdate> _levelUpdates;
base::flat_map<uint32, Data::LastSpokeTimes> _lastSpoke; base::flat_map<uint32, Data::LastSpokeTimes> _lastSpoke;
rpl::event_stream<Group::RejoinEvent> _rejoinEvents;
base::Timer _lastSpokeCheckTimer; base::Timer _lastSpokeCheckTimer;
base::Timer _checkJoinedTimer; base::Timer _checkJoinedTimer;

View file

@ -34,4 +34,15 @@ struct ParticipantState {
bool locallyOnly = false; bool locallyOnly = false;
}; };
struct RejoinEvent {
not_null<PeerData*> wasJoinAs;
not_null<PeerData*> nowJoinAs;
};
struct JoinInfo {
not_null<PeerData*> peer;
not_null<PeerData*> joinAs;
std::vector<not_null<PeerData*>> possibleJoinAs;
};
} // namespace Calls::Group } // namespace Calls::Group

View file

@ -855,6 +855,29 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
updateRowLevel(i->second, update.value); updateRowLevel(i->second, update.value);
} }
}, _lifetime); }, _lifetime);
call->rejoinEvents(
) | rpl::start_with_next([=](const Group::RejoinEvent &event) {
const auto guard = gsl::finally([&] {
delegate()->peerListRefreshRows();
});
if (const auto row = findRow(event.wasJoinAs)) {
if (row->state() != Row::State::Invited) {
if (const auto min = _fullCountMin.current()) {
_fullCountMin = min - 1;
}
}
removeRow(row);
}
if (findRow(event.nowJoinAs)) {
return;
} else if (auto row = createRowForMe()) {
if (row->state() != Row::State::Invited) {
_fullCountMin = _fullCountMin.current() + 1;
}
delegate()->peerListAppendRow(std::move(row));
}
}, _lifetime);
} }
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) { void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {

View file

@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_group_call.h" #include "calls/calls_group_call.h"
#include "calls/calls_group_panel.h" // LeaveGroupCallBox. #include "calls/calls_group_panel.h" // LeaveGroupCallBox.
#include "calls/calls_group_common.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "calls/calls_choose_join_as.h"
#include "ui/widgets/level_meter.h" #include "ui/widgets/level_meter.h"
#include "ui/widgets/continuous_sliders.h" #include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
@ -100,6 +102,7 @@ void GroupCallSettingsBox(
float micLevel = 0.; float micLevel = 0.;
Ui::Animations::Simple micLevelAnimation; Ui::Animations::Simple micLevelAnimation;
base::Timer levelUpdateTimer; base::Timer levelUpdateTimer;
Group::ChooseJoinAsProcess joinAsProcess;
bool generatingLink = false; bool generatingLink = false;
}; };
const auto state = box->lifetime().make_state<State>(); const auto state = box->lifetime().make_state<State>();
@ -115,16 +118,40 @@ void GroupCallSettingsBox(
const auto joinMuted = goodReal ? real->joinMuted() : false; const auto joinMuted = goodReal ? real->joinMuted() : false;
const auto canChangeJoinMuted = (goodReal && real->canChangeJoinMuted()); const auto canChangeJoinMuted = (goodReal && real->canChangeJoinMuted());
const auto addCheck = (peer->canManageGroupCall() && canChangeJoinMuted); const auto addCheck = (peer->canManageGroupCall() && canChangeJoinMuted);
if (addCheck) { const auto addEditJoinAs = (call->possibleJoinAs().size() > 1);
if (addCheck || addEditJoinAs) {
AddSkip(layout); AddSkip(layout);
} }
const auto editJoinAs = addEditJoinAs
? AddButton(
layout,
tr::lng_group_call_display_as_header(),
st::groupCallSettingsButton).get()
: nullptr;
if (editJoinAs) {
editJoinAs->setClickedCallback([=] {
const auto context = Group::ChooseJoinAsProcess::Context::Switch;
const auto callback = [=](Group::JoinInfo info) {
call->rejoinAs(info);
};
auto showBox = [=](object_ptr<Ui::BoxContent> next) {
box->getDelegate()->show(std::move(next));
};
state->joinAsProcess.start(
peer,
context,
showBox,
callback,
call->joinAs());
});
}
const auto muteJoined = addCheck const auto muteJoined = addCheck
? AddButton( ? AddButton(
layout, layout,
tr::lng_group_call_new_muted(), tr::lng_group_call_new_muted(),
st::groupCallSettingsButton)->toggleOn(rpl::single(joinMuted)) st::groupCallSettingsButton)->toggleOn(rpl::single(joinMuted))
: nullptr; : nullptr;
if (addCheck) { if (addCheck || addEditJoinAs) {
AddSkip(layout); AddSkip(layout);
} }

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "calls/calls_group_common.h"
#include "mtproto/mtproto_dh_utils.h" #include "mtproto/mtproto_dh_utils.h"
#include "core/application.h" #include "core/application.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -61,27 +62,18 @@ void Instance::startOutgoingCall(not_null<UserData*> user, bool video) {
void Instance::startOrJoinGroupCall(not_null<PeerData*> peer) { void Instance::startOrJoinGroupCall(not_null<PeerData*> peer) {
const auto context = peer->groupCall() const auto context = peer->groupCall()
? ChooseJoinAsProcess::Context::Join ? Group::ChooseJoinAsProcess::Context::Join
: ChooseJoinAsProcess::Context::Create; : Group::ChooseJoinAsProcess::Context::Create;
_chooseJoinAs.start(peer, context, [=]( _chooseJoinAs.start(peer, context, [=](object_ptr<Ui::BoxContent> box) {
not_null<PeerData*> peer, Ui::show(std::move(box), Ui::LayerOption::KeepOther);
not_null<PeerData*> joinAs) { }, [=](Group::JoinInfo info) {
startOrJoinGroupCall(peer, joinAs); const auto call = info.peer->groupCall();
createGroupCall(
std::move(info),
call ? call->input() : MTP_inputGroupCall(MTPlong(), MTPlong()));
}); });
} }
void Instance::startOrJoinGroupCall(
not_null<PeerData*> peer,
not_null<PeerData*> joinAs) {
destroyCurrentCall();
const auto call = peer->groupCall();
createGroupCall(
peer,
call ? call->input() : MTP_inputGroupCall(MTPlong(), MTPlong()),
joinAs);
}
void Instance::callFinished(not_null<Call*> call) { void Instance::callFinished(not_null<Call*> call) {
crl::on_main(call, [=] { crl::on_main(call, [=] {
destroyCall(call); destroyCall(call);
@ -208,19 +200,17 @@ void Instance::destroyGroupCall(not_null<GroupCall*> call) {
} }
void Instance::createGroupCall( void Instance::createGroupCall(
not_null<PeerData*> peer, Group::JoinInfo info,
const MTPInputGroupCall &inputCall, const MTPInputGroupCall &inputCall) {
not_null<PeerData*> joinAs) {
destroyCurrentCall(); destroyCurrentCall();
auto call = std::make_unique<GroupCall>( auto call = std::make_unique<GroupCall>(
getGroupCallDelegate(), getGroupCallDelegate(),
peer, std::move(info),
inputCall, inputCall);
joinAs);
const auto raw = call.get(); const auto raw = call.get();
peer->session().account().sessionChanges( info.peer->session().account().sessionChanges(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
destroyGroupCall(raw); destroyGroupCall(raw);
}, raw->lifetime()); }, raw->lifetime());

View file

@ -16,16 +16,18 @@ namespace Platform {
enum class PermissionType; enum class PermissionType;
} // namespace Platform } // namespace Platform
namespace Media { namespace Media::Audio {
namespace Audio {
class Track; class Track;
} // namespace Audio } // namespace Media::Audio
} // namespace Media
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
namespace Calls::Group {
struct JoinInfo;
} // namespace Calls::Group
namespace Calls { namespace Calls {
class Panel; class Panel;
@ -42,9 +44,6 @@ public:
void startOutgoingCall(not_null<UserData*> user, bool video); void startOutgoingCall(not_null<UserData*> user, bool video);
void startOrJoinGroupCall(not_null<PeerData*> peer); void startOrJoinGroupCall(not_null<PeerData*> peer);
void startOrJoinGroupCall(
not_null<PeerData*> peer,
not_null<PeerData*> joinAs);
void handleUpdate( void handleUpdate(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPUpdate &update); const MTPUpdate &update);
@ -107,9 +106,8 @@ private:
void destroyCall(not_null<Call*> call); void destroyCall(not_null<Call*> call);
void createGroupCall( void createGroupCall(
not_null<PeerData*> peer, Group::JoinInfo info,
const MTPInputGroupCall &inputCall, const MTPInputGroupCall &inputCall);
not_null<PeerData*> joinAs);
void destroyGroupCall(not_null<GroupCall*> call); void destroyGroupCall(not_null<GroupCall*> call);
void requestPermissionOrFail( void requestPermissionOrFail(
@ -150,7 +148,7 @@ private:
base::flat_map<QString, std::unique_ptr<Media::Audio::Track>> _tracks; base::flat_map<QString, std::unique_ptr<Media::Audio::Track>> _tracks;
ChooseJoinAsProcess _chooseJoinAs; Group::ChooseJoinAsProcess _chooseJoinAs;
}; };

View file

@ -79,7 +79,7 @@ void GroupCall::requestParticipants() {
} }
_participantsRequestId = api().request(MTPphone_GetGroupParticipants( _participantsRequestId = api().request(MTPphone_GetGroupParticipants(
input(), input(),
MTP_vector<MTPint>(), // ids MTP_vector<MTPInputPeer>(), // ids
MTP_vector<MTPint>(), // ssrcs MTP_vector<MTPint>(), // ssrcs
MTP_string(_nextOffset), MTP_string(_nextOffset),
MTP_int(kRequestPerPage) MTP_int(kRequestPerPage)
@ -296,7 +296,7 @@ void GroupCall::applyParticipantsSlice(
.peer = participantPeer, .peer = participantPeer,
.date = data.vdate().v, .date = data.vdate().v,
.lastActive = lastActive, .lastActive = lastActive,
.ssrc = uint32(data.vsource().value_or_empty()), .ssrc = uint32(data.vsource().v),
.volume = volume, .volume = volume,
.applyVolumeFromMin = applyVolumeFromMin, .applyVolumeFromMin = applyVolumeFromMin,
.speaking = canSelfUnmute && (was ? was->speaking : false), .speaking = canSelfUnmute && (was ? was->speaking : false),
@ -469,7 +469,7 @@ void GroupCall::requestUnknownParticipants() {
} }
return result; return result;
}(); }();
const auto peerIds = [&] { const auto participantPeerIds = [&] {
if (_unknownSpokenPeerIds.size() + ssrcs.size() < kRequestPerPage) { if (_unknownSpokenPeerIds.size() + ssrcs.size() < kRequestPerPage) {
return base::take(_unknownSpokenPeerIds); return base::take(_unknownSpokenPeerIds);
} }
@ -478,8 +478,9 @@ void GroupCall::requestUnknownParticipants() {
if (available > 0) { if (available > 0) {
result.reserve(available); result.reserve(available);
while (result.size() < available) { while (result.size() < available) {
const auto [userId, when] = _unknownSpokenPeerIds.back(); const auto &back = _unknownSpokenPeerIds.back();
result.emplace(userId, when); const auto [participantPeerId, when] = back;
result.emplace(participantPeerId, when);
_unknownSpokenPeerIds.erase(_unknownSpokenPeerIds.end() - 1); _unknownSpokenPeerIds.erase(_unknownSpokenPeerIds.end() - 1);
} }
} }
@ -490,16 +491,23 @@ void GroupCall::requestUnknownParticipants() {
for (const auto [ssrc, when] : ssrcs) { for (const auto [ssrc, when] : ssrcs) {
ssrcInputs.push_back(MTP_int(ssrc)); ssrcInputs.push_back(MTP_int(ssrc));
} }
auto uidInputs = QVector<MTPint>(); auto peerInputs = QVector<MTPInputPeer>();
uidInputs.reserve(peerIds.size()); peerInputs.reserve(participantPeerIds.size());
for (const auto [peerId, when] : peerIds) { for (const auto [participantPeerId, when] : participantPeerIds) {
Assert(peerIsUser(peerId)); // #TODO calls if (const auto userId = peerToUser(participantPeerId)) {
uidInputs.push_back(MTP_int(peerToUser(peerId))); peerInputs.push_back(
MTP_inputPeerUser(MTP_int(userId), MTP_long(0)));
} else if (const auto chatId = peerToChat(participantPeerId)) {
peerInputs.push_back(MTP_inputPeerChat(MTP_int(chatId)));
} else if (const auto channelId = peerToChannel(participantPeerId)) {
peerInputs.push_back(
MTP_inputPeerChannel(MTP_int(channelId), MTP_long(0)));
}
} }
_unknownParticipantPeersRequestId = api().request( _unknownParticipantPeersRequestId = api().request(
MTPphone_GetGroupParticipants( MTPphone_GetGroupParticipants(
input(), input(),
MTP_vector<MTPint>(uidInputs), MTP_vector<MTPInputPeer>(peerInputs),
MTP_vector<MTPint>(ssrcInputs), MTP_vector<MTPint>(ssrcInputs),
MTP_string(QString()), MTP_string(QString()),
MTP_int(kRequestPerPage) MTP_int(kRequestPerPage)
@ -517,17 +525,17 @@ void GroupCall::requestUnknownParticipants() {
applyLastSpoke(ssrc, when, now); applyLastSpoke(ssrc, when, now);
_unknownSpokenSsrcs.remove(ssrc); _unknownSpokenSsrcs.remove(ssrc);
} }
for (const auto [peerId, when] : peerIds) { for (const auto [id, when] : participantPeerIds) {
if (const auto participantPeer = _peer->owner().peerLoaded(peerId)) { if (const auto participantPeer = _peer->owner().peerLoaded(id)) {
const auto isParticipant = ranges::contains( const auto isParticipant = ranges::contains(
_participants, _participants,
not_null{ participantPeer }, not_null{ participantPeer },
&Participant::peer); &Participant::peer);
if (isParticipant) { if (isParticipant) {
applyActiveUpdate(peerId, when, participantPeer); applyActiveUpdate(id, when, participantPeer);
} }
} }
_unknownSpokenPeerIds.remove(peerId); _unknownSpokenPeerIds.remove(id);
} }
requestUnknownParticipants(); requestUnknownParticipants();
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
@ -535,8 +543,8 @@ void GroupCall::requestUnknownParticipants() {
for (const auto [ssrc, when] : ssrcs) { for (const auto [ssrc, when] : ssrcs) {
_unknownSpokenSsrcs.remove(ssrc); _unknownSpokenSsrcs.remove(ssrc);
} }
for (const auto [peerId, when] : peerIds) { for (const auto [participantPeerId, when] : participantPeerIds) {
_unknownSpokenPeerIds.remove(peerId); _unknownSpokenPeerIds.remove(participantPeerId);
} }
requestUnknownParticipants(); requestUnknownParticipants();
}).send(); }).send();