Update API scheme, something works now.

This commit is contained in:
John Preston 2025-03-28 00:41:05 +05:00
parent 0e46a4402a
commit 7deb5bfdf2
18 changed files with 464 additions and 157 deletions

View file

@ -4913,6 +4913,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_confcall_link_title" = "Call Link";
"lng_confcall_link_about" = "Anyone on Telegram can join your call by following the link below.";
"lng_confcall_link_or" = "or";
"lng_confcall_link_join" = "Be the first to join the call and add people from there. {link}";
"lng_confcall_link_join_link" = "Open call {arrow}";
"lng_no_mic_permission" = "Telegram needs microphone access so that you can make calls and record voice messages.";

View file

@ -1483,4 +1483,33 @@ groupCallCalendarColors: CalendarColors {
titleTextColor: groupCallMembersFg;
}
//
confcallLinkButton: RoundButton(defaultActiveButton) {
height: 42px;
textTop: 12px;
style: semiboldTextStyle;
}
confcallLinkBox: Box(defaultBox) {
buttonPadding: margins(22px, 11px, 22px, 64px);
buttonHeight: 42px;
button: confcallLinkButton;
shadowIgnoreTopSkip: true;
}
confcallLinkCopyButton: RoundButton(confcallLinkButton) {
icon: icon {{ "info/edit/links_copy", activeButtonFg }};
iconOver: icon {{ "info/edit/links_copy", activeButtonFgOver }};
iconPosition: point(-1px, 5px);
}
confcallLinkShareButton: RoundButton(confcallLinkCopyButton) {
icon: icon {{ "info/edit/links_share", activeButtonFg }};
iconOver: icon {{ "info/edit/links_share", activeButtonFgOver }};
}
confcallLinkHeaderIconPadding: margins(0px, 32px, 0px, 10px);
confcallLinkTitlePadding: margins(0px, 0px, 0px, 12px);
confcallLinkCenteredText: FlatLabel(defaultFlatLabel) {
align: align(top);
minWidth: 40px;
}
confcallLinkFooterOr: FlatLabel(confcallLinkCenteredText) {
textFg: windowSubTextFg;
}

View file

@ -690,7 +690,14 @@ void Instance::handleGroupCallUpdate(
}, [](const auto &) -> CallId {
Unexpected("Type in Instance::handleGroupCallUpdate.");
});
if (const auto existing = session->data().groupCall(callId)) {
if (update.type() == mtpc_updateGroupCallChainBlocks) {
const auto existing = session->data().groupCall(callId);
if (existing
&& _currentGroupCall
&& _currentGroupCall->lookupReal() == existing) {
_currentGroupCall->handleUpdate(update);
}
} else if (const auto existing = session->data().groupCall(callId)) {
existing->enqueueUpdate(update);
} else {
applyGroupCallUpdateChecked(session, update);

View file

@ -1405,101 +1405,162 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
const auto weak = base::make_weak(&_instanceGuard);
_instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) {
crl::on_main(weak, [=, payload = std::move(payload)] {
if (state() != State::Joining) {
_joinState.finish();
checkNextJoinAction();
return;
}
const auto ssrc = payload.audioSsrc;
_joinState.payload = {
.ssrc = payload.audioSsrc,
.json = QByteArray::fromStdString(payload.json),
};
LOG(("Call Info: Join payload received, joining with ssrc: %1."
).arg(ssrc));
const auto json = QByteArray::fromStdString(payload.json);
const auto wasMuteState = muted();
const auto wasVideoStopped = !isSharingCamera();
using Flag = MTPphone_JoinGroupCall::Flag;
const auto flags = (wasMuteState != MuteState::Active
? Flag::f_muted
: Flag(0))
| (_joinHash.isEmpty()
? Flag(0)
: Flag::f_invite_hash)
| (wasVideoStopped
? Flag::f_video_stopped
: Flag(0))
| (_conferenceJoinMessageId ? Flag::f_invite_msg_id : Flag())
| (_e2e ? (Flag::f_public_key | Flag::f_block) : Flag());
_api.request(MTPphone_JoinGroupCall(
MTP_flags(flags),
(_conferenceLinkSlug.isEmpty()
? inputCall()
: MTP_inputGroupCallSlug(
MTP_string(_conferenceLinkSlug))),
joinAs()->input,
MTP_string(_joinHash),
(_e2e ? TdE2E::PublicKeyToMTP(_e2e->myKey()) : MTPint256()),
(_e2e
? MTP_bytes(
_e2e->lastBlock0().value_or(TdE2E::Block()).data)
: MTPbytes()),
MTP_int(_conferenceJoinMessageId.bare),
MTP_dataJSON(MTP_bytes(json))
)).done([=](
const MTPUpdates &updates,
const MTP::Response &response) {
_serverTimeMs = TimestampInMsFromMsgId(response.outerMsgId);
_serverTimeMsGotAt = crl::now();
_joinState.finish(ssrc);
_mySsrcs.emplace(ssrc);
setState((_instanceState.current()
== InstanceState::Disconnected)
? State::Connecting
: State::Joined);
applyMeInCallLocally();
maybeSendMutedUpdate(wasMuteState);
_peer->session().api().applyUpdates(updates);
applyQueuedSelfUpdates();
checkFirstTimeJoined();
_screenJoinState.nextActionPending = true;
checkNextJoinAction();
if (wasVideoStopped == isSharingCamera()) {
sendSelfUpdate(SendUpdateType::CameraStopped);
}
if (isCameraPaused()) {
sendSelfUpdate(SendUpdateType::CameraPaused);
}
sendPendingSelfUpdates();
if (!_reloadedStaleCall
&& _state.current() != State::Joining) {
if (const auto real = lookupReal()) {
_reloadedStaleCall = true;
real->reloadIfStale();
}
}
requestSubchainBlocks(0, 0);
requestSubchainBlocks(1, 0);
}).fail([=](const MTP::Error &error) {
_joinState.finish();
const auto type = error.type();
LOG(("Call Error: Could not join, error: %1").arg(type));
if (type == u"GROUPCALL_SSRC_DUPLICATE_MUCH") {
rejoin();
return;
}
hangup();
Ui::Toast::Show((type == u"GROUPCALL_FORBIDDEN"_q)
? tr::lng_group_not_accessible(tr::now)
: Lang::Hard::ServerError());
}).send();
).arg(_joinState.payload.ssrc));
sendJoinRequest();
});
});
}
void GroupCall::sendJoinRequest() {
if (state() != State::Joining) {
_joinState.finish();
checkNextJoinAction();
return;
}
auto joinBlock = _e2e ? _e2e->makeJoinBlock().data : QByteArray();
if (_e2e && joinBlock.isEmpty()) {
_joinState.finish();
LOG(("Call Error: Could not generate join block."));
hangup();
Ui::Toast::Show(u"Could not generate join block."_q);
return;
}
const auto wasMuteState = muted();
const auto wasVideoStopped = !isSharingCamera();
using Flag = MTPphone_JoinGroupCall::Flag;
const auto flags = (wasMuteState != MuteState::Active
? Flag::f_muted
: Flag(0))
| (_joinHash.isEmpty()
? Flag(0)
: Flag::f_invite_hash)
| (wasVideoStopped
? Flag::f_video_stopped
: Flag(0))
| (_conferenceJoinMessageId ? Flag::f_invite_msg_id : Flag())
| (_e2e ? (Flag::f_public_key | Flag::f_block) : Flag());
_api.request(MTPphone_JoinGroupCall(
MTP_flags(flags),
(_conferenceLinkSlug.isEmpty()
? inputCall()
: MTP_inputGroupCallSlug(
MTP_string(_conferenceLinkSlug))),
joinAs()->input,
MTP_string(_joinHash),
(_e2e ? TdE2E::PublicKeyToMTP(_e2e->myKey()) : MTPint256()),
MTP_bytes(joinBlock),
MTP_int(_conferenceJoinMessageId.bare),
MTP_dataJSON(MTP_bytes(_joinState.payload.json))
)).done([=](
const MTPUpdates &updates,
const MTP::Response &response) {
_serverTimeMs = TimestampInMsFromMsgId(response.outerMsgId);
_serverTimeMsGotAt = crl::now();
_joinState.finish(_joinState.payload.ssrc);
_mySsrcs.emplace(_joinState.ssrc);
setState((_instanceState.current()
== InstanceState::Disconnected)
? State::Connecting
: State::Joined);
applyMeInCallLocally();
maybeSendMutedUpdate(wasMuteState);
_peer->session().api().applyUpdates(updates);
applyQueuedSelfUpdates();
checkFirstTimeJoined();
_screenJoinState.nextActionPending = true;
checkNextJoinAction();
if (wasVideoStopped == isSharingCamera()) {
sendSelfUpdate(SendUpdateType::CameraStopped);
}
if (isCameraPaused()) {
sendSelfUpdate(SendUpdateType::CameraPaused);
}
sendPendingSelfUpdates();
if (!_reloadedStaleCall
&& _state.current() != State::Joining) {
if (const auto real = lookupReal()) {
_reloadedStaleCall = true;
real->reloadIfStale();
}
}
if (_e2e) {
_e2e->joined();
}
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
if (_e2e) {
if (type == u"CONF_WRITE_CHAIN_INVALID"_q) {
refreshLastBlockAndJoin();
return;
}
}
_joinState.finish();
LOG(("Call Error: Could not join, error: %1").arg(type));
if (type == u"GROUPCALL_SSRC_DUPLICATE_MUCH") {
rejoin();
return;
}
hangup();
Ui::Toast::Show((type == u"GROUPCALL_FORBIDDEN"_q)
? tr::lng_group_not_accessible(tr::now)
: Lang::Hard::ServerError());
}).send();
}
void GroupCall::refreshLastBlockAndJoin() {
Expects(_e2e != nullptr);
if (state() != State::Joining) {
_joinState.finish();
checkNextJoinAction();
return;
}
_api.request(MTPphone_GetGroupCallChainBlocks(
inputCall(),
MTP_int(0),
MTP_int(-1),
MTP_int(1)
)).done([=](const MTPUpdates &result) {
if (result.type() != mtpc_updates) {
_joinState.finish();
LOG(("Call Error: Bad result in GroupCallChainBlocks."));
hangup();
Ui::Toast::Show(u"Bad Updates in GroupCallChainBlocks."_q);
return;
}
_e2e->refreshLastBlock0({});
const auto &data = result.c_updates();
for (const auto &update : data.vupdates().v) {
if (update.type() != mtpc_updateGroupCallChainBlocks) {
continue;
}
const auto &data = update.c_updateGroupCallChainBlocks();
const auto &blocks = data.vblocks().v;
if (!blocks.isEmpty()) {
_e2e->refreshLastBlock0(TdE2E::Block{ blocks.back().v });
break;
}
}
sendJoinRequest();
}).fail([=](const MTP::Error &error) {
_joinState.finish();
const auto &type = error.type();
LOG(("Call Error: Could not get last block, error: %1").arg(type));
hangup();
Ui::Toast::Show(error.type());
}).send();
}
void GroupCall::requestSubchainBlocks(int subchain, int height) {
Expects(subchain >= 0 && subchain < kSubChainsCount);

View file

@ -466,9 +466,14 @@ private:
Joining,
Leaving,
};
struct JoinPayload {
uint32 ssrc = 0;
QByteArray json;
};
struct JoinState {
uint32 ssrc = 0;
JoinAction action = JoinAction::None;
JoinPayload payload;
bool nextActionPending = false;
void finish(uint32 updatedSsrc = 0) {
@ -540,6 +545,8 @@ private:
void rejoinPresentation();
void leavePresentation();
void checkNextJoinAction();
void sendJoinRequest();
void refreshLastBlockAndJoin();
void requestSubchainBlocks(int subchain, int height);
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);

View file

@ -8,12 +8,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/group/calls_group_common.h"
#include "base/platform/base_platform_info.h"
#include "boxes/share_box.h"
#include "data/data_group_call.h"
#include "info/bot/starref/info_bot_starref_common.h"
#include "ui/boxes/boost_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "lang/lang_keys.h"
#include "window/window_session_controller.h"
#include "styles/style_layers.h"
#include "styles/style_calls.h"
#include "styles/style_chat.h"
#include <QtWidgets/QApplication>
#include <QtGui/QClipboard>
namespace Calls::Group {
@ -74,4 +85,93 @@ void ConferenceCallJoinConfirm(
});
}
void ShowConferenceCallLinkBox(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::GroupCall> call,
const QString &link,
bool initial) {
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
box->setStyle(st::confcallLinkBox);
box->setWidth(st::boxWideWidth);
box->setNoContentMargin(true);
box->addTopButton(st::boxTitleClose, [=] {
box->closeBox();
});
box->addRow(
Info::BotStarRef::CreateLinkHeaderIcon(box, &call->session()),
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
box,
object_ptr<Ui::FlatLabel>(
box,
tr::lng_confcall_link_title(),
st::boxTitle)),
st::boxRowPadding + st::confcallLinkTitlePadding);
box->addRow(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_confcall_link_about(),
st::confcallLinkCenteredText),
st::boxRowPadding
)->setTryMakeSimilarLines(true);
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 2);
const auto preview = box->addRow(
Info::BotStarRef::MakeLinkLabel(box, link));
Ui::AddSkip(box->verticalLayout());
const auto copyCallback = [=] {
QApplication::clipboard()->setText(link);
box->uiShow()->showToast(tr::lng_username_copied(tr::now));
};
const auto shareCallback = [=] {
FastShareLink(controller, link);
};
preview->setClickedCallback(copyCallback);
[[maybe_unused]] const auto copy = box->addButton(
tr::lng_group_invite_copy(),
copyCallback,
st::confcallLinkCopyButton);
[[maybe_unused]] const auto share = box->addButton(
tr::lng_group_invite_share(),
shareCallback,
st::confcallLinkShareButton);
const auto sep = Ui::CreateChild<Ui::FlatLabel>(
copy->parentWidget(),
tr::lng_confcall_link_or(),
st::confcallLinkFooterOr);
const auto footer = Ui::CreateChild<Ui::FlatLabel>(
copy->parentWidget(),
tr::lng_confcall_link_join(
lt_link,
tr::lng_confcall_link_join_link(
lt_arrow,
rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
[](QString v) { return Ui::Text::Link(v); }),
Ui::Text::WithEntities),
st::confcallLinkCenteredText);
footer->setTryMakeSimilarLines(true);
copy->geometryValue() | rpl::start_with_next([=](QRect geometry) {
const auto width = st::boxWideWidth
- st::boxRowPadding.left()
- st::boxRowPadding.right();
footer->resizeToWidth(width);
const auto &st = box->getDelegate()->style();
const auto top = geometry.y() + geometry.height();
const auto available = st.buttonPadding.bottom();
const auto footerHeight = sep->height() + footer->height();
const auto skip = (available - footerHeight) / 2;
sep->move(
st::boxRowPadding.left() + (width - sep->width()) / 2,
top + skip);
footer->moveToLeft(
st::boxRowPadding.left(),
top + skip + sep->height());
}, footer->lifetime());
}));
}
} // namespace Calls::Group

View file

@ -16,6 +16,7 @@ class GroupCall;
} // namespace Data
namespace Ui {
class Show;
class GenericBox;
} // namespace Ui
@ -23,6 +24,10 @@ namespace TdE2E {
class Call;
} // namespace TdE2E
namespace Window {
class SessionController;
} // namespace Window
namespace Calls::Group {
constexpr auto kDefaultVolume = 10000;
@ -113,4 +118,10 @@ void ConferenceCallJoinConfirm(
std::shared_ptr<Data::GroupCall> call,
Fn<void()> join);
void ShowConferenceCallLinkBox(
not_null<Window::SessionController*> controller,
std::shared_ptr<Data::GroupCall> call,
const QString &link,
bool initial = false);
} // namespace Calls::Group

View file

@ -1001,7 +1001,8 @@ void ChannelData::setGroupCall(
data.vid().v,
data.vaccess_hash().v,
scheduleDate,
rtmp);
rtmp,
false); // conference
owner().registerGroupCall(_call.get());
session().changes().peerUpdated(this, UpdateFlag::GroupCall);
addFlags(Flag::CallActive);

View file

@ -234,7 +234,8 @@ void ChatData::setGroupCall(
data.vid().v,
data.vaccess_hash().v,
scheduleDate,
rtmp);
rtmp,
false); // conference
owner().registerGroupCall(_call.get());
session().changes().peerUpdated(this, UpdateFlag::GroupCall);
addFlags(Flag::CallActive);

View file

@ -62,7 +62,8 @@ GroupCall::GroupCall(
CallId id,
uint64 accessHash,
TimeId scheduleDate,
bool rtmp)
bool rtmp,
bool conference)
: _id(id)
, _accessHash(accessHash)
, _peer(peer)
@ -70,15 +71,26 @@ GroupCall::GroupCall(
, _speakingByActiveFinishTimer([=] { checkFinishSpeakingByActive(); })
, _scheduleDate(scheduleDate)
, _rtmp(rtmp)
, _conference(conference)
, _listenersHidden(rtmp) {
if (_conference) {
session().data().registerGroupCall(this);
}
}
GroupCall::~GroupCall() {
if (_conference) {
session().data().unregisterGroupCall(this);
}
api().request(_unknownParticipantPeersRequestId).cancel();
api().request(_participantsRequestId).cancel();
api().request(_reloadRequestId).cancel();
}
Main::Session &GroupCall::session() const {
return _peer->session();
}
CallId GroupCall::id() const {
return _id;
}

View file

@ -17,6 +17,10 @@ namespace Calls {
struct ParticipantVideoParams;
} // namespace Calls
namespace Main {
class Session;
} // namespace Main
namespace TdE2E {
struct ParticipantState;
struct UserId;
@ -64,9 +68,12 @@ public:
CallId id,
uint64 accessHash,
TimeId scheduleDate,
bool rtmp);
bool rtmp,
bool conference);
~GroupCall();
[[nodiscard]] Main::Session &session() const;
[[nodiscard]] CallId id() const;
[[nodiscard]] bool loaded() const;
[[nodiscard]] bool rtmp() const;
@ -247,13 +254,14 @@ private:
rpl::event_stream<not_null<Participant*>> _participantSpeaking;
rpl::event_stream<> _participantsReloaded;
bool _joinMuted = false;
bool _canChangeJoinMuted = true;
bool _allParticipantsLoaded = false;
bool _joinedToTop = false;
bool _applyingQueuedUpdates = false;
bool _rtmp = false;
bool _listenersHidden = false;
bool _joinMuted : 1 = false;
bool _canChangeJoinMuted : 1 = true;
bool _allParticipantsLoaded : 1 = false;
bool _joinedToTop : 1 = false;
bool _applyingQueuedUpdates : 1 = false;
bool _rtmp : 1 = false;
bool _conference : 1 = false;
bool _listenersHidden : 1 = false;
};

View file

@ -73,7 +73,7 @@ void ConnectStarRef(
[[nodiscard]] object_ptr<Ui::RpWidget> CreateLinkIcon(
not_null<QWidget*> parent,
not_null<UserData*> bot,
not_null<Main::Session*> session,
int users) {
auto result = object_ptr<Ui::RpWidget>(parent);
const auto raw = result.data();
@ -92,7 +92,7 @@ void ConnectStarRef(
const auto inner = QSize(innerSide, innerSide);
const auto state = raw->lifetime().make_state<State>(State{
.icon = ChatHelpers::GenerateLocalTgsSticker(
&bot->session(),
session,
u"starref_link"_q),
});
state->icon->overrideEmojiUsesTextColor(true);
@ -419,7 +419,7 @@ object_ptr<Ui::AbstractButton> MakeLinkLabel(
p.drawText(
QRect(skip, margins.top(), available, font->height),
style::al_top,
font->elided(link, available));
font->elided(text, available));
}, raw->lifetime());
return result;
@ -441,7 +441,7 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
});
box->addRow(
CreateLinkIcon(box, bot, row.state.users),
CreateLinkIcon(box, &bot->session(), row.state.users),
st::boxRowPadding + st::starrefJoinUserpicsPadding);
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
@ -1058,4 +1058,11 @@ ConnectedBots Parse(
return result;
}
object_ptr<Ui::RpWidget> CreateLinkHeaderIcon(
not_null<QWidget*> parent,
not_null<Main::Session*> session,
int usersCount) {
return CreateLinkIcon(parent, session, usersCount);
}
} // namespace Info::BotStarRef

View file

@ -105,4 +105,12 @@ void FinishProgram(
not_null<Main::Session*> session,
const MTPpayments_ConnectedStarRefBots &bots);
[[nodiscard]] object_ptr<Ui::AbstractButton> MakeLinkLabel(
not_null<QWidget*> parent,
const QString &link);
[[nodiscard]] object_ptr<Ui::RpWidget> CreateLinkHeaderIcon(
not_null<QWidget*> parent,
not_null<Main::Session*> session,
int usersCount = 0);
} // namespace Info::BotStarRef

View file

@ -2606,7 +2606,7 @@ phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates;
phone.getGroupCallStreamChannels#1ab21940 call:InputGroupCall = phone.GroupCallStreamChannels;
phone.getGroupCallStreamRtmpUrl#deb3abbf peer:InputPeer revoke:Bool = phone.GroupCallStreamRtmpUrl;
phone.saveCallLog#41248786 peer:InputPhoneCall file:InputFile = Bool;
phone.createConferenceCall#dae2632f public_key:int256 zero_block:bytes = phone.GroupCall;
phone.createConferenceCall#fbcefee6 random_id:int = phone.GroupCall;
phone.deleteConferenceCallParticipant#7b8cc2a3 call:InputGroupCall peer:InputPeer block:bytes = Updates;
phone.sendConferenceCallBroadcast#c6701900 call:InputGroupCall block:bytes = Updates;
phone.inviteConferenceCallParticipant#3e9cf7ee call:InputGroupCall user_id:InputUser = Updates;

View file

@ -57,7 +57,15 @@ PublicKey Call::myKey() const {
return _myKey;
}
Block Call::makeZeroBlock() const {
void Call::refreshLastBlock0(std::optional<Block> block) {
_lastBlock0 = std::move(block);
}
Block Call::makeJoinBlock() {
if (failed()) {
return {};
}
const auto publicKeyView = std::string_view{
reinterpret_cast<const char*>(&_myKey),
sizeof(_myKey),
@ -66,22 +74,48 @@ Block Call::makeZeroBlock() const {
Assert(publicKeyId.is_ok());
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());
const auto myParticipant = tde2e_api::CallParticipant{
.user_id = std::int64_t(_myUserId.v),
.public_key_id = publicKeyId.value(),
.permissions = kPermissionAdd | kPermissionRemove,
};
const auto result = _lastBlock0
? tde2e_api::call_create_self_add_block(
myKeyId,
Slice(_lastBlock0->data),
myParticipant)
: tde2e_api::call_create_zero_block(myKeyId, {
.height = 0,
.participants = { myParticipant },
});
if (!result.is_ok()) {
LOG_AND_FAIL(result.error(), CallFailure::Unknown);
return {};
}
return {
.data = QByteArray::fromStdString(result.value()),
};
}
void Call::create(const Block &last) {
void Call::joined() {
shortPoll(0);
if (_id.v) {
shortPoll(1);
}
}
void Call::apply(const Block &last) {
if (_id.v) {
const auto result = tde2e_api::call_apply_block(
std::int64_t(_id.v),
Slice(last.data));
if (!result.is_ok()) {
LOG_AND_FAIL(result.error(), CallFailure::Unknown);
}
return;
}
const auto id = tde2e_api::call_create(
std::int64_t(_myKeyId.v),
Slice(last.data));
@ -89,6 +123,7 @@ void Call::create(const Block &last) {
LOG_AND_FAIL(id.error(), CallFailure::Unknown);
return;
}
_id = CallId{ uint64(id.value()) };
for (auto i = 0; i != kSubChainsCount; ++i) {
auto &entry = _subchains[i];
@ -98,7 +133,9 @@ void Call::create(const Block &last) {
entry.shortPollTimer.setCallback([=] {
shortPoll(i);
});
entry.shortPollTimer.callOnce(kShortPollChainBlocksTimeout);
if (!entry.waitingTimer.isActive()) {
entry.shortPollTimer.callOnce(kShortPollChainBlocksTimeout);
}
}
}
@ -108,14 +145,12 @@ void Call::apply(
const Block &block,
bool fromShortPoll) {
Expects(subchain >= 0 && subchain < kSubChainsCount);
Expects(_id.v != 0 || !fromShortPoll || !subchain);
if (!subchain && index >= _lastBlock0Height) {
_lastBlock0 = block;
_lastBlock0Height = index;
}
if (!subchain && !_id.v) {
create(block);
}
if (failed()) {
return;
}
@ -123,22 +158,19 @@ void Call::apply(
auto &entry = _subchains[subchain];
if (!fromShortPoll) {
entry.lastUpdate = crl::now();
if (index > entry.height + 1) {
if (index > entry.height || (!_id.v && subchain != 0)) {
entry.waiting.emplace(index, block);
checkWaitingBlocks(subchain);
return;
}
}
const auto result = tde2e_api::call_apply_block(
std::int64_t(_id.v),
Slice(block.data));
if (!result.is_ok()) {
LOG_AND_FAIL(result.error(), CallFailure::Unknown);
if (failed()) {
return;
} else if (!_id.v || entry.height == index) {
apply(block);
}
entry.height = std::max(entry.height, index);
entry.height = index + 1;
checkWaitingBlocks(subchain);
}
@ -150,7 +182,10 @@ void Call::checkWaitingBlocks(int subchain, bool waited) {
}
auto &entry = _subchains[subchain];
if (entry.shortPolling) {
if (!_id.v) {
entry.waitingTimer.callOnce(kShortPollChainBlocksWaitFor);
return;
} else if (entry.shortPolling) {
return;
}
auto &waiting = entry.waiting;
@ -186,6 +221,11 @@ void Call::shortPoll(int subchain) {
auto &entry = _subchains[subchain];
entry.waitingTimer.cancel();
entry.shortPollTimer.cancel();
if (subchain && !_id.v) {
// Not ready.
entry.waitingTimer.callOnce(kShortPollChainBlocksWaitFor);
return;
}
entry.shortPolling = true;
_subchainRequests.fire({ subchain, entry.height });
}
@ -217,10 +257,6 @@ rpl::producer<CallFailure> Call::failures() const {
return _failures.events();
}
const std::optional<Block> &Call::lastBlock0() const {
return _lastBlock0;
}
std::vector<uint8_t> Call::encrypt(const std::vector<uint8_t> &data) const {
const auto result = tde2e_api::call_encrypt(
std::int64_t(_id.v),

View file

@ -55,10 +55,7 @@ public:
[[nodiscard]] PublicKey myKey() const;
[[nodiscard]] Block makeZeroBlock() const;
void create(const Block &last);
void joined();
void apply(
int subchain,
int index,
@ -75,7 +72,8 @@ public:
[[nodiscard]] std::optional<CallFailure> failed() const;
[[nodiscard]] rpl::producer<CallFailure> failures() const;
[[nodiscard]] const std::optional<Block> &lastBlock0() const;
void refreshLastBlock0(std::optional<Block> block);
[[nodiscard]] Block makeJoinBlock();
[[nodiscard]] std::vector<uint8_t> encrypt(
const std::vector<uint8_t> &data) const;
@ -94,6 +92,7 @@ private:
int height = 0;
};
void apply(const Block &last);
void fail(CallFailure reason);
void checkWaitingBlocks(int subchain, bool waited = false);

View file

@ -10,9 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "base/event_filter.h"
#include "base/qt_signal_producer.h"
#include "base/random.h"
#include "boxes/about_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/premium_preview_box.h"
#include "calls/group/calls_group_common.h"
#include "calls/calls_box_controller.h"
#include "calls/calls_instance.h"
#include "core/application.h"
@ -121,18 +123,16 @@ constexpr auto kPlayStatusLimit = 2;
(height - st::inviteViaLinkIcon.height()) / 2);
}, icon->lifetime());
const auto creating = result->lifetime().make_state<bool>();
const auto creating = result->lifetime().make_state<int32>();
result->setClickedCallback([=] {
if (*creating) {
return;
}
*creating = true;
auto e2e = std::make_shared<TdE2E::Call>(
TdE2E::MakeUserId(controller->session().user()));
*creating = base::RandomValue<int32>();
const auto show = controller->uiShow();
const auto session = &controller->session();
session->api().request(MTPphone_CreateConferenceCall(
TdE2E::PublicKeyToMTP(e2e->myKey()),
MTP_bytes(e2e->makeZeroBlock().data)
MTP_int(*creating)
)).done(crl::guard(controller, [=](const MTPphone_GroupCall &result) {
result.data().vcall().match([&](const auto &data) {
const auto call = std::make_shared<Data::GroupCall>(
@ -140,18 +140,32 @@ constexpr auto kPlayStatusLimit = 2;
data.vid().v,
data.vaccess_hash().v,
TimeId(), // scheduleDate
false); // rtmp
false, // rtmp
true); // conference
call->processFullCall(result);
Core::App().calls().startOrJoinConferenceCall(
controller->uiShow(),
{ .call = call, .e2e = e2e });
using Flag = MTPphone_ExportGroupCallInvite::Flag;
session->api().request(MTPphone_ExportGroupCallInvite(
MTP_flags(Flag::f_can_self_unmute),
MTP_inputGroupCall(data.vid(), data.vaccess_hash())
)).done(crl::guard(controller, [=](
const MTPphone_ExportedGroupCallInvite &result) {
const auto link = qs(result.data().vlink());
Calls::Group::ShowConferenceCallLinkBox(
controller,
call,
link,
true);
if (const auto onstack = done) {
onstack();
}
})).fail(crl::guard(controller, [=](const MTP::Error &error) {
show->showToast(error.type());
*creating = 0;
})).send();
});
if (const auto onstack = done) {
onstack();
}
})).fail(crl::guard(controller, [=](const MTP::Error &error) {
controller->uiShow()->showToast(error.type());
*creating = false;
show->showToast(error.type());
*creating = 0;
})).send();
});
return result;

View file

@ -861,7 +861,8 @@ void SessionNavigation::resolveConferenceCall(const QString &slug) {
data.vid().v,
data.vaccess_hash().v,
TimeId(), // scheduleDate
false); // rtmp
false, // rtmp
true); // conference
call->processFullCall(result);
const auto join = [=] {
Core::App().calls().startOrJoinConferenceCall(