Support distinct calling/invited states.

This commit is contained in:
John Preston 2025-04-06 17:21:24 +04:00
parent 55c05d1a6e
commit b036bedbc3
16 changed files with 221 additions and 105 deletions

View file

@ -4762,6 +4762,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_invite" = "Invite Members"; "lng_group_call_invite" = "Invite Members";
"lng_group_call_invite_conf" = "Add People"; "lng_group_call_invite_conf" = "Add People";
"lng_group_call_invited_status" = "invited"; "lng_group_call_invited_status" = "invited";
"lng_group_call_calling_status" = "calling...";
"lng_group_call_blockchain_only_status" = "listening"; "lng_group_call_blockchain_only_status" = "listening";
"lng_group_call_muted_by_me_status" = "muted for you"; "lng_group_call_muted_by_me_status" = "muted for you";
"lng_group_call_invite_title" = "Invite members"; "lng_group_call_invite_title" = "Invite members";

View file

@ -893,6 +893,8 @@ groupCallMemberColoredCrossLine: CrossLineAnimation(groupCallMemberInactiveCross
fg: groupCallMemberMutedIcon; fg: groupCallMemberMutedIcon;
icon: icon {{ "calls/group_calls_unmuted", groupCallMemberActiveIcon }}; icon: icon {{ "calls/group_calls_unmuted", groupCallMemberActiveIcon }};
} }
groupCallMemberCalling: icon {{ "calls/call_answer", groupCallMemberInactiveIcon }};
groupCallMemberCallingPosition: point(0px, 8px);
groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInactiveIcon }}; groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInactiveIcon }};
groupCallMemberInvitedPosition: point(2px, 12px); groupCallMemberInvitedPosition: point(2px, 12px);
groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMemberInactiveStatus }}; groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMemberInactiveStatus }};
@ -1327,6 +1329,7 @@ groupCallNarrowRaisedHand: icon {{ "calls/video_mini_speak", groupCallMemberInac
groupCallNarrowCameraIcon: icon {{ "calls/video_mini_video", groupCallMemberNotJoinedStatus }}; groupCallNarrowCameraIcon: icon {{ "calls/video_mini_video", groupCallMemberNotJoinedStatus }};
groupCallNarrowScreenIcon: icon {{ "calls/video_mini_screencast", groupCallMemberNotJoinedStatus }}; groupCallNarrowScreenIcon: icon {{ "calls/video_mini_screencast", groupCallMemberNotJoinedStatus }};
groupCallNarrowInvitedIcon: icon {{ "calls/video_mini_invited", groupCallMemberNotJoinedStatus }}; groupCallNarrowInvitedIcon: icon {{ "calls/video_mini_invited", groupCallMemberNotJoinedStatus }};
groupCallNarrowCallingIcon: icon {{ "calls/video_mini_invited", groupCallMemberNotJoinedStatus }};
groupCallNarrowIconPosition: point(-4px, 2px); groupCallNarrowIconPosition: point(-4px, 2px);
groupCallNarrowIconSkip: 15px; groupCallNarrowIconSkip: 15px;
groupCallOutline: 2px; groupCallOutline: 2px;

View file

@ -1542,7 +1542,8 @@ void Call::finish(
_user->owner().registerInvitedToCallUser( _user->owner().registerInvitedToCallUser(
migrateCall->id(), migrateCall->id(),
migrateCall, migrateCall,
_user); _user,
true);
} }
const auto session = &_user->session(); const auto session = &_user->session();
const auto weak = base::make_weak(this); const auto weak = base::make_weak(this);

View file

@ -930,7 +930,8 @@ void Instance::unregisterConferenceInvite(
CallId conferenceId, CallId conferenceId,
not_null<UserData*> user, not_null<UserData*> user,
MsgId messageId, MsgId messageId,
bool incoming) { bool incoming,
bool onlyStopCalling) {
const auto i = _conferenceInvites.find(conferenceId); const auto i = _conferenceInvites.find(conferenceId);
if (i == end(_conferenceInvites)) { if (i == end(_conferenceInvites)) {
return; return;
@ -940,9 +941,14 @@ void Instance::unregisterConferenceInvite(
return; return;
} }
auto &info = j->second; auto &info = j->second;
(incoming ? info.incoming : info.outgoing).remove(messageId); if (!(incoming ? info.incoming : info.outgoing).remove(messageId)) {
return;
}
if (!incoming) { if (!incoming) {
user->owner().unregisterInvitedToCallUser(conferenceId, user); user->owner().unregisterInvitedToCallUser(
conferenceId,
user,
onlyStopCalling);
} }
if (info.incoming.empty() && info.outgoing.empty()) { if (info.incoming.empty() && info.outgoing.empty()) {
i->second.users.erase(j); i->second.users.erase(j);
@ -1010,6 +1016,11 @@ void Instance::declineOutgoingConferenceInvite(
user->owner().history(user), user->owner().history(user),
std::move(inputs), std::move(inputs),
true); true);
for (const auto &messageId : ids) {
if (const auto item = user->owner().message(user, messageId)) {
item->destroy();
}
}
} }
if (!j->second.incoming.empty()) { if (!j->second.incoming.empty()) {
return; return;
@ -1018,7 +1029,7 @@ void Instance::declineOutgoingConferenceInvite(
if (i->second.users.empty()) { if (i->second.users.empty()) {
_conferenceInvites.erase(i); _conferenceInvites.erase(i);
} }
user->owner().unregisterInvitedToCallUser(conferenceId, user); user->owner().unregisterInvitedToCallUser(conferenceId, user, !discard);
} }
void Instance::showConferenceInvite( void Instance::showConferenceInvite(

View file

@ -131,7 +131,8 @@ public:
CallId conferenceId, CallId conferenceId,
not_null<UserData*> user, not_null<UserData*> user,
MsgId messageId, MsgId messageId,
bool incoming); bool incoming,
bool onlyStopCalling = false);
void showConferenceInvite( void showConferenceInvite(
not_null<UserData*> user, not_null<UserData*> user,
MsgId conferenceInviteMsgId); MsgId conferenceInviteMsgId);

View file

@ -1623,6 +1623,9 @@ void GroupCall::sendJoinRequest() {
if (const auto once = base::take(_migratedConferenceInfo)) { if (const auto once = base::take(_migratedConferenceInfo)) {
processMigration(*once); processMigration(*once);
} }
for (const auto &callback : base::take(_rejoinedCallbacks)) {
callback();
}
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
const auto type = error.type(); const auto type = error.type();
if (_e2e) { if (_e2e) {
@ -3775,6 +3778,45 @@ void GroupCall::editParticipant(
}).send(); }).send();
} }
void GroupCall::inviteToConference(
InviteRequest request,
Fn<not_null<InviteResult*>()> resultAddress,
Fn<void()> finishRequest) {
using Flag = MTPphone_InviteConferenceCallParticipant::Flag;
const auto user = request.user;
_api.request(MTPphone_InviteConferenceCallParticipant(
MTP_flags(request.video ? Flag::f_video : Flag()),
inputCall(),
user->inputUser
)).done([=](const MTPUpdates &result) {
const auto call = _conferenceCall.get();
user->owner().registerInvitedToCallUser(_id, call, user, true);
_peer->session().api().applyUpdates(result);
resultAddress()->invited.push_back(user);
finishRequest();
}).fail([=](const MTP::Error &error) {
const auto result = resultAddress();
const auto type = error.type();
if (type == u"USER_PRIVACY_RESTRICTED"_q) {
result->privacyRestricted.push_back(user);
} else if (type == u"USER_ALREADY_PARTICIPANT"_q) {
result->alreadyIn.push_back(user);
} else if (type == u"USER_WAS_KICKED"_q) {
result->kicked.push_back(user);
} else if (type == u"GROUPCALL_FORBIDDEN"_q) {
startRejoin();
_rejoinedCallbacks.push_back([=] {
inviteToConference(request, resultAddress, finishRequest);
});
return;
} else {
result->failed.push_back(user);
}
finishRequest();
}).send();
}
void GroupCall::inviteUsers( void GroupCall::inviteUsers(
const std::vector<InviteRequest> &requests, const std::vector<InviteRequest> &requests,
Fn<void(InviteResult)> done) { Fn<void(InviteResult)> done) {
@ -3802,32 +3844,9 @@ void GroupCall::inviteUsers(
if (const auto call = _conferenceCall.get()) { if (const auto call = _conferenceCall.get()) {
for (const auto &request : requests) { for (const auto &request : requests) {
using Flag = MTPphone_InviteConferenceCallParticipant::Flag; inviteToConference(request, [=] {
const auto user = request.user; return &state->result;
_api.request(MTPphone_InviteConferenceCallParticipant( }, finishRequest);
MTP_flags(request.video ? Flag::f_video : Flag()),
inputCallSafe(),
user->inputUser
)).done([=](const MTPUpdates &result) {
owner->registerInvitedToCallUser(_id, call, user);
_peer->session().api().applyUpdates(result);
state->result.invited.push_back(user);
finishRequest();
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
if (type == u"USER_PRIVACY_RESTRICTED"_q) {
state->result.privacyRestricted.push_back(user);
} else if (type == u"USER_ALREADY_PARTICIPANT"_q) {
state->result.alreadyIn.push_back(user);
} else if (type == u"USER_WAS_KICKED"_q) {
state->result.kicked.push_back(user);
} else if (type == u"GROUPCALL_FORBIDDEN"_q) {
startRejoin();
} else {
state->result.failed.push_back(user);
}
finishRequest();
}).send();
++state->requests; ++state->requests;
} }
return; return;
@ -3857,7 +3876,7 @@ void GroupCall::inviteUsers(
}; };
for (const auto &request : requests) { for (const auto &request : requests) {
const auto user = request.user; const auto user = request.user;
owner->registerInvitedToCallUser(_id, _peer, user); owner->registerInvitedToCallUser(_id, _peer, user, false);
usersSlice.push_back(user); usersSlice.push_back(user);
slice.push_back(user->inputUser); slice.push_back(user->inputUser);
if (slice.size() == kMaxInvitePerSlice) { if (slice.size() == kMaxInvitePerSlice) {

View file

@ -625,6 +625,10 @@ private:
void markTrackShown(const VideoEndpoint &endpoint, bool shown); void markTrackShown(const VideoEndpoint &endpoint, bool shown);
void processMigration(StartConferenceInfo conference); void processMigration(StartConferenceInfo conference);
void inviteToConference(
InviteRequest request,
Fn<not_null<InviteResult*>()> resultAddress,
Fn<void()> finishRequest);
[[nodiscard]] int activeVideoSendersCount() const; [[nodiscard]] int activeVideoSendersCount() const;
@ -645,6 +649,7 @@ private:
rpl::variable<State> _state = State::Creating; rpl::variable<State> _state = State::Creating;
base::flat_set<uint32> _unresolvedSsrcs; base::flat_set<uint32> _unresolvedSsrcs;
rpl::event_stream<Error> _errors; rpl::event_stream<Error> _errors;
std::vector<Fn<void()>> _rejoinedCallbacks;
bool _recordingStoppedByMe = false; bool _recordingStoppedByMe = false;
bool _requestedVideoChannelsUpdateScheduled = false; bool _requestedVideoChannelsUpdateScheduled = false;

View file

@ -477,15 +477,23 @@ object_ptr<Ui::BoxContent> PrepareInviteBox(
return nullptr; return nullptr;
} }
const auto peer = call->peer(); const auto peer = call->peer();
const auto conference = call->conference();
const auto weak = base::make_weak(call); const auto weak = base::make_weak(call);
auto alreadyIn = peer->owner().invitedToCallUsers(real->id()); const auto &invited = peer->owner().invitedToCallUsers(real->id());
auto alreadyIn = base::flat_set<not_null<UserData*>>();
alreadyIn.reserve(invited.size() + real->participants().size() + 1);
alreadyIn.emplace(peer->session().user());
for (const auto &participant : real->participants()) { for (const auto &participant : real->participants()) {
if (const auto user = participant.peer->asUser()) { if (const auto user = participant.peer->asUser()) {
alreadyIn.emplace(user); alreadyIn.emplace(user);
} }
} }
alreadyIn.emplace(peer->session().user()); for (const auto &[user, calling] : invited) {
if (call->conference()) { if (!conference || calling) {
alreadyIn.emplace(user);
}
}
if (conference) {
const auto close = std::make_shared<Fn<void()>>(); const auto close = std::make_shared<Fn<void()>>();
const auto shareLink = [=] { const auto shareLink = [=] {
Assert(shareConferenceLink != nullptr); Assert(shareConferenceLink != nullptr);

View file

@ -108,7 +108,8 @@ private:
[[nodiscard]] std::unique_ptr<Row> createRow( [[nodiscard]] std::unique_ptr<Row> createRow(
const Data::GroupCallParticipant &participant); const Data::GroupCallParticipant &participant);
[[nodiscard]] std::unique_ptr<Row> createInvitedRow( [[nodiscard]] std::unique_ptr<Row> createInvitedRow(
not_null<PeerData*> participantPeer); not_null<PeerData*> participantPeer,
bool calling);
[[nodiscard]] std::unique_ptr<Row> createWithAccessRow( [[nodiscard]] std::unique_ptr<Row> createWithAccessRow(
not_null<PeerData*> participantPeer); not_null<PeerData*> participantPeer);
@ -494,8 +495,9 @@ void Members::Controller::toggleVideoEndpointActive(
bool Members::Controller::appendInvitedUsers() { bool Members::Controller::appendInvitedUsers() {
auto changed = false; auto changed = false;
if (const auto id = _call->id()) { if (const auto id = _call->id()) {
for (const auto &user : _peer->owner().invitedToCallUsers(id)) { const auto &invited = _peer->owner().invitedToCallUsers(id);
if (auto row = createInvitedRow(user)) { for (const auto &[user, calling] : invited) {
if (auto row = createInvitedRow(user, calling)) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
changed = true; changed = true;
} }
@ -514,14 +516,16 @@ void Members::Controller::setupInvitedUsers() {
) | rpl::filter([=](const Invite &invite) { ) | rpl::filter([=](const Invite &invite) {
return (invite.id == _call->id()); return (invite.id == _call->id());
}) | rpl::start_with_next([=](const Invite &invite) { }) | rpl::start_with_next([=](const Invite &invite) {
const auto user = invite.user;
if (invite.removed) { if (invite.removed) {
if (const auto row = findRow(invite.user)) { if (const auto row = findRow(user)) {
if (row->state() == Row::State::Invited) { if (row->state() == Row::State::Invited
|| row->state() == Row::State::Calling) {
delegate()->peerListRemoveRow(row); delegate()->peerListRemoveRow(row);
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
} }
} }
} else if (auto row = createInvitedRow(invite.user)) { } else if (auto row = createInvitedRow(user, invite.calling)) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
} }
@ -571,7 +575,8 @@ void Members::Controller::setupWithAccessUsers() {
if (const auto count = delegate()->peerListFullRowsCount()) { if (const auto count = delegate()->peerListFullRowsCount()) {
const auto last = delegate()->peerListRowAt(count - 1); const auto last = delegate()->peerListRowAt(count - 1);
const auto state = static_cast<Row*>(last.get())->state(); const auto state = static_cast<Row*>(last.get())->state();
if (state == Row::State::Invited) { if (state == Row::State::Invited
|| state == Row::State::Calling) {
partition = true; partition = true;
} }
} }
@ -584,7 +589,8 @@ void Members::Controller::setupWithAccessUsers() {
if (partition) { if (partition) {
delegate()->peerListPartitionRows([](const PeerListRow &row) { delegate()->peerListPartitionRows([](const PeerListRow &row) {
const auto state = static_cast<const Row&>(row).state(); const auto state = static_cast<const Row&>(row).state();
return (state != Row::State::Invited); return (state != Row::State::Invited)
&& (state != Row::State::Calling);
}); });
} }
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
@ -599,6 +605,7 @@ void Members::Controller::updateRow(
auto addedToBottom = (Row*)nullptr; auto addedToBottom = (Row*)nullptr;
if (const auto row = findRow(now.peer)) { if (const auto row = findRow(now.peer)) {
if (row->state() == Row::State::Invited if (row->state() == Row::State::Invited
|| row->state() == Row::State::Calling
|| row->state() == Row::State::WithAccess) { || row->state() == Row::State::WithAccess) {
reorderIfNonRealBefore = row->absoluteIndex(); reorderIfNonRealBefore = row->absoluteIndex();
} }
@ -631,7 +638,9 @@ void Members::Controller::updateRow(
reorderIfNonRealBefore - 1).get(); reorderIfNonRealBefore - 1).get();
using State = Row::State; using State = Row::State;
const auto state = static_cast<Row*>(row)->state(); const auto state = static_cast<Row*>(row)->state();
return (state == State::Invited) || (state == State::WithAccess); return (state == State::Invited)
|| (state == State::Calling)
|| (state == State::WithAccess);
}(); }();
if (reorder) { if (reorder) {
partitionRows(); partitionRows();
@ -666,12 +675,15 @@ void Members::Controller::partitionRows() {
if (state == State::WithAccess) { if (state == State::WithAccess) {
hadWithAccess = true; hadWithAccess = true;
} }
return (state != State::Invited) && (state != State::WithAccess); return (state != State::Invited)
&& (state != State::Calling)
&& (state != State::WithAccess);
}); });
if (hadWithAccess) { if (hadWithAccess) {
delegate()->peerListPartitionRows([](const PeerListRow &row) { delegate()->peerListPartitionRows([](const PeerListRow &row) {
const auto state = static_cast<const Row&>(row).state(); const auto state = static_cast<const Row&>(row).state();
return (state != Row::State::Invited); return (state != Row::State::Invited)
&& (state != Row::State::Calling);
}); });
} }
} }
@ -811,7 +823,7 @@ void Members::Controller::updateRow(
} else if (noParticipantState == Row::State::WithAccess) { } else if (noParticipantState == Row::State::WithAccess) {
row->updateStateWithAccess(); row->updateStateWithAccess();
} else { } else {
row->updateStateInvited(); row->updateStateInvited(noParticipantState == Row::State::Calling);
} }
const auto wasNoSounding = _soundingRowBySsrc.empty(); const auto wasNoSounding = _soundingRowBySsrc.empty();
@ -1093,15 +1105,21 @@ void Members::Controller::rowPaintIcon(
return; return;
} }
const auto narrow = (state.style == MembersRowStyle::Narrow); const auto narrow = (state.style == MembersRowStyle::Narrow);
if (state.invited) { if (state.invited || state.calling) {
if (narrow) { if (narrow) {
st::groupCallNarrowInvitedIcon.paintInCenter(p, rect); (state.invited
? st::groupCallNarrowInvitedIcon
: st::groupCallNarrowCallingIcon).paintInCenter(p, rect);
} else { } else {
st::groupCallMemberInvited.paintInCenter( const auto &icon = state.invited
? st::groupCallMemberInvited
: st::groupCallMemberCalling;
const auto shift = state.invited
? st::groupCallMemberInvitedPosition
: st::groupCallMemberCallingPosition;
icon.paintInCenter(
p, p,
QRect( QRect(rect.topLeft() + shift, icon.size()));
rect.topLeft() + st::groupCallMemberInvitedPosition,
st::groupCallMemberInvited.size()));
} }
return; return;
} }
@ -1464,9 +1482,10 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
} }
} else { } else {
const auto conference = _call->conferenceCall().get(); const auto conference = _call->conferenceCall().get();
if (muteState == Row::State::Invited if (conference
&& participantPeer->isUser() && participantPeer->isUser()
&& conference) { && (muteState == Row::State::Invited
|| muteState == Row::State::Calling)) {
const auto id = conference->id(); const auto id = conference->id();
const auto cancelInvite = [=](bool discard) { const auto cancelInvite = [=](bool discard) {
Core::App().calls().declineOutgoingConferenceInvite( Core::App().calls().declineOutgoingConferenceInvite(
@ -1474,9 +1493,11 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
participantPeer->asUser(), participantPeer->asUser(),
discard); discard);
}; };
result->addAction( if (muteState == Row::State::Calling) {
tr::lng_group_call_context_stop_ringing(tr::now), result->addAction(
[=] { cancelInvite(false); }); tr::lng_group_call_context_stop_ringing(tr::now),
[=] { cancelInvite(false); });
}
result->addAction( result->addAction(
tr::lng_group_call_context_cancel_invite(tr::now), tr::lng_group_call_context_cancel_invite(tr::now),
[=] { cancelInvite(true); }); [=] { cancelInvite(true); });
@ -1497,6 +1518,7 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
const auto canKick = [&] { const auto canKick = [&] {
const auto user = participantPeer->asUser(); const auto user = participantPeer->asUser();
if (muteState == Row::State::Invited if (muteState == Row::State::Invited
|| muteState == Row::State::Calling
|| muteState == Row::State::WithAccess) { || muteState == Row::State::WithAccess) {
return false; return false;
} else if (const auto chat = _peer->asChat()) { } else if (const auto chat = _peer->asChat()) {
@ -1626,6 +1648,7 @@ void Members::Controller::addMuteActionsToContextMenu(
const auto muteAction = [&]() -> QAction* { const auto muteAction = [&]() -> QAction* {
if (muteState == Row::State::Invited if (muteState == Row::State::Invited
|| muteState == Row::State::Calling
|| muteState == Row::State::WithAccess || muteState == Row::State::WithAccess
|| _call->rtmp() || _call->rtmp()
|| isMe(participantPeer) || isMe(participantPeer)
@ -1681,12 +1704,19 @@ std::unique_ptr<Row> Members::Controller::createRow(
} }
std::unique_ptr<Row> Members::Controller::createInvitedRow( std::unique_ptr<Row> Members::Controller::createInvitedRow(
not_null<PeerData*> participantPeer) { not_null<PeerData*> participantPeer,
if (findRow(participantPeer)) { bool calling) {
if (const auto row = findRow(participantPeer)) {
if (row->state() == Row::State::Invited
|| row->state() == Row::State::Calling) {
row->updateStateInvited(calling);
delegate()->peerListUpdateRow(row);
}
return nullptr; return nullptr;
} }
const auto state = calling ? Row::State::Calling : Row::State::Invited;
auto result = std::make_unique<Row>(this, participantPeer); auto result = std::make_unique<Row>(this, participantPeer);
updateRow(result.get(), std::nullopt, nullptr); updateRow(result.get(), std::nullopt, nullptr, state);
return result; return result;
} }

View file

@ -138,9 +138,9 @@ void MembersRow::setSkipLevelUpdate(bool value) {
_skipLevelUpdate = value; _skipLevelUpdate = value;
} }
void MembersRow::updateStateInvited() { void MembersRow::updateStateInvited(bool calling) {
setVolume(Group::kDefaultVolume); setVolume(Group::kDefaultVolume);
setState(State::Invited); setState(calling ? State::Calling : State::Invited);
setSounding(false); setSounding(false);
setSpeaking(false); setSpeaking(false);
_mutedByMe = false; _mutedByMe = false;
@ -640,11 +640,13 @@ void MembersRow::paintComplexStatusText(
const auto useAbout = !_about.isEmpty() const auto useAbout = !_about.isEmpty()
&& (_state != State::WithAccess) && (_state != State::WithAccess)
&& (_state != State::Invited) && (_state != State::Invited)
&& (_state != State::Calling)
&& (style != MembersRowStyle::Video) && (style != MembersRowStyle::Video)
&& ((_state == State::RaisedHand && !_raisedHandStatus) && ((_state == State::RaisedHand && !_raisedHandStatus)
|| (_state != State::RaisedHand && !_speaking)); || (_state != State::RaisedHand && !_speaking));
if (!useAbout if (!useAbout
&& _state != State::Invited && _state != State::Invited
&& _state != State::Calling
&& _state != State::WithAccess && _state != State::WithAccess
&& !_mutedByMe) { && !_mutedByMe) {
paintStatusIcon(p, x, y, st, font, selected, narrowMode); paintStatusIcon(p, x, y, st, font, selected, narrowMode);
@ -693,6 +695,8 @@ void MembersRow::paintComplexStatusText(
? tr::lng_status_connecting(tr::now) ? tr::lng_status_connecting(tr::now)
: (_state == State::WithAccess) : (_state == State::WithAccess)
? tr::lng_group_call_blockchain_only_status(tr::now) ? tr::lng_group_call_blockchain_only_status(tr::now)
: (_state == State::Calling)
? tr::lng_group_call_calling_status(tr::now)
: tr::lng_group_call_invited_status(tr::now))); : tr::lng_group_call_invited_status(tr::now)));
} }
} }
@ -706,6 +710,7 @@ QSize MembersRow::rightActionSize() const {
bool MembersRow::rightActionDisabled() const { bool MembersRow::rightActionDisabled() const {
return _delegate->rowIsMe(peer()) return _delegate->rowIsMe(peer())
|| (_state == State::Invited) || (_state == State::Invited)
|| (_state == State::Calling)
|| !_delegate->rowCanMuteMembers(); || !_delegate->rowCanMuteMembers();
} }
@ -731,7 +736,9 @@ void MembersRow::rightActionPaint(
size.width(), size.width(),
size.height(), size.height(),
outerWidth); outerWidth);
if (_state == State::Invited) { if (_state == State::Invited
|| _state == State::Calling
|| _state == State::WithAccess) {
_actionRipple = nullptr; _actionRipple = nullptr;
} }
if (_actionRipple) { if (_actionRipple) {
@ -761,6 +768,7 @@ MembersRowDelegate::IconState MembersRow::computeIconState(
.mutedByMe = _mutedByMe, .mutedByMe = _mutedByMe,
.raisedHand = (_state == State::RaisedHand), .raisedHand = (_state == State::RaisedHand),
.invited = (_state == State::Invited), .invited = (_state == State::Invited),
.calling = (_state == State::Calling),
.style = style, .style = style,
}; };
} }

View file

@ -24,7 +24,7 @@ struct PeerUserpicView;
namespace Calls::Group { namespace Calls::Group {
enum class MembersRowStyle { enum class MembersRowStyle : uchar {
Default, Default,
Narrow, Narrow,
Video, Video,
@ -40,6 +40,7 @@ public:
bool mutedByMe = false; bool mutedByMe = false;
bool raisedHand = false; bool raisedHand = false;
bool invited = false; bool invited = false;
bool calling = false;
MembersRowStyle style = MembersRowStyle::Default; MembersRowStyle style = MembersRowStyle::Default;
}; };
virtual bool rowIsMe(not_null<PeerData*> participantPeer) = 0; virtual bool rowIsMe(not_null<PeerData*> participantPeer) = 0;
@ -75,13 +76,14 @@ public:
Muted, Muted,
RaisedHand, RaisedHand,
Invited, Invited,
Calling,
WithAccess, WithAccess,
}; };
void setAbout(const QString &about); void setAbout(const QString &about);
void setSkipLevelUpdate(bool value); void setSkipLevelUpdate(bool value);
void updateState(const Data::GroupCallParticipant &participant); void updateState(const Data::GroupCallParticipant &participant);
void updateStateInvited(); void updateStateInvited(bool calling);
void updateStateWithAccess(); void updateStateWithAccess();
void updateLevel(float level); void updateLevel(float level);
void updateBlobAnimation(crl::time now); void updateBlobAnimation(crl::time now);

View file

@ -791,7 +791,10 @@ void GroupCall::applyParticipantsSlice(
} }
if (adding) { if (adding) {
if (const auto user = participantPeer->asUser()) { if (const auto user = participantPeer->asUser()) {
_peer->owner().unregisterInvitedToCallUser(_id, user); _peer->owner().unregisterInvitedToCallUser(
_id,
user,
false);
} }
} }
}); });

View file

@ -1701,13 +1701,11 @@ MediaCall::MediaCall(not_null<HistoryItem*> parent, const Call &call)
const auto peer = parent->history()->peer; const auto peer = parent->history()->peer;
peer->owner().registerCallItem(parent); peer->owner().registerCallItem(parent);
if (const auto user = _call.conferenceId ? peer->asUser() : nullptr) { if (const auto user = _call.conferenceId ? peer->asUser() : nullptr) {
if (_call.state == CallState::Invitation) { Core::App().calls().registerConferenceInvite(
Core::App().calls().registerConferenceInvite( _call.conferenceId,
_call.conferenceId, user,
user, parent->id,
parent->id, !parent->out());
!parent->out());
}
} }
} }
@ -1716,13 +1714,11 @@ MediaCall::~MediaCall() {
const auto peer = parent->history()->peer; const auto peer = parent->history()->peer;
peer->owner().unregisterCallItem(parent); peer->owner().unregisterCallItem(parent);
if (const auto user = _call.conferenceId ? peer->asUser() : nullptr) { if (const auto user = _call.conferenceId ? peer->asUser() : nullptr) {
if (_call.state == CallState::Invitation) { Core::App().calls().unregisterConferenceInvite(
Core::App().calls().unregisterConferenceInvite( _call.conferenceId,
_call.conferenceId, user,
user, parent->id,
parent->id, !parent->out());
!parent->out());
}
} }
} }

View file

@ -1219,8 +1219,8 @@ void Session::checkLocalUsersWentOffline() {
} }
auto Session::invitedToCallUsers(CallId callId) const auto Session::invitedToCallUsers(CallId callId) const
-> const base::flat_set<not_null<UserData*>> & { -> const base::flat_map<not_null<UserData*>, bool> & {
static const base::flat_set<not_null<UserData*>> kEmpty; static const base::flat_map<not_null<UserData*>, bool> kEmpty;
const auto i = _invitedToCallUsers.find(callId); const auto i = _invitedToCallUsers.find(callId);
return (i != _invitedToCallUsers.end()) ? i->second : kEmpty; return (i != _invitedToCallUsers.end()) ? i->second : kEmpty;
} }
@ -1228,14 +1228,16 @@ auto Session::invitedToCallUsers(CallId callId) const
void Session::registerInvitedToCallUser( void Session::registerInvitedToCallUser(
CallId callId, CallId callId,
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> user) { not_null<UserData*> user,
registerInvitedToCallUser(callId, peer->groupCall(), user); bool calling) {
registerInvitedToCallUser(callId, peer->groupCall(), user, calling);
} }
void Session::registerInvitedToCallUser( void Session::registerInvitedToCallUser(
CallId callId, CallId callId,
GroupCall *call, GroupCall *call,
not_null<UserData*> user) { not_null<UserData*> user,
bool calling) {
if (call && call->id() == callId) { if (call && call->id() == callId) {
const auto inCall = ranges::contains( const auto inCall = ranges::contains(
call->participants(), call->participants(),
@ -1245,19 +1247,32 @@ void Session::registerInvitedToCallUser(
return; return;
} }
} }
_invitedToCallUsers[callId].emplace(user); _invitedToCallUsers[callId][user] = calling;
_invitesToCalls.fire({ callId, user }); _invitesToCalls.fire({ callId, user, calling });
} }
void Session::unregisterInvitedToCallUser( void Session::unregisterInvitedToCallUser(
CallId callId, CallId callId,
not_null<UserData*> user) { not_null<UserData*> user,
bool onlyStopCalling) {
const auto i = _invitedToCallUsers.find(callId); const auto i = _invitedToCallUsers.find(callId);
if (i != _invitedToCallUsers.end()) { if (i != _invitedToCallUsers.end()) {
i->second.remove(user); const auto j = i->second.find(user);
if (i->second.empty()) { if (j != end(i->second)) {
_invitedToCallUsers.erase(i); if (onlyStopCalling) {
_invitesToCalls.fire({ callId, user, true }); if (!j->second) {
return;
}
j->second = false;
} else {
i->second.erase(j);
if (i->second.empty()) {
_invitedToCallUsers.erase(i);
}
}
const auto calling = false;
const auto removed = !onlyStopCalling;
_invitesToCalls.fire({ callId, user, calling, removed });
} }
} }
} }

View file

@ -240,22 +240,26 @@ public:
void maybeStopWatchForOffline(not_null<UserData*> user); void maybeStopWatchForOffline(not_null<UserData*> user);
[[nodiscard]] auto invitedToCallUsers(CallId callId) const [[nodiscard]] auto invitedToCallUsers(CallId callId) const
-> const base::flat_set<not_null<UserData*>> &; -> const base::flat_map<not_null<UserData*>, bool> &;
void registerInvitedToCallUser( void registerInvitedToCallUser(
CallId callId, CallId callId,
not_null<PeerData*> peer, not_null<PeerData*> peer,
not_null<UserData*> user); not_null<UserData*> user,
bool calling);
void registerInvitedToCallUser( void registerInvitedToCallUser(
CallId callId, CallId callId,
GroupCall *call, GroupCall *call,
not_null<UserData*> user); not_null<UserData*> user,
bool calling);
void unregisterInvitedToCallUser( void unregisterInvitedToCallUser(
CallId callId, CallId callId,
not_null<UserData*> user); not_null<UserData*> user,
bool onlyStopCalling);
struct InviteToCall { struct InviteToCall {
CallId id = 0; CallId id = 0;
not_null<UserData*> user; not_null<UserData*> user;
bool calling = false;
bool removed = false; bool removed = false;
}; };
[[nodiscard]] rpl::producer<InviteToCall> invitesToCalls() const { [[nodiscard]] rpl::producer<InviteToCall> invitesToCalls() const {
@ -1112,7 +1116,7 @@ private:
rpl::event_stream<InviteToCall> _invitesToCalls; rpl::event_stream<InviteToCall> _invitesToCalls;
base::flat_map< base::flat_map<
CallId, CallId,
base::flat_set<not_null<UserData*>>> _invitedToCallUsers; base::flat_map<not_null<UserData*>, bool>> _invitedToCallUsers;
base::flat_set<not_null<ViewElement*>> _shownSpoilers; base::flat_set<not_null<ViewElement*>> _shownSpoilers;
base::flat_map< base::flat_map<

View file

@ -1902,12 +1902,21 @@ void HistoryItem::applyEdition(const MTPDmessageService &message) {
_flags &= ~MessageFlag::DisplayFromChecked; _flags &= ~MessageFlag::DisplayFromChecked;
} else if (message.vaction().type() == mtpc_messageActionConferenceCall) { } else if (message.vaction().type() == mtpc_messageActionConferenceCall) {
removeFromSharedMediaIndex(); removeFromSharedMediaIndex();
const auto owner = &history()->owner();
const auto &data = message.vaction().c_messageActionConferenceCall();
const auto info = Data::ComputeCallData(owner, data);
if (const auto user = history()->peer->asUser()) {
if (const auto conferenceId = out() ? info.conferenceId : 0) {
Core::App().calls().unregisterConferenceInvite(
conferenceId,
user,
id,
!out(),
true);
}
}
_media = nullptr; _media = nullptr;
_media = std::make_unique<Data::MediaCall>( _media = std::make_unique<Data::MediaCall>(this, info);
this,
Data::ComputeCallData(
&history()->owner(),
message.vaction().c_messageActionConferenceCall()));
addToSharedMediaIndex(); addToSharedMediaIndex();
finishEdition(-1); finishEdition(-1);
_flags &= ~MessageFlag::DisplayFromChecked; _flags &= ~MessageFlag::DisplayFromChecked;
@ -4896,7 +4905,7 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
for (const auto &id : action.vusers().v) { for (const auto &id : action.vusers().v) {
const auto user = owner->user(id.v); const auto user = owner->user(id.v);
if (callId) { if (callId) {
owner->registerInvitedToCallUser(callId, peer, user); owner->registerInvitedToCallUser(callId, peer, user, false);
} }
}; };
const auto linkCallId = PeerHasThisCall(peer, callId).value_or(false) const auto linkCallId = PeerHasThisCall(peer, callId).value_or(false)