Implement multi-inviting to confcalls.

This commit is contained in:
John Preston 2025-03-29 16:28:17 +05:00
parent 6451e1cfe2
commit 9dbd134601
15 changed files with 366 additions and 125 deletions

View file

@ -4765,6 +4765,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_add_to_group_all" = "Those users aren't members of «{group}». Add them to the group?";
"lng_group_call_invite_members" = "Group members";
"lng_group_call_invite_search_results" = "Search results";
"lng_group_call_invite_limit" = "This is currently the maximum allowed number of participants.";
"lng_group_call_new_muted" = "Mute new participants";
"lng_group_call_speakers" = "Speakers";
"lng_group_call_microphone" = "Microphone";
@ -4921,6 +4922,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"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_confcall_invite_done_user" = "You invited {user} to join the call.";
"lng_confcall_invite_done_many#one" = "You invited **{count} person** to join the call.";
"lng_confcall_invite_done_many#other" = "You invited **{count} people** to join the call.";
"lng_confcall_invite_fail_user" = "You cannot call {user} because of their privacy settings.";
"lng_confcall_invite_fail_many#one" = "You cannot call **{count} person** because of their privacy settings.";
"lng_confcall_invite_fail_many#other" = "You cannot call **{count} people** because of their privacy settings.";
"lng_no_mic_permission" = "Telegram needs microphone access so that you can make calls and record voice messages.";

View file

@ -1538,3 +1538,20 @@ groupCallLinkPreview: InputField(defaultInputField) {
style: defaultTextStyle;
heightMin: 35px;
}
groupCallInviteLink: SettingsButton(defaultSettingsButton) {
textFg: mediaviewTextLinkFg;
textFgOver: mediaviewTextLinkFg;
textBg: groupCallMembersBg;
textBgOver: groupCallMembersBgOver;
style: TextStyle(defaultTextStyle) {
font: font(14px semibold);
}
height: 20px;
padding: margins(63px, 8px, 8px, 9px);
ripple: groupCallRipple;
}
groupCallInviteLinkIcon: icon {{ "info/edit/group_manage_links", mediaviewTextLinkFg }};
groupCallInviteLinkIconPosition: point(23px, 2px);

View file

@ -3697,61 +3697,88 @@ void GroupCall::editParticipant(
}).send();
}
std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
const std::vector<not_null<UserData*>> &users) {
void GroupCall::inviteUsers(
const std::vector<not_null<UserData*>> &users,
Fn<void(InviteResult)> done) {
const auto real = lookupReal();
if (!real) {
return 0;
if (done) {
done({});
}
return;
}
const auto owner = &_peer->owner();
if (_conferenceCall) {
struct State {
InviteResult result;
int requests = 0;
};
const auto state = std::make_shared<State>();
const auto finishRequest = [=] {
if (!--state->requests) {
if (done) {
done(std::move(state->result));
}
}
};
if (const auto call = _conferenceCall.get()) {
for (const auto &user : users) {
_api.request(MTPphone_InviteConferenceCallParticipant(
inputCallSafe(),
user->inputUser
)).send();
)).done([=](const MTPUpdates &result) {
owner->registerInvitedToCallUser(_id, call, user);
_peer->session().api().applyUpdates(result);
state->result.invited.push_back(user);
finishRequest();
}).fail([=](const MTP::Error &error) {
const auto type = error.type();
if (type == u"USER_PRIVACY_RESTRICTED"_q) {
state->result.privacyRestricted.push_back(user);
} else if (type == u"USER_ALREADY_PARTICIPANT"_q) {
state->result.alreadyIn.push_back(user);
}
finishRequest();
}).send();
++state->requests;
}
auto result = std::variant<int, not_null<UserData*>>(0);
if (users.size() != 1) {
result = int(users.size());
} else {
result = users.front();
}
return result;
return;
}
auto count = 0;
auto usersSlice = std::vector<not_null<UserData*>>();
usersSlice.reserve(kMaxInvitePerSlice);
auto slice = QVector<MTPInputUser>();
auto result = std::variant<int, not_null<UserData*>>(0);
slice.reserve(kMaxInvitePerSlice);
const auto sendSlice = [&] {
count += slice.size();
_api.request(MTPphone_InviteToGroupCall(
inputCall(),
MTP_vector<MTPInputUser>(slice)
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
for (const auto &user : usersSlice) {
state->result.invited.push_back(user);
}
finishRequest();
}).fail([=](const MTP::Error &error) {
finishRequest();
}).send();
++state->requests;
slice.clear();
usersSlice.clear();
};
for (const auto &user : users) {
if (!count && slice.empty()) {
result = user;
}
owner->registerInvitedToCallUser(_id, _peer, user);
usersSlice.push_back(user);
slice.push_back(user->inputUser);
if (slice.size() == kMaxInvitePerSlice) {
sendSlice();
}
}
if (count != 0 || slice.size() != 1) {
result = int(count + slice.size());
}
if (!slice.empty()) {
sendSlice();
}
return result;
}
auto GroupCall::ensureGlobalShortcutManager()

View file

@ -416,8 +416,15 @@ public:
void toggleMute(const Group::MuteRequest &data);
void changeVolume(const Group::VolumeRequest &data);
std::variant<int, not_null<UserData*>> inviteUsers(
const std::vector<not_null<UserData*>> &users);
struct InviteResult {
std::vector<not_null<UserData*>> invited;
std::vector<not_null<UserData*>> alreadyIn;
std::vector<not_null<UserData*>> privacyRestricted;
};
void inviteUsers(
const std::vector<not_null<UserData*>> &users,
Fn<void(InviteResult)> done);
std::shared_ptr<GlobalShortcutManager> ensureGlobalShortcutManager();
void applyGlobalShortcutChanges();

View file

@ -15,12 +15,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_session.h"
#include "data/data_group_call.h"
#include "info/profile/info_profile_icon.h"
#include "main/main_session.h"
#include "main/session/session_show.h"
#include "ui/text/text_utilities.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h" // membersMarginTop
#include "styles/style_calls.h"
#include "styles/style_dialogs.h" // searchedBarHeight
@ -61,38 +65,139 @@ public:
ConfInviteController(
not_null<Main::Session*> session,
base::flat_set<not_null<UserData*>> alreadyIn,
Fn<void(not_null<PeerData*>)> choose)
: ContactsBoxController(session)
, _alreadyIn(std::move(alreadyIn))
, _choose(std::move(choose)) {
}
Fn<void()> shareLink);
[[nodiscard]] rpl::producer<bool> hasSelectedValue() const;
protected:
std::unique_ptr<PeerListRow> createRow(
not_null<UserData*> user) override {
if (user->isSelf()
|| user->isBot()
|| user->isServiceUser()
|| user->isInaccessible()) {
return nullptr;
}
auto result = ContactsBoxController::createRow(user);
if (_alreadyIn.contains(user)) {
result->setDisabledState(PeerListRow::State::DisabledChecked);
}
return result;
}
void prepareViewHook() override;
void rowClicked(not_null<PeerListRow*> row) override {
_choose(row->peer());
}
std::unique_ptr<PeerListRow> createRow(
not_null<UserData*> user) override;
void rowClicked(not_null<PeerListRow*> row) override;
private:
const base::flat_set<not_null<UserData*>> _alreadyIn;
const Fn<void(not_null<PeerData*>)> _choose;
[[nodiscard]] int fullCount() const;
base::flat_set<not_null<UserData*>> _alreadyIn;
const Fn<void()> _shareLink;
rpl::variable<bool> _hasSelected;
};
ConfInviteController::ConfInviteController(
not_null<Main::Session*> session,
base::flat_set<not_null<UserData*>> alreadyIn,
Fn<void()> shareLink)
: ContactsBoxController(session)
, _alreadyIn(std::move(alreadyIn))
, _shareLink(std::move(shareLink)) {
_alreadyIn.remove(session->user());
}
rpl::producer<bool> ConfInviteController::hasSelectedValue() const {
return _hasSelected.value();
}
std::unique_ptr<PeerListRow> ConfInviteController::createRow(
not_null<UserData*> user) {
if (user->isSelf()
|| user->isBot()
|| user->isServiceUser()
|| user->isInaccessible()) {
return nullptr;
}
auto result = ContactsBoxController::createRow(user);
if (_alreadyIn.contains(user)) {
result->setDisabledState(PeerListRow::State::DisabledChecked);
}
return result;
}
int ConfInviteController::fullCount() const {
return _alreadyIn.size() + delegate()->peerListSelectedRowsCount();
}
void ConfInviteController::rowClicked(not_null<PeerListRow*> row) {
auto count = fullCount();
auto limit = Data::kMaxConferenceMembers;
if (count < limit || row->checked()) {
delegate()->peerListSetRowChecked(row, !row->checked());
_hasSelected = (delegate()->peerListSelectedRowsCount() > 0);
} else {
delegate()->peerListUiShow()->showToast(
tr::lng_group_call_invite_limit(tr::now));
}
}
void ConfInviteController::prepareViewHook() {
auto button = object_ptr<Ui::PaddingWrap<Ui::SettingsButton>>(
nullptr,
object_ptr<Ui::SettingsButton>(
nullptr,
tr::lng_profile_add_via_link(),
st::groupCallInviteLink),
style::margins(0, st::membersMarginTop, 0, 0));
const auto icon = Ui::CreateChild<Info::Profile::FloatingIcon>(
button->entity(),
st::groupCallInviteLinkIcon,
QPoint());
button->entity()->heightValue(
) | rpl::start_with_next([=](int height) {
icon->moveToLeft(
st::groupCallInviteLinkIconPosition.x(),
(height - st::groupCallInviteLinkIcon.height()) / 2);
}, icon->lifetime());
button->entity()->setClickedCallback(_shareLink);
button->entity()->events(
) | rpl::filter([=](not_null<QEvent*> e) {
return (e->type() == QEvent::Enter);
}) | rpl::start_with_next([=] {
delegate()->peerListMouseLeftGeometry();
}, button->lifetime());
delegate()->peerListSetAboveWidget(std::move(button));
}
[[nodiscard]] TextWithEntities ComposeInviteResultToast(
const GroupCall::InviteResult &result) {
auto text = TextWithEntities();
const auto invited = int(result.invited.size());
const auto restricted = int(result.privacyRestricted.size());
if (invited == 1) {
text.append(tr::lng_confcall_invite_done_user(
tr::now,
lt_user,
Ui::Text::Bold(result.invited.front()->shortName()),
Ui::Text::RichLangValue));
} else if (invited > 1) {
text.append(tr::lng_confcall_invite_done_many(
tr::now,
lt_count,
invited,
Ui::Text::RichLangValue));
}
if (invited && restricted) {
text.append(u"\n\n"_q);
}
if (restricted == 1) {
text.append(tr::lng_confcall_invite_fail_user(
tr::now,
lt_user,
Ui::Text::Bold(result.privacyRestricted.front()->shortName()),
Ui::Text::RichLangValue));
} else if (restricted > 1) {
text.append(tr::lng_confcall_invite_fail_many(
tr::now,
lt_count,
restricted,
Ui::Text::RichLangValue));
}
return text;
}
} // namespace
InviteController::InviteController(
@ -204,7 +309,8 @@ std::unique_ptr<PeerListRow> InviteContactsController::createRow(
object_ptr<Ui::BoxContent> PrepareInviteBox(
not_null<GroupCall*> call,
Fn<void(TextWithEntities&&)> showToast) {
Fn<void(TextWithEntities&&)> showToast,
Fn<void(Fn<void(bool)> finished)> shareConferenceLink) {
const auto real = call->lookupReal();
if (!real) {
return nullptr;
@ -220,33 +326,44 @@ object_ptr<Ui::BoxContent> PrepareInviteBox(
alreadyIn.emplace(peer->session().user());
if (call->conference()) {
const auto close = std::make_shared<Fn<void()>>();
const auto invite = [=](not_null<PeerData*> peer) {
Expects(peer->isUser());
const auto call = weak.get();
if (!call) {
return;
}
const auto user = peer->asUser();
call->inviteUsers({ user });
showToast(tr::lng_group_call_invite_done_user(
tr::now,
lt_user,
Ui::Text::Bold(user->firstName),
Ui::Text::WithEntities));
(*close)();
const auto shareLink = [=] {
Assert(shareConferenceLink != nullptr);
shareConferenceLink([=](bool ok) { if (ok) (*close)(); });
};
auto controller = std::make_unique<ConfInviteController>(
&real->session(),
alreadyIn,
invite);
controller->setStyleOverrides(
shareLink);
const auto raw = controller.get();
raw->setStyleOverrides(
&st::groupCallInviteMembersList,
&st::groupCallMultiSelect);
auto initBox = [=](not_null<PeerListBox*> box) {
box->setTitle(tr::lng_group_call_invite_title());
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
*close = [=] { box->closeBox(); };
box->setTitle(tr::lng_group_call_invite_conf());
raw->hasSelectedValue() | rpl::start_with_next([=](bool has) {
box->clearButtons();
if (has) {
box->addButton(tr::lng_group_call_invite_button(), [=] {
const auto call = weak.get();
if (!call) {
return;
}
auto peers = box->collectSelectedRows();
auto users = ranges::views::all(
peers
) | ranges::views::transform([](not_null<PeerData*> peer) {
return not_null(peer->asUser());
}) | ranges::to_vector;
const auto done = [=](GroupCall::InviteResult result) {
(*close)();
showToast({ ComposeInviteResultToast(result) });
};
call->inviteUsers(users, done);
});
}
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}, box->lifetime());
*close = crl::guard(box, [=] { box->closeBox(); });
};
return Box<PeerListBox>(std::move(controller), initBox);
}
@ -270,24 +387,23 @@ object_ptr<Ui::BoxContent> PrepareInviteBox(
if (!call) {
return;
}
const auto result = call->inviteUsers(users);
if (const auto user = std::get_if<not_null<UserData*>>(&result)) {
showToast(tr::lng_group_call_invite_done_user(
tr::now,
lt_user,
Ui::Text::Bold((*user)->firstName),
Ui::Text::WithEntities));
} else if (const auto count = std::get_if<int>(&result)) {
if (*count > 0) {
call->inviteUsers(users, [=](GroupCall::InviteResult result) {
if (result.invited.size() == 1) {
showToast(tr::lng_group_call_invite_done_user(
tr::now,
lt_user,
Ui::Text::Bold(result.invited.front()->firstName),
Ui::Text::WithEntities));
} else if (result.invited.size() > 1) {
showToast(tr::lng_group_call_invite_done_many(
tr::now,
lt_count,
*count,
result.invited.size(),
Ui::Text::RichLangValue));
} else {
Unexpected("Result in GroupCall::inviteUsers.");
}
} else {
Unexpected("Result in GroupCall::inviteUsers.");
}
});
};
const auto inviteWithAdd = [=](
std::shared_ptr<Ui::Show> show,

View file

@ -77,6 +77,7 @@ private:
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareInviteBox(
not_null<GroupCall*> call,
Fn<void(TextWithEntities&&)> showToast);
Fn<void(TextWithEntities&&)> showToast,
Fn<void(Fn<void(bool)> finished)> shareConferenceLink = nullptr);
} // namespace Calls::Group

View file

@ -1742,13 +1742,15 @@ void Members::setMode(PanelMode mode) {
}
QRect Members::getInnerGeometry() const {
const auto shareLink = _shareLinkButton.current();
const auto addMembers = _addMemberButton.current();
const auto share = shareLink ? shareLink->height() : 0;
const auto add = addMembers ? addMembers->height() : 0;
return QRect(
0,
-_scroll->scrollTop(),
width(),
_list->y() + _list->height() + _bottomSkip->height() + add);
_list->y() + _list->height() + _bottomSkip->height() + add + share);
}
rpl::producer<int> Members::fullCountValue() const {
@ -1923,16 +1925,22 @@ void Members::setupFakeRoundCorners() {
const auto bottomleft = create({ 0, shift });
const auto bottomright = create({ shift, shift });
const auto heightValue = [=](Ui::RpWidget *widget) {
topleft->raise();
topright->raise();
bottomleft->raise();
bottomright->raise();
return widget ? widget->heightValue() : rpl::single(0);
};
rpl::combine(
_list->geometryValue(),
_addMemberButton.value() | rpl::map([=](Ui::RpWidget *widget) {
topleft->raise();
topright->raise();
bottomleft->raise();
bottomright->raise();
return widget ? widget->heightValue() : rpl::single(0);
}) | rpl::flatten_latest()
) | rpl::start_with_next([=](QRect list, int addMembers) {
_addMemberButton.value() | rpl::map(
heightValue
) | rpl::flatten_latest(),
_shareLinkButton.value() | rpl::map(
heightValue
) | rpl::flatten_latest()
) | rpl::start_with_next([=](QRect list, int addMembers, int shareLink) {
const auto left = list.x();
const auto top = list.y() - _topSkip->height();
const auto right = left + list.width() - topright->width();
@ -1941,6 +1949,7 @@ void Members::setupFakeRoundCorners() {
+ list.height()
+ _bottomSkip->height()
+ addMembers
+ shareLink
- bottomleft->height();
topleft->move(left, top);
topright->move(right, top);

View file

@ -949,7 +949,9 @@ void Panel::setupMembers() {
_members->addMembersRequests(
) | rpl::start_with_next([=] {
if (!_peer->isBroadcast()
if (_call->conference()) {
addMembers();
} else if (!_peer->isBroadcast()
&& Data::CanSend(_peer, ChatRestriction::SendOther, false)
&& _call->joinAs()->isSelf()) {
addMembers();
@ -960,19 +962,9 @@ void Panel::setupMembers() {
}
}, _callLifetime);
const auto exporting = std::make_shared<bool>();
_members->shareLinkRequests(
) | rpl::start_with_next([=] {
Expects(_call->conference());
if (*exporting) {
return;
}
*exporting = true;
ExportConferenceCallLink(uiShow(), _call->conferenceCall(), {
.st = DarkConferenceCallLinkStyle(),
.finished = [=](bool) { *exporting = false; },
});
) | rpl::start_with_next([cb = shareConferenceLinkCallback()] {
cb(nullptr);
}, _callLifetime);
_call->videoEndpointLargeValue(
@ -984,6 +976,28 @@ void Panel::setupMembers() {
}, _callLifetime);
}
Fn<void(Fn<void(bool)> finished)> Panel::shareConferenceLinkCallback() {
const auto exporting = std::make_shared<bool>();
return [=](Fn<void(bool)> finished) {
Expects(_call->conference());
if (*exporting) {
return;
}
*exporting = true;
const auto done = [=](bool ok) {
*exporting = false;
if (const auto onstack = finished) {
onstack(ok);
}
};
ExportConferenceCallLink(uiShow(), _call->conferenceCall(), {
.finished = done,
.st = DarkConferenceCallLinkStyle(),
});
};
}
void Panel::enlargeVideo() {
_lastSmallGeometry = window()->geometry();
@ -1536,10 +1550,17 @@ void Panel::showMainMenu() {
}
void Panel::addMembers() {
if (_call->conference()
&& _call->conferenceCall()->fullCount() >= Data::kMaxConferenceMembers) {
showToast({ tr::lng_group_call_invite_limit(tr::now) });
}
const auto showToastCallback = [=](TextWithEntities &&text) {
showToast(std::move(text));
};
if (auto box = PrepareInviteBox(_call, showToastCallback)) {
const auto link = _call->conference()
? shareConferenceLinkCallback()
: nullptr;
if (auto box = PrepareInviteBox(_call, showToastCallback, link)) {
showBox(std::move(box));
}
}

View file

@ -193,6 +193,8 @@ private:
void toggleWideControls(bool shown);
void updateWideControlsVisibility();
[[nodiscard]] bool videoButtonInNarrowMode() const;
[[nodiscard]] auto shareConferenceLinkCallback()
-> Fn<void(Fn<void(bool)> finished)>;
void endCall();

View file

@ -27,7 +27,6 @@ constexpr auto kSpeakingAfterActive = crl::time(6000);
constexpr auto kActiveAfterJoined = crl::time(1000);
constexpr auto kWaitForUpdatesTimeout = 3 * crl::time(1000);
constexpr auto kReloadStaleTimeout = 16 * crl::time(1000);
constexpr auto kMaxConferenceMembers = 50;
[[nodiscard]] QString ExtractNextOffset(const MTPphone_GroupCall &call) {
return call.match([&](const MTPDphone_groupCall &data) {
@ -245,7 +244,7 @@ void GroupCall::checkStaleRequest() {
MTP_vector<MTPInputPeer>(), // ids
MTP_vector<MTPint>(), // ssrcs
MTP_string(QString()),
MTP_int(kMaxConferenceMembers)
MTP_int(kMaxConferenceMembers + 10)
)).done([=](const MTPphone_GroupParticipants &result) {
_checkStaleRequestId = 0;
const auto &list = _participantsWithAccess.current();

View file

@ -30,6 +30,8 @@ namespace Data {
[[nodiscard]] const std::string &RtmpEndpointId();
inline constexpr auto kMaxConferenceMembers = 10;
struct LastSpokeTimes {
crl::time anything = 0;
crl::time voice = 0;

View file

@ -1135,6 +1135,30 @@ GroupCall *Session::groupCall(CallId callId) const {
return (i != end(_groupCalls)) ? i->second.get() : nullptr;
}
std::shared_ptr<GroupCall> Session::sharedConferenceCall(
CallId id,
uint64 accessHash) {
const auto i = _conferenceCalls.find(id);
if (i != end(_conferenceCalls)) {
if (auto result = i->second.lock()) {
return result;
}
}
auto result = std::make_shared<GroupCall>(
session().user(),
id,
accessHash,
TimeId(), // scheduledDate
false, // rtmp
true); // conference
if (i != end(_conferenceCalls)) {
i->second = result;
} else {
_conferenceCalls.emplace(id, result);
}
return result;
}
void Session::watchForOffline(not_null<UserData*> user, TimeId now) {
if (!now) {
now = base::unixtime::now();
@ -1205,7 +1229,13 @@ void Session::registerInvitedToCallUser(
CallId callId,
not_null<PeerData*> peer,
not_null<UserData*> user) {
const auto call = peer->groupCall();
registerInvitedToCallUser(callId, peer->groupCall(), user);
}
void Session::registerInvitedToCallUser(
CallId callId,
GroupCall *call,
not_null<UserData*> user) {
if (call && call->id() == callId) {
const auto inCall = ranges::contains(
call->participants(),

View file

@ -230,7 +230,11 @@ public:
void registerGroupCall(not_null<GroupCall*> call);
void unregisterGroupCall(not_null<GroupCall*> call);
GroupCall *groupCall(CallId callId) const;
[[nodiscard]] GroupCall *groupCall(CallId callId) const;
[[nodiscard]] std::shared_ptr<GroupCall> sharedConferenceCall(
CallId id,
uint64 accessHash);
void watchForOffline(not_null<UserData*> user, TimeId now = 0);
void maybeStopWatchForOffline(not_null<UserData*> user);
@ -241,7 +245,13 @@ public:
CallId callId,
not_null<PeerData*> peer,
not_null<UserData*> user);
void unregisterInvitedToCallUser(CallId callId, not_null<UserData*> user);
void registerInvitedToCallUser(
CallId callId,
GroupCall *call,
not_null<UserData*> user);
void unregisterInvitedToCallUser(
CallId callId,
not_null<UserData*> user);
struct InviteToCall {
CallId id = 0;
@ -1096,10 +1106,11 @@ private:
base::flat_set<not_null<ViewElement*>> _heavyViewParts;
base::flat_map<uint64, not_null<GroupCall*>> _groupCalls;
base::flat_map<CallId, not_null<GroupCall*>> _groupCalls;
base::flat_map<CallId, std::weak_ptr<GroupCall>> _conferenceCalls;
rpl::event_stream<InviteToCall> _invitesToCalls;
base::flat_map<
uint64,
CallId,
base::flat_set<not_null<UserData*>>> _invitedToCallUsers;
base::flat_set<not_null<ViewElement*>> _shownSpoilers;

View file

@ -135,13 +135,9 @@ constexpr auto kPlayStatusLimit = 2;
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>(
session->user(),
const auto call = session->data().sharedConferenceCall(
data.vid().v,
data.vaccess_hash().v,
TimeId(), // scheduleDate
false, // rtmp
true); // conference
data.vaccess_hash().v);
call->processFullCall(result);
const auto finished = [=](bool ok) {
if (!ok) {

View file

@ -896,13 +896,9 @@ void SessionNavigation::resolveConferenceCall(
const auto inviteMsgId = base::take(_conferenceCallInviteMsgId);
const auto finished = base::take(_conferenceCallResolveFinished);
result.data().vcall().match([&](const auto &data) {
const auto call = std::make_shared<Data::GroupCall>(
session().user(),
const auto call = session().data().sharedConferenceCall(
data.vid().v,
data.vaccess_hash().v,
TimeId(), // scheduleDate
false, // rtmp
true); // conference
data.vaccess_hash().v);
call->processFullCall(result);
const auto confirmed = std::make_shared<bool>();
const auto join = [=] {