mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-03 21:54:05 +02:00
Implement multi-inviting to confcalls.
This commit is contained in:
parent
6451e1cfe2
commit
9dbd134601
15 changed files with 366 additions and 125 deletions
|
@ -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.";
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 = [=] {
|
||||
|
|
Loading…
Add table
Reference in a new issue