mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-03 21:54:05 +02:00
Update API scheme, something works now.
This commit is contained in:
parent
0e46a4402a
commit
7deb5bfdf2
18 changed files with 464 additions and 157 deletions
|
@ -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.";
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Reference in a new issue