Implement incoming confcall window.

This commit is contained in:
John Preston 2025-03-31 11:44:39 +04:00
parent 7c709fddba
commit a3ba99c682
11 changed files with 327 additions and 25 deletions

View file

@ -247,7 +247,49 @@ Call::Call(
setupOutgoingVideo(); setupOutgoingVideo();
} }
Call::Call(
not_null<Delegate*> delegate,
not_null<UserData*> user,
CallId conferenceId,
MsgId conferenceInviteMsgId)
: _delegate(delegate)
, _user(user)
, _api(&_user->session().mtp())
, _type(Type::Incoming)
, _state(State::WaitingIncoming)
, _discardByTimeoutTimer([=] { hangup(); })
, _playbackDeviceId(
&Core::App().mediaDevices(),
Webrtc::DeviceType::Playback,
Webrtc::DeviceIdValueWithFallback(
Core::App().settings().callPlaybackDeviceIdValue(),
Core::App().settings().playbackDeviceIdValue()))
, _captureDeviceId(
&Core::App().mediaDevices(),
Webrtc::DeviceType::Capture,
Webrtc::DeviceIdValueWithFallback(
Core::App().settings().callCaptureDeviceIdValue(),
Core::App().settings().captureDeviceIdValue()))
, _cameraDeviceId(
&Core::App().mediaDevices(),
Webrtc::DeviceType::Camera,
Core::App().settings().cameraDeviceIdValue())
, _id(base::RandomValue<CallId>())
, _conferenceId(conferenceId)
, _conferenceInviteMsgId(conferenceInviteMsgId)
, _videoIncoming(
std::make_unique<Webrtc::VideoTrack>(
StartVideoState(false)))
, _videoOutgoing(
std::make_unique<Webrtc::VideoTrack>(
StartVideoState(false))) {
startWaitingTrack();
setupOutgoingVideo();
}
void Call::generateModExpFirst(bytes::const_span randomSeed) { void Call::generateModExpFirst(bytes::const_span randomSeed) {
Expects(!conferenceInvite());
auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed); auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed);
if (first.modexp.empty()) { if (first.modexp.empty()) {
LOG(("Call Error: Could not compute mod-exp first.")); LOG(("Call Error: Could not compute mod-exp first."));
@ -273,6 +315,8 @@ bool Call::isIncomingWaiting() const {
} }
void Call::start(bytes::const_span random) { void Call::start(bytes::const_span random) {
Expects(!conferenceInvite());
// Save config here, because it is possible that it changes between // Save config here, because it is possible that it changes between
// different usages inside the same call. // different usages inside the same call.
_dhConfig = _delegate->getDhConfig(); _dhConfig = _delegate->getDhConfig();
@ -297,6 +341,7 @@ void Call::startOutgoing() {
Expects(_type == Type::Outgoing); Expects(_type == Type::Outgoing);
Expects(_state.current() == State::Requesting); Expects(_state.current() == State::Requesting);
Expects(_gaHash.size() == kSha256Size); Expects(_gaHash.size() == kSha256Size);
Expects(!conferenceInvite());
const auto flags = _videoCapture const auto flags = _videoCapture
? MTPphone_RequestCall::Flag::f_video ? MTPphone_RequestCall::Flag::f_video
@ -350,6 +395,7 @@ void Call::startOutgoing() {
void Call::startIncoming() { void Call::startIncoming() {
Expects(_type == Type::Incoming); Expects(_type == Type::Incoming);
Expects(_state.current() == State::Starting); Expects(_state.current() == State::Starting);
Expects(!conferenceInvite());
_api.request(MTPphone_ReceivedCall( _api.request(MTPphone_ReceivedCall(
MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)) MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash))
@ -363,6 +409,8 @@ void Call::startIncoming() {
} }
void Call::applyUserConfirmation() { void Call::applyUserConfirmation() {
Expects(!conferenceInvite());
if (_state.current() == State::WaitingUserConfirmation) { if (_state.current() == State::WaitingUserConfirmation) {
setState(State::Requesting); setState(State::Requesting);
} }
@ -375,9 +423,44 @@ void Call::answer() {
}), video); }), video);
} }
void Call::acceptConferenceInvite() {
Expects(conferenceInvite());
if (_state.current() != State::WaitingIncoming) {
return;
}
setState(State::ExchangingKeys);
const auto limit = 5;
const auto messageId = _conferenceInviteMsgId;
const auto session = &_user->session();
session->api().request(MTPphone_GetGroupCall(
MTP_inputGroupCallInviteMessage(MTP_int(messageId.bare)),
MTP_int(limit)
)).done([session, messageId](const MTPphone_GroupCall &result) {
result.data().vcall().match([&](const auto &data) {
const auto call = session->data().sharedConferenceCall(
data.vid().v,
data.vaccess_hash().v);
call->processFullCall(result);
Core::App().calls().startOrJoinConferenceCall({
.call = call,
.joinMessageId = messageId,
.migrating = true,
});
});
}).fail(crl::guard(this, [=](const MTP::Error &error) {
handleRequestError(error.type());
})).send();
}
void Call::actuallyAnswer() { void Call::actuallyAnswer() {
Expects(_type == Type::Incoming); Expects(_type == Type::Incoming);
if (conferenceInvite()) {
acceptConferenceInvite();
return;
}
const auto state = _state.current(); const auto state = _state.current();
if (state != State::Starting && state != State::WaitingIncoming) { if (state != State::Starting && state != State::WaitingIncoming) {
if (state != State::ExchangingKeys if (state != State::ExchangingKeys
@ -435,6 +518,8 @@ void Call::setMuted(bool mute) {
} }
void Call::setupMediaDevices() { void Call::setupMediaDevices() {
Expects(!conferenceInvite());
_playbackDeviceId.changes() | rpl::filter([=] { _playbackDeviceId.changes() | rpl::filter([=] {
return _instance && _setDeviceIdCallback; return _instance && _setDeviceIdCallback;
}) | rpl::start_with_next([=](const Webrtc::DeviceResolvedId &deviceId) { }) | rpl::start_with_next([=](const Webrtc::DeviceResolvedId &deviceId) {
@ -530,7 +615,8 @@ crl::time Call::getDurationMs() const {
void Call::hangup(Data::GroupCall *migrateCall, const QString &migrateSlug) { void Call::hangup(Data::GroupCall *migrateCall, const QString &migrateSlug) {
const auto state = _state.current(); const auto state = _state.current();
if (state == State::Busy || state == State::MigrationHangingUp) { if (state == State::Busy
|| state == State::MigrationHangingUp) {
_delegate->callFinished(this); _delegate->callFinished(this);
} else { } else {
const auto missed = (state == State::Ringing const auto missed = (state == State::Ringing
@ -549,6 +635,8 @@ void Call::hangup(Data::GroupCall *migrateCall, const QString &migrateSlug) {
} }
void Call::redial() { void Call::redial() {
Expects(!conferenceInvite());
if (_state.current() != State::Busy) { if (_state.current() != State::Busy) {
return; return;
} }
@ -578,6 +666,8 @@ void Call::startWaitingTrack() {
} }
void Call::sendSignalingData(const QByteArray &data) { void Call::sendSignalingData(const QByteArray &data) {
Expects(!conferenceInvite());
_api.request(MTPphone_SendSignalingData( _api.request(MTPphone_SendSignalingData(
MTP_inputPhoneCall( MTP_inputPhoneCall(
MTP_long(_id), MTP_long(_id),
@ -776,6 +866,8 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
} }
void Call::finishByMigration(const QString &slug) { void Call::finishByMigration(const QString &slug) {
Expects(!conferenceInvite());
if (_state.current() == State::MigrationHangingUp) { if (_state.current() == State::MigrationHangingUp) {
return; return;
} }
@ -841,6 +933,7 @@ bool Call::handleSignalingData(
void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) { void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
Expects(_type == Type::Outgoing); Expects(_type == Type::Outgoing);
Expects(!conferenceInvite());
if (_state.current() == State::ExchangingKeys if (_state.current() == State::ExchangingKeys
|| _instance) { || _instance) {
@ -893,6 +986,7 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
void Call::startConfirmedCall(const MTPDphoneCall &call) { void Call::startConfirmedCall(const MTPDphoneCall &call) {
Expects(_type == Type::Incoming); Expects(_type == Type::Incoming);
Expects(!conferenceInvite());
const auto firstBytes = bytes::make_span(call.vg_a_or_b().v); const auto firstBytes = bytes::make_span(call.vg_a_or_b().v);
if (_gaHash != openssl::Sha256(firstBytes)) { if (_gaHash != openssl::Sha256(firstBytes)) {
@ -919,6 +1013,8 @@ void Call::startConfirmedCall(const MTPDphoneCall &call) {
} }
void Call::createAndStartController(const MTPDphoneCall &call) { void Call::createAndStartController(const MTPDphoneCall &call) {
Expects(!conferenceInvite());
_discardByTimeoutTimer.cancel(); _discardByTimeoutTimer.cancel();
if (!checkCallFields(call) || _authKey.size() != kAuthKeySize) { if (!checkCallFields(call) || _authKey.size() != kAuthKeySize) {
return; return;
@ -1116,6 +1212,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
} }
void Call::handleControllerStateChange(tgcalls::State state) { void Call::handleControllerStateChange(tgcalls::State state) {
Expects(!conferenceInvite());
switch (state) { switch (state) {
case tgcalls::State::WaitInit: { case tgcalls::State::WaitInit: {
DEBUG_LOG(("Call Info: State changed to WaitingInit.")); DEBUG_LOG(("Call Info: State changed to WaitingInit."));
@ -1390,8 +1488,11 @@ void Call::finish(
|| state == State::Ended || state == State::Ended
|| state == State::Failed) { || state == State::Failed) {
return; return;
} } else if (conferenceInvite()) {
if (!_id) { Core::App().calls().declineIncomingConferenceInvites(_conferenceId);
setState(finalState);
return;
} else if (!_id) {
setState(finalState); setState(finalState);
return; return;
} }
@ -1460,7 +1561,7 @@ void Call::handleRequestError(const QString &error) {
? Lang::Hard::CallErrorIncompatible().replace( ? Lang::Hard::CallErrorIncompatible().replace(
"{user}", "{user}",
_user->name()) _user->name())
: QString(); : error;
if (!inform.isEmpty()) { if (!inform.isEmpty()) {
if (const auto window = Core::App().windowFor( if (const auto window = Core::App().windowFor(
Window::SeparateId(_user))) { Window::SeparateId(_user))) {

View file

@ -102,6 +102,11 @@ public:
not_null<UserData*> user, not_null<UserData*> user,
Type type, Type type,
bool video); bool video);
Call(
not_null<Delegate*> delegate,
not_null<UserData*> user,
CallId conferenceId,
MsgId conferenceInviteMsgId);
[[nodiscard]] Type type() const { [[nodiscard]] Type type() const {
return _type; return _type;
@ -112,6 +117,15 @@ public:
[[nodiscard]] CallId id() const { [[nodiscard]] CallId id() const {
return _id; return _id;
} }
[[nodiscard]] bool conferenceInvite() const {
return _conferenceId != 0;
}
[[nodiscard]] CallId conferenceId() const {
return _conferenceId;
}
[[nodiscard]] MsgId conferenceInviteMsgId() const {
return _conferenceInviteMsgId;
}
[[nodiscard]] bool isIncomingWaiting() const; [[nodiscard]] bool isIncomingWaiting() const;
void start(bytes::const_span random); void start(bytes::const_span random);
@ -272,6 +286,7 @@ private:
bool checkCallFields(const MTPDphoneCallAccepted &call); bool checkCallFields(const MTPDphoneCallAccepted &call);
void actuallyAnswer(); void actuallyAnswer();
void acceptConferenceInvite();
void confirmAcceptedCall(const MTPDphoneCallAccepted &call); void confirmAcceptedCall(const MTPDphoneCallAccepted &call);
void startConfirmedCall(const MTPDphoneCall &call); void startConfirmedCall(const MTPDphoneCall &call);
void setState(State state); void setState(State state);
@ -325,6 +340,9 @@ private:
uint64 _accessHash = 0; uint64 _accessHash = 0;
uint64 _keyFingerprint = 0; uint64 _keyFingerprint = 0;
CallId _conferenceId = 0;
MsgId _conferenceInviteMsgId = 0;
std::unique_ptr<tgcalls::Instance> _instance; std::unique_ptr<tgcalls::Instance> _instance;
std::shared_ptr<tgcalls::VideoCaptureInterface> _videoCapture; std::shared_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
QString _videoCaptureDeviceId; QString _videoCaptureDeviceId;

View file

@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/group/calls_choose_join_as.h" #include "calls/group/calls_choose_join_as.h"
#include "calls/group/calls_group_call.h" #include "calls/group/calls_group_call.h"
#include "calls/group/calls_group_rtmp.h" #include "calls/group/calls_group_rtmp.h"
#include "history/history.h"
#include "history/history_item.h"
#include "mtproto/mtproto_dh_utils.h" #include "mtproto/mtproto_dh_utils.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
@ -26,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_panel.h" #include "calls/calls_panel.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_group_call.h" #include "data/data_group_call.h"
#include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -103,6 +106,8 @@ void Instance::Delegate::callFailed(not_null<Call*> call) {
} }
void Instance::Delegate::callRedial(not_null<Call*> call) { void Instance::Delegate::callRedial(not_null<Call*> call) {
Expects(!call->conferenceInvite());
if (_instance->_currentCall.get() == call) { if (_instance->_currentCall.get() == call) {
_instance->refreshDhConfig(); _instance->refreshDhConfig();
} }
@ -256,7 +261,7 @@ void Instance::startOrJoinConferenceCall(StartConferenceCallArgs args) {
_currentGroupCallChanges.fire_copy(raw); _currentGroupCallChanges.fire_copy(raw);
if (!args.invite.empty()) { if (!args.invite.empty()) {
_currentGroupCallPanel->migrationInviteUsers(std::move(args.invite)); _currentGroupCallPanel->migrationInviteUsers(std::move(args.invite));
} else if (args.migrating) { } else if (args.migrating && !args.linkSlug.isEmpty()) {
_currentGroupCallPanel->migrationShowShareLink(); _currentGroupCallPanel->migrationShowShareLink();
} }
} }
@ -442,6 +447,7 @@ void Instance::createGroupCall(
void Instance::refreshDhConfig() { void Instance::refreshDhConfig() {
Expects(_currentCall != nullptr); Expects(_currentCall != nullptr);
Expects(!_currentCall->conferenceInvite());
const auto weak = base::make_weak(_currentCall); const auto weak = base::make_weak(_currentCall);
_currentCall->user()->session().api().request(MTPmessages_GetDhConfig( _currentCall->user()->session().api().request(MTPmessages_GetDhConfig(
@ -899,4 +905,129 @@ std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::getVideoCapture(
return result; return result;
} }
const ConferenceInvites &Instance::conferenceInvites(
CallId conferenceId) const {
static const auto kEmpty = ConferenceInvites();
const auto i = _conferenceInvites.find(conferenceId);
return (i != end(_conferenceInvites)) ? i->second : kEmpty;
}
void Instance::registerConferenceInvite(
CallId conferenceId,
not_null<UserData*> user,
MsgId messageId,
bool incoming) {
auto &info = _conferenceInvites[conferenceId].users[user];
(incoming ? info.incoming : info.outgoing).emplace(messageId);
}
void Instance::unregisterConferenceInvite(
CallId conferenceId,
not_null<UserData*> user,
MsgId messageId,
bool incoming) {
const auto i = _conferenceInvites.find(conferenceId);
if (i == end(_conferenceInvites)) {
return;
}
const auto j = i->second.users.find(user);
if (j == end(i->second.users)) {
return;
}
auto &info = j->second;
(incoming ? info.incoming : info.outgoing).remove(messageId);
if (!incoming) {
user->owner().unregisterInvitedToCallUser(conferenceId, user);
}
if (info.incoming.empty() && info.outgoing.empty()) {
i->second.users.erase(j);
if (i->second.users.empty()) {
_conferenceInvites.erase(i);
}
}
if (_currentCall
&& _currentCall->user() == user
&& _currentCall->conferenceInviteMsgId() == messageId
&& _currentCall->state() == Call::State::WaitingIncoming) {
destroyCurrentCall();
}
}
void Instance::declineIncomingConferenceInvites(CallId conferenceId) {
const auto i = _conferenceInvites.find(conferenceId);
if (i == end(_conferenceInvites)) {
return;
}
for (auto j = begin(i->second.users); j != end(i->second.users);) {
const auto api = &j->first->session().api();
for (const auto &messageId : base::take(j->second.incoming)) {
api->request(MTPphone_DeclineConferenceCallInvite(
MTP_int(messageId.bare)
)).send();
}
if (j->second.outgoing.empty()) {
j = i->second.users.erase(j);
} else {
++j;
}
}
if (i->second.users.empty()) {
_conferenceInvites.erase(i);
}
}
void Instance::showConferenceInvite(
not_null<UserData*> user,
MsgId conferenceInviteMsgId) {
const auto item = user->owner().message(user, conferenceInviteMsgId);
const auto media = item ? item->media() : nullptr;
const auto call = media ? media->call() : nullptr;
const auto conferenceId = call ? call->conferenceId : 0;
if (!conferenceId
|| call->state != Data::CallState::Invitation
|| user->isSelf()) {
return;
} else if (_currentCall
&& _currentCall->conferenceId() == conferenceId) {
return;
} else if (inGroupCall()
&& _currentGroupCall->conference()
&& _currentGroupCall->conferenceCall()->id() == conferenceId) {
return;
}
const auto &config = user->session().serverConfig();
if (inCall() || inGroupCall()) {
declineIncomingConferenceInvites(conferenceId);
} else if (item->date() + (config.callRingTimeoutMs / 1000)
< base::unixtime::now()) {
declineIncomingConferenceInvites(conferenceId);
LOG(("Ignoring too old conference call invitation."));
} else {
const auto delegate = _delegate.get();
auto call = std::make_unique<Call>(
delegate,
user,
conferenceId,
conferenceInviteMsgId);
const auto raw = call.get();
user->session().account().sessionChanges(
) | rpl::start_with_next([=] {
destroyCall(raw);
}, raw->lifetime());
if (_currentCall) {
_currentCallPanel->replaceCall(raw);
std::swap(_currentCall, call);
call->hangup();
} else {
_currentCallPanel = std::make_unique<Panel>(raw);
_currentCall = std::move(call);
}
_currentCallChanges.fire_copy(raw);
}
}
} // namespace Calls } // namespace Calls

View file

@ -77,6 +77,15 @@ struct StartConferenceCallArgs {
bool migrating = false; bool migrating = false;
}; };
struct ConferenceInviteMessages {
base::flat_set<MsgId> incoming;
base::flat_set<MsgId> outgoing;
};
struct ConferenceInvites {
base::flat_map<not_null<UserData*>, ConferenceInviteMessages> users;
};
class Instance final : public base::has_weak_ptr { class Instance final : public base::has_weak_ptr {
public: public:
Instance(); Instance();
@ -122,6 +131,23 @@ public:
-> std::shared_ptr<tgcalls::VideoCaptureInterface>; -> std::shared_ptr<tgcalls::VideoCaptureInterface>;
void requestPermissionsOrFail(Fn<void()> onSuccess, bool video = true); void requestPermissionsOrFail(Fn<void()> onSuccess, bool video = true);
[[nodiscard]] const ConferenceInvites &conferenceInvites(
CallId conferenceId) const;
void registerConferenceInvite(
CallId conferenceId,
not_null<UserData*> user,
MsgId messageId,
bool incoming);
void unregisterConferenceInvite(
CallId conferenceId,
not_null<UserData*> user,
MsgId messageId,
bool incoming);
void showConferenceInvite(
not_null<UserData*> user,
MsgId conferenceInviteMsgId);
void declineIncomingConferenceInvites(CallId conferenceId);
[[nodiscard]] FnMut<void()> addAsyncWaiter(); [[nodiscard]] FnMut<void()> addAsyncWaiter();
[[nodiscard]] bool isSharingScreen() const; [[nodiscard]] bool isSharingScreen() const;
@ -188,6 +214,8 @@ private:
const std::unique_ptr<Group::ChooseJoinAsProcess> _chooseJoinAs; const std::unique_ptr<Group::ChooseJoinAsProcess> _chooseJoinAs;
const std::unique_ptr<Group::StartRtmpProcess> _startWithRtmp; const std::unique_ptr<Group::StartRtmpProcess> _startWithRtmp;
base::flat_map<CallId, ConferenceInvites> _conferenceInvites;
base::flat_set<std::unique_ptr<crl::semaphore>> _asyncWaiters; base::flat_set<std::unique_ptr<crl::semaphore>> _asyncWaiters;
}; };

View file

@ -513,7 +513,14 @@ 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) {
if (auto row = createInvitedRow(invite.user)) { if (invite.removed) {
if (const auto row = findRow(invite.user)) {
if (row->state() == Row::State::Invited) {
delegate()->peerListRemoveRow(row);
delegate()->peerListRefreshRows();
}
}
} else if (auto row = createInvitedRow(invite.user)) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
} }

View file

@ -789,7 +789,8 @@ void GroupCall::applyParticipantsSlice(
.videoJoined = videoJoined, .videoJoined = videoJoined,
.applyVolumeFromMin = applyVolumeFromMin, .applyVolumeFromMin = applyVolumeFromMin,
}; };
if (i == end(_participants)) { const auto adding = (i == end(_participants));
if (adding) {
if (value.ssrc) { if (value.ssrc) {
_participantPeerByAudioSsrc.emplace( _participantPeerByAudioSsrc.emplace(
value.ssrc, value.ssrc,
@ -802,9 +803,6 @@ void GroupCall::applyParticipantsSlice(
participantPeer); participantPeer);
} }
_participants.push_back(value); _participants.push_back(value);
if (const auto user = participantPeer->asUser()) {
_peer->owner().unregisterInvitedToCallUser(_id, user);
}
} else { } else {
if (i->ssrc != value.ssrc) { if (i->ssrc != value.ssrc) {
_participantPeerByAudioSsrc.erase(i->ssrc); _participantPeerByAudioSsrc.erase(i->ssrc);
@ -836,6 +834,11 @@ void GroupCall::applyParticipantsSlice(
.now = value, .now = value,
}); });
} }
if (adding) {
if (const auto user = participantPeer->asUser()) {
_peer->owner().unregisterInvitedToCallUser(_id, user);
}
}
}); });
} }
if (sliceSource == ApplySliceSource::UpdateReceived) { if (sliceSource == ApplySliceSource::UpdateReceived) {

View file

@ -68,6 +68,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_session_settings.h" #include "main/main_session_settings.h"
#include "calls/calls_instance.h"
#include "core/application.h" #include "core/application.h"
#include "core/click_handler_types.h" // ClickHandlerContext #include "core/click_handler_types.h" // ClickHandlerContext
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -1684,11 +1685,32 @@ std::unique_ptr<HistoryView::Media> MediaLocation::createView(
MediaCall::MediaCall(not_null<HistoryItem*> parent, const Call &call) MediaCall::MediaCall(not_null<HistoryItem*> parent, const Call &call)
: Media(parent) : Media(parent)
, _call(call) { , _call(call) {
parent->history()->owner().registerCallItem(parent); const auto peer = parent->history()->peer;
peer->owner().registerCallItem(parent);
if (const auto user = _call.conferenceId ? peer->asUser() : nullptr) {
if (_call.state == CallState::Invitation) {
Core::App().calls().registerConferenceInvite(
_call.conferenceId,
user,
parent->id,
!parent->out());
}
}
} }
MediaCall::~MediaCall() { MediaCall::~MediaCall() {
parent()->history()->owner().unregisterCallItem(parent()); const auto parent = this->parent();
const auto peer = parent->history()->peer;
peer->owner().unregisterCallItem(parent);
if (const auto user = _call.conferenceId ? peer->asUser() : nullptr) {
if (_call.state == CallState::Invitation) {
Core::App().calls().unregisterConferenceInvite(
_call.conferenceId,
user,
parent->id,
!parent->out());
}
}
} }
std::unique_ptr<Media> MediaCall::clone(not_null<HistoryItem*> parent) { std::unique_ptr<Media> MediaCall::clone(not_null<HistoryItem*> parent) {

View file

@ -1257,6 +1257,7 @@ void Session::unregisterInvitedToCallUser(
i->second.remove(user); i->second.remove(user);
if (i->second.empty()) { if (i->second.empty()) {
_invitedToCallUsers.erase(i); _invitedToCallUsers.erase(i);
_invitesToCalls.fire({ callId, user, true });
} }
} }
} }

View file

@ -256,6 +256,7 @@ public:
struct InviteToCall { struct InviteToCall {
CallId id = 0; CallId id = 0;
not_null<UserData*> user; not_null<UserData*> user;
bool removed = false;
}; };
[[nodiscard]] rpl::producer<InviteToCall> invitesToCalls() const { [[nodiscard]] rpl::producer<InviteToCall> invitesToCalls() const {
return _invitesToCalls.events(); return _invitesToCalls.events();

View file

@ -52,7 +52,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h" #include "mainwindow.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "window/window_session_controller.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "spellcheck/spellcheck_types.h" #include "spellcheck/spellcheck_types.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
@ -1228,8 +1227,8 @@ void History::applyServiceChanges(
} }
}, [&](const MTPDmessageActionConferenceCall &data) { }, [&](const MTPDmessageActionConferenceCall &data) {
if (!data.is_active() && !data.is_missed() && !item->out()) { if (!data.is_active() && !data.is_missed() && !item->out()) {
if (const auto window = session().tryResolveWindow()) { if (const auto user = item->history()->peer->asUser()) {
window->resolveConferenceCall(item->id); Core::App().calls().showConferenceInvite(user, item->id);
} }
} }
}, [](const auto &) { }, [](const auto &) {

View file

@ -881,26 +881,17 @@ void SessionNavigation::resolveConferenceCall(
data.vid().v, data.vid().v,
data.vaccess_hash().v); data.vaccess_hash().v);
call->processFullCall(result); call->processFullCall(result);
const auto confirmed = std::make_shared<bool>();
const auto join = [=] { const auto join = [=] {
*confirmed = true;
Core::App().calls().startOrJoinConferenceCall({ Core::App().calls().startOrJoinConferenceCall({
.call = call, .call = call,
.linkSlug = slug, .linkSlug = slug,
.joinMessageId = inviteMsgId, .joinMessageId = inviteMsgId,
}); });
}; };
const auto box = uiShow()->show(Box( uiShow()->show(Box(
Calls::Group::ConferenceCallJoinConfirm, Calls::Group::ConferenceCallJoinConfirm,
call, call,
join)); join));
box->boxClosing() | rpl::start_with_next([=] {
if (inviteMsgId && !*confirmed) {
_api.request(MTPphone_DeclineConferenceCallInvite(
MTP_int(inviteMsgId.bare)
)).send();
}
}, box->lifetime());
if (finished) { if (finished) {
finished(true); finished(true);
} }