mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-03 21:54:05 +02:00
PoC conference call creation.
This commit is contained in:
parent
5e6c81a98e
commit
c80c23e8e8
14 changed files with 363 additions and 38 deletions
|
@ -1540,6 +1540,8 @@ PRIVATE
|
|||
support/support_preload.h
|
||||
support/support_templates.cpp
|
||||
support/support_templates.h
|
||||
tde2e/tde2e_integration.cpp
|
||||
tde2e/tde2e_integration.h
|
||||
ui/boxes/edit_invite_link_session.cpp
|
||||
ui/boxes/edit_invite_link_session.h
|
||||
ui/boxes/peer_qr_box.cpp
|
||||
|
|
|
@ -4911,6 +4911,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_confcall_join_title" = "Group Call";
|
||||
"lng_confcall_join_text" = "You are invited to join a Telegram Call.";
|
||||
"lng_confcall_join_button" = "Join Group Call";
|
||||
"lng_confcall_create_link" = "Create Call Link";
|
||||
"lng_confcall_create_link_description" = "You can create a link that will allow your friends on Telegram to join the call.";
|
||||
|
||||
"lng_no_mic_permission" = "Telegram needs microphone access so that you can make calls and record voice messages.";
|
||||
|
||||
|
|
|
@ -303,14 +303,19 @@ void Updates::feedUpdateVector(
|
|||
auto list = updates.v;
|
||||
const auto hasGroupCallParticipantUpdates = ranges::contains(
|
||||
list,
|
||||
mtpc_updateGroupCallParticipants,
|
||||
&MTPUpdate::type);
|
||||
true,
|
||||
[](const MTPUpdate &update) {
|
||||
return update.type() == mtpc_updateGroupCallParticipants
|
||||
|| update.type() == mtpc_updateGroupCallChainBlocks;
|
||||
});
|
||||
if (hasGroupCallParticipantUpdates) {
|
||||
ranges::stable_sort(list, std::less<>(), [](const MTPUpdate &entry) {
|
||||
if (entry.type() == mtpc_updateGroupCallParticipants) {
|
||||
if (entry.type() == mtpc_updateGroupCallChainBlocks) {
|
||||
return 0;
|
||||
} else {
|
||||
} else if (entry.type() == mtpc_updateGroupCallParticipants) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
});
|
||||
} else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
|
||||
|
@ -324,7 +329,8 @@ void Updates::feedUpdateVector(
|
|||
if ((policy == SkipUpdatePolicy::SkipMessageIds
|
||||
&& type == mtpc_updateMessageID)
|
||||
|| (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants
|
||||
&& type != mtpc_updateGroupCallParticipants)) {
|
||||
&& type != mtpc_updateGroupCallParticipants
|
||||
&& type != mtpc_updateGroupCallChainBlocks)) {
|
||||
continue;
|
||||
}
|
||||
feedUpdate(entry);
|
||||
|
@ -954,7 +960,8 @@ void Updates::applyGroupCallParticipantUpdates(const MTPUpdates &updates) {
|
|||
data.vupdates(),
|
||||
SkipUpdatePolicy::SkipExceptGroupCallParticipants);
|
||||
}, [&](const MTPDupdateShort &data) {
|
||||
if (data.vupdate().type() == mtpc_updateGroupCallParticipants) {
|
||||
if (data.vupdate().type() == mtpc_updateGroupCallParticipants
|
||||
|| data.vupdate().type() == mtpc_updateGroupCallChainBlocks) {
|
||||
feedUpdate(data.vupdate());
|
||||
}
|
||||
}, [](const auto &) {
|
||||
|
@ -2110,6 +2117,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
case mtpc_updatePhoneCall:
|
||||
case mtpc_updatePhoneCallSignalingData:
|
||||
case mtpc_updateGroupCallParticipants:
|
||||
case mtpc_updateGroupCallChainBlocks:
|
||||
case mtpc_updateGroupCallConnection:
|
||||
case mtpc_updateGroupCall: {
|
||||
Core::App().calls().handleUpdate(&session(), update);
|
||||
|
|
|
@ -235,16 +235,18 @@ void Instance::startOrJoinConferenceCall(
|
|||
StartConferenceCallArgs args) {
|
||||
destroyCurrentCall();
|
||||
|
||||
const auto session = &args.call->peer()->session();
|
||||
auto call = std::make_unique<GroupCall>(
|
||||
_delegate.get(),
|
||||
Calls::Group::ConferenceInfo{
|
||||
.call = args.call,
|
||||
.call = std::move(args.call),
|
||||
.e2e = std::move(args.e2e),
|
||||
.linkSlug = args.linkSlug,
|
||||
.joinMessageId = args.joinMessageId,
|
||||
});
|
||||
const auto raw = call.get();
|
||||
|
||||
args.call->peer()->session().account().sessionChanges(
|
||||
session->account().sessionChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
destroyGroupCall(raw);
|
||||
}, raw->lifetime());
|
||||
|
@ -547,6 +549,8 @@ void Instance::handleUpdate(
|
|||
handleGroupCallUpdate(session, update);
|
||||
}, [&](const MTPDupdateGroupCallParticipants &data) {
|
||||
handleGroupCallUpdate(session, update);
|
||||
}, [&](const MTPDupdateGroupCallChainBlocks &data) {
|
||||
handleGroupCallUpdate(session, update);
|
||||
}, [](const auto &) {
|
||||
Unexpected("Update type in Calls::Instance::handleUpdate.");
|
||||
});
|
||||
|
@ -677,6 +681,12 @@ void Instance::handleGroupCallUpdate(
|
|||
}, [](const MTPDinputGroupCallSlug &) -> CallId {
|
||||
Unexpected("slug in Instance::handleGroupCallUpdate");
|
||||
});
|
||||
}, [](const MTPDupdateGroupCallChainBlocks &data) {
|
||||
return data.vcall().match([&](const MTPDinputGroupCall &data) {
|
||||
return data.vid().v;
|
||||
}, [](const MTPDinputGroupCallSlug &) -> CallId {
|
||||
Unexpected("slug in Instance::handleGroupCallUpdate");
|
||||
});
|
||||
}, [](const auto &) -> CallId {
|
||||
Unexpected("Type in Instance::handleGroupCallUpdate.");
|
||||
});
|
||||
|
|
|
@ -45,6 +45,10 @@ namespace tgcalls {
|
|||
class VideoCaptureInterface;
|
||||
} // namespace tgcalls
|
||||
|
||||
namespace TdE2E {
|
||||
class Call;
|
||||
} // namespace TdE2E
|
||||
|
||||
namespace Calls {
|
||||
|
||||
class Call;
|
||||
|
@ -66,6 +70,7 @@ struct StartGroupCallArgs {
|
|||
|
||||
struct StartConferenceCallArgs {
|
||||
std::shared_ptr<Data::GroupCall> call;
|
||||
std::shared_ptr<TdE2E::Call> e2e;
|
||||
QString linkSlug;
|
||||
MsgId joinMessageId;
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/global_shortcuts.h"
|
||||
#include "base/random.h"
|
||||
#include "tde2e/tde2e_api.h"
|
||||
#include "tde2e/tde2e_integration.h"
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "webrtc/webrtc_create_adm.h"
|
||||
#include "webrtc/webrtc_environment.h"
|
||||
|
@ -53,6 +54,10 @@ constexpr auto kFixManualLargeVideoDuration = 5 * crl::time(1000);
|
|||
constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000);
|
||||
constexpr auto kFullAsMediumsCount = 4; // 1 Full is like 4 Mediums.
|
||||
constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums.
|
||||
constexpr auto kShortPollChainBlocksTimeout = 5 * crl::time(1000);
|
||||
constexpr auto kShortPollChainBlocksPerRequest = 50;
|
||||
constexpr auto kSubChain0 = 0;
|
||||
constexpr auto kSubChain1 = 1;
|
||||
|
||||
[[nodiscard]] const Data::GroupCallParticipant *LookupParticipant(
|
||||
not_null<PeerData*> peer,
|
||||
|
@ -603,7 +608,8 @@ GroupCall::GroupCall(
|
|||
Group::ConferenceInfo conference,
|
||||
const MTPInputGroupCall &inputCall)
|
||||
: _delegate(delegate)
|
||||
, _conferenceCall(conference.call)
|
||||
, _conferenceCall(std::move(conference.call))
|
||||
, _e2e(std::move(conference.e2e))
|
||||
, _peer(join.peer)
|
||||
, _history(_peer->owner().history(_peer))
|
||||
, _api(&_peer->session().mtp())
|
||||
|
@ -641,8 +647,15 @@ GroupCall::GroupCall(
|
|||
, _rtmp(join.rtmp)
|
||||
, _rtmpVolume(Group::kDefaultVolume) {
|
||||
if (_conferenceCall) {
|
||||
_e2eState = std::make_unique<TdE2E::CallState>(
|
||||
TdE2E::CreateCallState());
|
||||
if (!_e2e) {
|
||||
_e2e = std::make_shared<TdE2E::Call>(
|
||||
TdE2E::MakeUserId(_peer->session().user()));
|
||||
}
|
||||
for (auto i = 0; i != kSubChainsCount; ++i) {
|
||||
_subchains[i].timer.setCallback([=] {
|
||||
checkChainBlocksRequest(i);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_muted.value(
|
||||
|
@ -1419,10 +1432,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
|||
? Flag::f_video_stopped
|
||||
: Flag(0))
|
||||
| (_conferenceJoinMessageId ? Flag::f_invite_msg_id : Flag())
|
||||
| (_e2eState ? Flag::f_public_key : Flag());
|
||||
const auto publicKey = _e2eState
|
||||
? _e2eState->myKey
|
||||
: TdE2E::PublicKey();
|
||||
| (_e2e ? Flag::f_public_key : Flag());
|
||||
_api.request(MTPphone_JoinGroupCall(
|
||||
MTP_flags(flags),
|
||||
(_conferenceLinkSlug.isEmpty()
|
||||
|
@ -1431,9 +1441,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
|||
MTP_string(_conferenceLinkSlug))),
|
||||
joinAs()->input,
|
||||
MTP_string(_joinHash),
|
||||
MTP_int256(
|
||||
MTP_int128(publicKey.a, publicKey.b),
|
||||
MTP_int128(publicKey.c, publicKey.d)),
|
||||
(_e2e ? TdE2E::PublicKeyToMTP(_e2e->myKey()) : MTPint256()),
|
||||
MTP_int(_conferenceJoinMessageId.bare),
|
||||
MTP_dataJSON(MTP_bytes(json))
|
||||
)).done([=](
|
||||
|
@ -1470,6 +1478,8 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
|||
real->reloadIfStale();
|
||||
}
|
||||
}
|
||||
checkChainBlocksRequest(kSubChain0);
|
||||
checkChainBlocksRequest(kSubChain1);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_joinState.finish();
|
||||
|
||||
|
@ -1490,6 +1500,40 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
|||
});
|
||||
}
|
||||
|
||||
void GroupCall::checkChainBlocksRequest(int subchain) {
|
||||
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
||||
|
||||
auto &state = _subchains[subchain];
|
||||
if (state.requestId) {
|
||||
return;
|
||||
}
|
||||
const auto now = crl::now();
|
||||
const auto left = state.lastUpdate + kShortPollChainBlocksTimeout - now;
|
||||
if (left > 0) {
|
||||
if (!state.timer.isActive()) {
|
||||
state.timer.callOnce(left);
|
||||
}
|
||||
return;
|
||||
}
|
||||
state.requestId = _api.request(MTPphone_GetGroupCallChainBlocks(
|
||||
inputCall(),
|
||||
MTP_int(subchain),
|
||||
MTP_int(state.height),
|
||||
MTP_int(kShortPollChainBlocksPerRequest)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
auto &state = _subchains[subchain];
|
||||
state.lastUpdate = crl::now();
|
||||
state.requestId = 0;
|
||||
_peer->session().api().applyUpdates(result);
|
||||
state.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
auto &state = _subchains[subchain];
|
||||
state.lastUpdate = crl::now();
|
||||
state.requestId = 0;
|
||||
state.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GroupCall::checkNextJoinAction() {
|
||||
if (_joinState.action != JoinAction::None) {
|
||||
return;
|
||||
|
@ -1640,7 +1684,6 @@ void GroupCall::applyMeInCallLocally() {
|
|||
: participant
|
||||
? participant->raisedHandRating
|
||||
: FindLocalRaisedHandRating(real->participants());
|
||||
const auto publicKey = _e2eState ? _e2eState->myKey : TdE2E::PublicKey();
|
||||
const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
|
||||
| (lastActive ? Flag::f_active_date : Flag(0))
|
||||
| (_joinState.ssrc ? Flag(0) : Flag::f_left)
|
||||
|
@ -1650,7 +1693,7 @@ void GroupCall::applyMeInCallLocally() {
|
|||
| Flag::f_volume_by_admin // Self volume can only be set by admin.
|
||||
| ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0))
|
||||
| (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0))
|
||||
| (_e2eState ? Flag::f_public_key : Flag(0));
|
||||
| (_e2e ? Flag::f_public_key : Flag(0));
|
||||
real->applyLocalUpdate(
|
||||
MTP_updateGroupCallParticipants(
|
||||
inputCall(),
|
||||
|
@ -1667,9 +1710,9 @@ void GroupCall::applyMeInCallLocally() {
|
|||
MTP_long(raisedHandRating),
|
||||
MTPGroupCallParticipantVideo(),
|
||||
MTPGroupCallParticipantVideo(),
|
||||
MTP_int256(
|
||||
MTP_int128(publicKey.a, publicKey.b),
|
||||
MTP_int128(publicKey.c, publicKey.d)))),
|
||||
(_e2e
|
||||
? TdE2E::PublicKeyToMTP(_e2e->myKey())
|
||||
: MTPint256()))),
|
||||
MTP_int(0)).c_updateGroupCallParticipants());
|
||||
}
|
||||
|
||||
|
@ -2022,6 +2065,8 @@ void GroupCall::handleUpdate(const MTPUpdate &update) {
|
|||
handleUpdate(data);
|
||||
}, [&](const MTPDupdateGroupCallParticipants &data) {
|
||||
handleUpdate(data);
|
||||
}, [&](const MTPDupdateGroupCallChainBlocks &data) {
|
||||
handleUpdate(data);
|
||||
}, [](const auto &) {
|
||||
Unexpected("Type in Instance::applyGroupCallUpdateChecked.");
|
||||
});
|
||||
|
@ -2063,6 +2108,33 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
|
|||
}
|
||||
}
|
||||
|
||||
void GroupCall::handleUpdate(const MTPDupdateGroupCallChainBlocks &data) {
|
||||
const auto callId = data.vcall().match([](
|
||||
const MTPDinputGroupCall &data) {
|
||||
return data.vid().v;
|
||||
}, [](const MTPDinputGroupCallSlug &) -> CallId {
|
||||
Unexpected("inputGroupCallSlug in GroupCall::handleUpdate.");
|
||||
});
|
||||
if (_id != callId || !_e2e) {
|
||||
return;
|
||||
}
|
||||
const auto subchain = data.vsub_chain_id().v;
|
||||
if (subchain < 0 || subchain >= kSubChainsCount) {
|
||||
return;
|
||||
}
|
||||
auto &entry = _subchains[subchain];
|
||||
entry.lastUpdate = crl::now();
|
||||
entry.height = data.vnext_offset().v;
|
||||
entry.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
||||
for (const auto &block : data.vblocks().v) {
|
||||
const auto result = _e2e->apply({ block.v });
|
||||
if (result == TdE2E::Call::ApplyResult::BlockSkipped) {
|
||||
AssertIsDebug();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::applyQueuedSelfUpdates() {
|
||||
const auto weak = base::make_weak(this);
|
||||
while (weak
|
||||
|
|
|
@ -44,7 +44,7 @@ class GroupCall;
|
|||
|
||||
namespace TdE2E {
|
||||
struct ParticipantState;
|
||||
struct CallState;
|
||||
class Call;
|
||||
} // namespace TdE2E
|
||||
|
||||
namespace Calls {
|
||||
|
@ -437,6 +437,7 @@ private:
|
|||
struct SinkPointer;
|
||||
|
||||
static constexpr uint32 kDisabledSsrc = uint32(-1);
|
||||
static constexpr int kSubChainsCount = 2;
|
||||
|
||||
struct LoadingPart {
|
||||
std::shared_ptr<LoadPartTask> task;
|
||||
|
@ -475,6 +476,12 @@ private:
|
|||
ssrc = updatedSsrc;
|
||||
}
|
||||
};
|
||||
struct SubChainState {
|
||||
crl::time lastUpdate = 0;
|
||||
base::Timer timer;
|
||||
int height = 0;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
friend inline constexpr bool is_flag_type(SendUpdateType) {
|
||||
return true;
|
||||
|
@ -507,6 +514,7 @@ private:
|
|||
void handlePossibleDiscarded(const MTPDgroupCallDiscarded &data);
|
||||
void handleUpdate(const MTPDupdateGroupCall &data);
|
||||
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
|
||||
void handleUpdate(const MTPDupdateGroupCallChainBlocks &data);
|
||||
bool tryCreateController();
|
||||
void destroyController();
|
||||
bool tryCreateScreencast();
|
||||
|
@ -535,6 +543,7 @@ private:
|
|||
void rejoinPresentation();
|
||||
void leavePresentation();
|
||||
void checkNextJoinAction();
|
||||
void checkChainBlocksRequest(int subchain);
|
||||
|
||||
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
|
||||
void setInstanceConnected(tgcalls::GroupNetworkState networkState);
|
||||
|
@ -593,6 +602,8 @@ private:
|
|||
|
||||
const not_null<Delegate*> _delegate;
|
||||
const std::shared_ptr<Data::GroupCall> _conferenceCall;
|
||||
std::shared_ptr<TdE2E::Call> _e2e;
|
||||
|
||||
not_null<PeerData*> _peer; // Can change in legacy group migration.
|
||||
rpl::event_stream<PeerData*> _peerStream;
|
||||
not_null<History*> _history; // Can change in legacy group migration.
|
||||
|
@ -624,8 +635,6 @@ private:
|
|||
int64 _serverTimeMs = 0;
|
||||
crl::time _serverTimeMsGotAt = 0;
|
||||
|
||||
std::unique_ptr<TdE2E::CallState> _e2eState;
|
||||
|
||||
QString _rtmpUrl;
|
||||
QString _rtmpKey;
|
||||
|
||||
|
@ -710,6 +719,8 @@ private:
|
|||
bool _reloadedStaleCall = false;
|
||||
int _rtmpVolume = 0;
|
||||
|
||||
SubChainState _subchains[kSubChainsCount];
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -19,6 +19,10 @@ namespace Ui {
|
|||
class GenericBox;
|
||||
} // namespace Ui
|
||||
|
||||
namespace TdE2E {
|
||||
class Call;
|
||||
} // namespace TdE2E
|
||||
|
||||
namespace Calls::Group {
|
||||
|
||||
constexpr auto kDefaultVolume = 10000;
|
||||
|
@ -67,6 +71,7 @@ struct JoinInfo {
|
|||
|
||||
struct ConferenceInfo {
|
||||
std::shared_ptr<Data::GroupCall> call;
|
||||
std::shared_ptr<TdE2E::Call> e2e;
|
||||
QString linkSlug;
|
||||
MsgId joinMessageId;
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ struct ParticipantVideoParams;
|
|||
|
||||
namespace TdE2E {
|
||||
struct ParticipantState;
|
||||
struct UserId;
|
||||
} // namespace TdE2E
|
||||
|
||||
namespace Data {
|
||||
|
|
|
@ -12,20 +12,77 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <tde2e/td/e2e/e2e_api.h>
|
||||
|
||||
namespace TdE2E {
|
||||
namespace {
|
||||
|
||||
CallState CreateCallState() {
|
||||
constexpr auto kPermissionAdd = 1;
|
||||
constexpr auto kPermissionRemove = 2;
|
||||
|
||||
[[nodiscard]] tde2e_api::Slice Slice(const QByteArray &data) {
|
||||
return {
|
||||
data.constData(),
|
||||
std::string_view::size_type(data.size())
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Call::Call(UserId myUserId)
|
||||
: _myUserId(myUserId) {
|
||||
const auto id = tde2e_api::key_generate_temporary_private_key();
|
||||
Assert(id.is_ok());
|
||||
const auto key = tde2e_api::key_to_public_key(id.value());
|
||||
_myKeyId = { .v = uint64(id.value()) };
|
||||
|
||||
const auto key = tde2e_api::key_to_public_key(_myKeyId.v);
|
||||
Assert(key.is_ok());
|
||||
Assert(key.value().size() == sizeof(_myKey));
|
||||
memcpy(&_myKey, key.value().data(), sizeof(_myKey));
|
||||
}
|
||||
|
||||
auto result = CallState{
|
||||
.myKeyId = PrivateKeyId{ .v = uint64(id.value()) },
|
||||
PublicKey Call::myKey() const {
|
||||
return _myKey;
|
||||
}
|
||||
|
||||
Block Call::makeZeroBlock() const {
|
||||
const auto publicKeyView = std::string_view{
|
||||
reinterpret_cast<const char*>(&_myKey),
|
||||
sizeof(_myKey),
|
||||
};
|
||||
Assert(key.value().size() == sizeof(result.myKey));
|
||||
memcpy(&result.myKey, key.value().data(), 32);
|
||||
const auto publicKeyId = tde2e_api::key_from_public_key(publicKeyView);
|
||||
Assert(publicKeyId.is_ok());
|
||||
|
||||
return result;
|
||||
const auto myKeyId = std::int64_t(_myKeyId.v);
|
||||
const auto result = tde2e_api::call_create_zero_block(myKeyId, {
|
||||
.height = 0,
|
||||
.participants = { {
|
||||
.user_id = std::int64_t(_myUserId.v),
|
||||
.public_key_id = publicKeyId.value(),
|
||||
.permissions = kPermissionAdd | kPermissionRemove,
|
||||
} },
|
||||
});
|
||||
Assert(result.is_ok());
|
||||
|
||||
return {
|
||||
.data = QByteArray::fromStdString(result.value()),
|
||||
};
|
||||
}
|
||||
|
||||
void Call::create(const Block &last) {
|
||||
tde2e_api::call_create(std::int64_t(_myKeyId.v), Slice(last.data));
|
||||
}
|
||||
|
||||
Call::ApplyResult Call::apply(const Block &block) {
|
||||
const auto result = tde2e_api::call_apply_block(
|
||||
std::int64_t(_id.v),
|
||||
Slice(block.data));
|
||||
|
||||
if (!result.is_ok()) {
|
||||
const auto error = result.error();
|
||||
(void)error;
|
||||
}
|
||||
|
||||
return result.is_ok()
|
||||
? ApplyResult::Success
|
||||
: ApplyResult::BlockSkipped;
|
||||
}
|
||||
|
||||
} // namespace TdE2E
|
||||
|
|
|
@ -11,10 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace TdE2E {
|
||||
|
||||
struct UserId {
|
||||
uint64 v = 0;
|
||||
};
|
||||
|
||||
struct PrivateKeyId {
|
||||
uint64 v = 0;
|
||||
};
|
||||
|
||||
struct CallId {
|
||||
uint64 v = 0;
|
||||
};
|
||||
|
||||
struct PublicKey {
|
||||
uint64 a = 0;
|
||||
uint64 b = 0;
|
||||
|
@ -23,15 +31,36 @@ struct PublicKey {
|
|||
};
|
||||
|
||||
struct ParticipantState {
|
||||
uint64 id = 0;
|
||||
UserId id;
|
||||
PublicKey key;
|
||||
};
|
||||
|
||||
struct CallState {
|
||||
PrivateKeyId myKeyId;
|
||||
PublicKey myKey;
|
||||
struct Block {
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
[[nodiscard]] CallState CreateCallState();
|
||||
class Call final {
|
||||
public:
|
||||
explicit Call(UserId myUserId);
|
||||
|
||||
[[nodiscard]] PublicKey myKey() const;
|
||||
|
||||
[[nodiscard]] Block makeZeroBlock() const;
|
||||
|
||||
void create(const Block &last);
|
||||
|
||||
enum class ApplyResult {
|
||||
Success,
|
||||
BlockSkipped
|
||||
};
|
||||
[[nodiscard]] ApplyResult apply(const Block &block);
|
||||
|
||||
private:
|
||||
CallId _id;
|
||||
UserId _myUserId;
|
||||
PrivateKeyId _myKeyId;
|
||||
PublicKey _myKey;
|
||||
|
||||
};
|
||||
|
||||
} // namespace TdE2E
|
||||
|
|
27
Telegram/SourceFiles/tde2e/tde2e_integration.cpp
Normal file
27
Telegram/SourceFiles/tde2e/tde2e_integration.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "tde2e/tde2e_integration.h"
|
||||
|
||||
#include "data/data_user.h"
|
||||
#include "tde2e/tde2e_api.h"
|
||||
|
||||
namespace TdE2E {
|
||||
|
||||
UserId MakeUserId(not_null<UserData*> user) {
|
||||
return MakeUserId(peerToUser(user->id));
|
||||
}
|
||||
|
||||
UserId MakeUserId(::UserId id) {
|
||||
return { .v = id.bare };
|
||||
}
|
||||
|
||||
MTPint256 PublicKeyToMTP(const PublicKey &key) {
|
||||
return MTP_int256(MTP_int128(key.a, key.b), MTP_int128(key.c, key.d));
|
||||
}
|
||||
|
||||
} // namespace TdE2E
|
20
Telegram/SourceFiles/tde2e/tde2e_integration.h
Normal file
20
Telegram/SourceFiles/tde2e/tde2e_integration.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace TdE2E {
|
||||
|
||||
struct UserId;
|
||||
struct PublicKey;
|
||||
|
||||
[[nodiscard]] UserId MakeUserId(not_null<UserData*> user);
|
||||
[[nodiscard]] UserId MakeUserId(::UserId id);
|
||||
|
||||
[[nodiscard]] MTPint256 PublicKeyToMTP(const PublicKey &key);
|
||||
|
||||
} // namespace TdE2E
|
|
@ -14,17 +14,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "calls/calls_box_controller.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "core/application.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_group_call.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_user.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/profile/info_profile_badge.h"
|
||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/stories/info_stories_widget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_account.h"
|
||||
|
@ -38,6 +41,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/localstorage.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "support/support_templates.h"
|
||||
#include "tde2e/tde2e_api.h"
|
||||
#include "tde2e/tde2e_integration.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/controls/swipe_handler.h"
|
||||
|
@ -92,6 +97,66 @@ constexpr auto kPlayStatusLimit = 2;
|
|||
|| (now.month() == 1 && now.day() == 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Ui::SettingsButton*> AddCreateCallLinkButton(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Window::SessionController*> controller,
|
||||
Fn<void()> done) {
|
||||
const auto result = container->add(object_ptr<Ui::SettingsButton>(
|
||||
container,
|
||||
tr::lng_confcall_create_link(),
|
||||
st::inviteViaLinkButton), QMargins());
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddDividerText(
|
||||
container,
|
||||
tr::lng_confcall_create_link_description(Ui::Text::WithEntities));
|
||||
|
||||
const auto icon = Ui::CreateChild<Info::Profile::FloatingIcon>(
|
||||
result,
|
||||
st::inviteViaLinkIcon,
|
||||
QPoint());
|
||||
result->heightValue(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
icon->moveToLeft(
|
||||
st::inviteViaLinkIconPosition.x(),
|
||||
(height - st::inviteViaLinkIcon.height()) / 2);
|
||||
}, icon->lifetime());
|
||||
|
||||
const auto creating = result->lifetime().make_state<bool>();
|
||||
result->setClickedCallback([=] {
|
||||
if (*creating) {
|
||||
return;
|
||||
}
|
||||
*creating = true;
|
||||
auto e2e = std::make_shared<TdE2E::Call>(
|
||||
TdE2E::MakeUserId(controller->session().user()));
|
||||
const auto session = &controller->session();
|
||||
session->api().request(MTPphone_CreateConferenceCall(
|
||||
TdE2E::PublicKeyToMTP(e2e->myKey()),
|
||||
MTP_bytes(e2e->makeZeroBlock().data)
|
||||
)).done(crl::guard(controller, [=](const MTPphone_GroupCall &result) {
|
||||
result.data().vcall().match([&](const auto &data) {
|
||||
const auto call = std::make_shared<Data::GroupCall>(
|
||||
session->user(),
|
||||
data.vid().v,
|
||||
data.vaccess_hash().v,
|
||||
TimeId(), // scheduleDate
|
||||
false); // rtmp
|
||||
call->processFullCall(result);
|
||||
Core::App().calls().startOrJoinConferenceCall(
|
||||
controller->uiShow(),
|
||||
{ .call = call, .e2e = e2e });
|
||||
});
|
||||
if (const auto onstack = done) {
|
||||
onstack();
|
||||
}
|
||||
})).fail(crl::guard(controller, [=](const MTP::Error &error) {
|
||||
controller->uiShow()->showToast(error.type());
|
||||
*creating = false;
|
||||
})).send();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void ShowCallsBox(not_null<Window::SessionController*> window) {
|
||||
struct State {
|
||||
State(not_null<Window::SessionController*> window)
|
||||
|
@ -129,6 +194,17 @@ void ShowCallsBox(not_null<Window::SessionController*> window) {
|
|||
Ui::AddDivider(groupCalls->entity());
|
||||
Ui::AddSkip(groupCalls->entity());
|
||||
|
||||
const auto button = AddCreateCallLinkButton(
|
||||
box->verticalLayout(),
|
||||
window,
|
||||
crl::guard(box, [=] { box->closeBox(); }));
|
||||
button->events(
|
||||
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::Enter);
|
||||
}) | rpl::start_with_next([=] {
|
||||
state->callsDelegate.peerListMouseLeftGeometry();
|
||||
}, button->lifetime());
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, &state->callsController),
|
||||
{});
|
||||
|
|
Loading…
Add table
Reference in a new issue