Put prioritized users on top of inactive confcall.

This commit is contained in:
John Preston 2025-04-11 20:23:37 +04:00
parent 844be11d43
commit b583240d58
5 changed files with 284 additions and 36 deletions

View file

@ -649,7 +649,7 @@ createCallListItem: PeerListItem(defaultPeerListItem) {
}
createCallList: PeerList(defaultPeerList) {
item: createCallListItem;
padding: margins(0px, 10px, 0px, 10px);
padding: margins(0px, 6px, 0px, 6px);
}
groupCallRecordingTimerPadding: margins(0px, 4px, 0px, 4px);

View file

@ -112,13 +112,21 @@ private:
};
struct PrioritizedSelector {
object_ptr<Ui::RpWidget> content = { nullptr };
Fn<bool(int, int, int)> overrideKey;
Fn<void(PeerListRowId)> deselect;
Fn<void()> activate;
};
class ConfInviteController final : public ContactsBoxController {
public:
ConfInviteController(
not_null<Main::Session*> session,
ConfInviteStyles st,
base::flat_set<not_null<UserData*>> alreadyIn,
Fn<void()> shareLink);
Fn<void()> shareLink,
std::vector<not_null<UserData*>> prioritize);
[[nodiscard]] rpl::producer<bool> hasSelectedValue() const;
[[nodiscard]] std::vector<InviteRequest> requests(
@ -132,15 +140,27 @@ protected:
void rowClicked(not_null<PeerListRow*> row) override;
void rowElementClicked(not_null<PeerListRow*> row, int element) override;
bool handleDeselectForeignRow(PeerListRowId itemId) override;
bool overrideKeyboardNavigation(
int direction,
int fromIndex,
int toIndex) override;
private:
[[nodiscard]] int fullCount() const;
void toggleRowSelected(not_null<PeerListRow*> row, bool video);
[[nodiscard]] bool toggleRowGetChecked(
not_null<PeerListRow*> row,
bool video);
void addShareLinkButton();
void addPriorityInvites();
const ConfInviteStyles _st;
const base::flat_set<not_null<UserData*>> _alreadyIn;
const std::vector<not_null<UserData*>> _prioritize;
const Fn<void()> _shareLink;
PrioritizedSelector _prioritizeRows;
base::flat_set<not_null<UserData*>> _skip;
rpl::variable<bool> _hasSelected;
base::flat_set<not_null<UserData*>> _withVideo;
bool _lastSelectWithVideo = false;
@ -290,15 +310,160 @@ void ConfInviteRow::elementsPaint(
paintElement(2);
}
[[nodiscard]] PrioritizedSelector PrioritizedInviteSelector(
const ConfInviteStyles &st,
std::vector<not_null<UserData*>> users,
Fn<bool(not_null<PeerListRow*>, bool)> toggleGetChecked,
Fn<bool()> lastSelectWithVideo,
Fn<void(bool)> setLastSelectWithVideo) {
class PrioritizedController final : public PeerListController {
public:
PrioritizedController(
const ConfInviteStyles &st,
std::vector<not_null<UserData*>> users,
Fn<bool(not_null<PeerListRow*>, bool)> toggleGetChecked,
Fn<bool()> lastSelectWithVideo,
Fn<void(bool)> setLastSelectWithVideo)
: _st(st)
, _users(std::move(users))
, _toggleGetChecked(std::move(toggleGetChecked))
, _lastSelectWithVideo(std::move(lastSelectWithVideo))
, _setLastSelectWithVideo(std::move(setLastSelectWithVideo)) {
Expects(!_users.empty());
}
void prepare() override {
for (const auto user : _users) {
delegate()->peerListAppendRow(
std::make_unique<ConfInviteRow>(user, _st));
}
delegate()->peerListRefreshRows();
}
void loadMoreRows() override {
}
void rowClicked(not_null<PeerListRow*> row) override {
toggleRowSelected(row, _lastSelectWithVideo());
}
void rowElementClicked(
not_null<PeerListRow*> row,
int element) override {
if (row->checked()) {
static_cast<ConfInviteRow*>(row.get())->setVideo(
element == 1);
_setLastSelectWithVideo(element == 1);
} else if (element == 1) {
toggleRowSelected(row, true);
} else if (element == 2) {
toggleRowSelected(row, false);
}
}
void toggleRowSelected(not_null<PeerListRow*> row, bool video) {
delegate()->peerListSetRowChecked(
row,
_toggleGetChecked(row, video));
}
Main::Session &session() const override {
return _users.front()->session();
}
void toggleFirst() {
Expects(delegate()->peerListFullRowsCount() > 0);
rowClicked(delegate()->peerListRowAt(0));
}
private:
const ConfInviteStyles &_st;
std::vector<not_null<UserData*>> _users;
Fn<bool(not_null<PeerListRow*>, bool)> _toggleGetChecked;
Fn<bool()> _lastSelectWithVideo;
Fn<void(bool)> _setLastSelectWithVideo;
};
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
const auto container = result.data();
const auto delegate = container->lifetime().make_state<
PeerListContentDelegateSimple
>();
const auto controller = container->lifetime(
).make_state<PrioritizedController>(
st,
users,
toggleGetChecked,
lastSelectWithVideo,
setLastSelectWithVideo);
const auto activate = [=] {
controller->toggleFirst();
};
controller->setStyleOverrides(&st::createCallList);
const auto content = container->add(object_ptr<PeerListContent>(
container,
controller));
delegate->setContent(content);
controller->setDelegate(delegate);
Ui::AddDivider(container);
const auto overrideKey = [=](int direction, int from, int to) {
if (!content->isVisible()) {
return false;
} else if (direction > 0 && from < 0 && to >= 0) {
if (content->hasSelection()) {
const auto was = content->selectedIndex();
const auto now = content->selectSkip(1).reallyMovedTo;
if (was != now) {
return true;
}
content->clearSelection();
} else {
content->selectSkip(1);
return true;
}
} else if (direction < 0 && to < 0) {
if (!content->hasSelection()) {
content->selectLast();
} else if (from >= 0 || content->hasSelection()) {
content->selectSkip(-1);
}
}
return false;
};
const auto deselect = [=](PeerListRowId rowId) {
if (const auto row = delegate->peerListFindRow(rowId)) {
delegate->peerListSetRowChecked(row, false);
}
};
return {
.content = std::move(result),
.overrideKey = overrideKey,
.deselect = deselect,
.activate = activate,
};
}
ConfInviteController::ConfInviteController(
not_null<Main::Session*> session,
ConfInviteStyles st,
base::flat_set<not_null<UserData*>> alreadyIn,
Fn<void()> shareLink)
Fn<void()> shareLink,
std::vector<not_null<UserData*>> prioritize)
: ContactsBoxController(session)
, _st(st)
, _alreadyIn(std::move(alreadyIn))
, _prioritize(std::move(prioritize))
, _shareLink(std::move(shareLink)) {
if (!_shareLink) {
_skip.reserve(_prioritize.size());
for (const auto user : _prioritize) {
_skip.emplace(user);
}
}
}
rpl::producer<bool> ConfInviteController::hasSelectedValue() const {
@ -322,7 +487,8 @@ std::unique_ptr<PeerListRow> ConfInviteController::createRow(
if (user->isSelf()
|| user->isBot()
|| user->isServiceUser()
|| user->isInaccessible()) {
|| user->isInaccessible()
|| _skip.contains(user)) {
return nullptr;
}
auto result = std::make_unique<ConfInviteRow>(user, _st);
@ -358,39 +524,87 @@ void ConfInviteController::rowElementClicked(
}
}
bool ConfInviteController::handleDeselectForeignRow(PeerListRowId itemId) {
if (_prioritizeRows.deselect) {
const auto userId = peerToUser(PeerId(itemId));
if (ranges::contains(_prioritize, session().data().user(userId))) {
_prioritizeRows.deselect(itemId);
return true;
}
}
return false;
}
bool ConfInviteController::overrideKeyboardNavigation(
int direction,
int fromIndex,
int toIndex) {
return _prioritizeRows.overrideKey
&& _prioritizeRows.overrideKey(direction, fromIndex, toIndex);
}
void ConfInviteController::toggleRowSelected(
not_null<PeerListRow*> row,
bool video) {
delegate()->peerListSetRowChecked(row, toggleRowGetChecked(row, video));
// row may have been destroyed here, from search.
_hasSelected = (delegate()->peerListSelectedRowsCount() > 0);
}
bool ConfInviteController::toggleRowGetChecked(
not_null<PeerListRow*> row,
bool video) {
auto count = fullCount();
const auto conferenceLimit = session().appConfig().confcallSizeLimit();
if (count < conferenceLimit || row->checked()) {
const auto real = static_cast<ConfInviteRow*>(row.get());
if (!row->checked()) {
real->setVideo(video);
_lastSelectWithVideo = video;
}
const auto user = row->peer()->asUser();
if (!row->checked() && video) {
_withVideo.emplace(user);
} else {
_withVideo.remove(user);
}
delegate()->peerListSetRowChecked(row, !row->checked());
// row may have been destroyed here, from search.
_hasSelected = (delegate()->peerListSelectedRowsCount() > 0);
} else {
if (!row->checked() && count >= conferenceLimit) {
delegate()->peerListUiShow()->showToast(
tr::lng_group_call_invite_limit(tr::now));
return false;
}
const auto real = static_cast<ConfInviteRow*>(row.get());
if (!row->checked()) {
real->setVideo(video);
_lastSelectWithVideo = video;
}
const auto user = row->peer()->asUser();
if (!row->checked() && video) {
_withVideo.emplace(user);
} else {
_withVideo.remove(user);
}
return !row->checked();
}
void ConfInviteController::prepareViewHook() {
if (_shareLink) {
addShareLinkButton();
} else if (!_prioritize.empty()) {
addPriorityInvites();
}
}
void ConfInviteController::addPriorityInvites() {
const auto toggleGetChecked = [=](not_null<PeerListRow*> row, bool video) {
const auto result = toggleRowGetChecked(row, video);
delegate()->peerListSetForeignRowChecked(
row,
result,
anim::type::normal);
_hasSelected = (delegate()->peerListSelectedRowsCount() > 0);
return result;
};
_prioritizeRows = PrioritizedInviteSelector(
_st,
_prioritize,
toggleGetChecked,
[=] { return _lastSelectWithVideo; },
[=](bool video) { _lastSelectWithVideo = video; });
delegate()->peerListSetAboveWidget(std::move(_prioritizeRows.content));
}
void ConfInviteController::addShareLinkButton() {
auto button = object_ptr<Ui::PaddingWrap<Ui::SettingsButton>>(
nullptr,
@ -571,7 +785,8 @@ object_ptr<Ui::BoxContent> PrepareInviteBox(
&real->session(),
ConfInviteDarkStyles(),
alreadyIn,
shareLink);
shareLink,
std::vector<not_null<UserData*>>());
const auto raw = controller.get();
raw->setStyleOverrides(
&st::groupCallInviteMembersList,
@ -742,7 +957,8 @@ object_ptr<Ui::BoxContent> PrepareInviteBox(
&user->session(),
ConfInviteDarkStyles(),
alreadyIn,
shareLink);
shareLink,
std::vector<not_null<UserData*>>());
const auto raw = controller.get();
raw->setStyleOverrides(
&st::groupCallInviteMembersList,
@ -806,12 +1022,14 @@ void InitReActivate(not_null<PeerListBox*> box) {
object_ptr<Ui::BoxContent> PrepareInviteToEmptyBox(
std::shared_ptr<Data::GroupCall> call,
MsgId inviteMsgId) {
MsgId inviteMsgId,
std::vector<not_null<UserData*>> prioritize) {
auto controller = std::make_unique<ConfInviteController>(
&call->session(),
ConfInviteDefaultStyles(),
base::flat_set<not_null<UserData*>>(),
nullptr);
nullptr,
std::move(prioritize));
const auto raw = controller.get();
raw->setStyleOverrides(&st::createCallList);
const auto initBox = [=](not_null<PeerListBox*> box) {
@ -845,7 +1063,8 @@ object_ptr<Ui::BoxContent> PrepareInviteToEmptyBox(
object_ptr<Ui::BoxContent> PrepareCreateCallBox(
not_null<::Window::SessionController*> window,
Fn<void()> created,
MsgId discardedInviteMsgId) {
MsgId discardedInviteMsgId,
std::vector<not_null<UserData*>> prioritize) {
struct State {
bool creatingLink = false;
QPointer<PeerListBox> box;
@ -877,7 +1096,8 @@ object_ptr<Ui::BoxContent> PrepareCreateCallBox(
&window->session(),
ConfInviteDefaultStyles(),
base::flat_set<not_null<UserData*>>(),
discardedInviteMsgId ? Fn<void()>() : shareLink);
discardedInviteMsgId ? Fn<void()>() : shareLink,
std::move(prioritize));
const auto raw = controller.get();
if (discardedInviteMsgId) {
raw->setStyleOverrides(&st::createCallList);

View file

@ -93,11 +93,13 @@ private:
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareInviteToEmptyBox(
std::shared_ptr<Data::GroupCall> call,
MsgId inviteMsgId);
MsgId inviteMsgId,
std::vector<not_null<UserData*>> prioritize);
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareCreateCallBox(
not_null<::Window::SessionController*> window,
Fn<void()> created = nullptr,
MsgId discardedInviteMsgId = 0);
MsgId discardedInviteMsgId = 0,
std::vector<not_null<UserData*>> prioritize = {});
} // namespace Calls::Group

View file

@ -115,8 +115,6 @@ int AppConfig::confcallSizeLimit() const {
}
bool AppConfig::confcallPrioritizeVP8() const {
AssertIsDebug();
return false;
return get<bool>(u"confcall_use_vp8"_q, false);
}

View file

@ -853,6 +853,30 @@ void SessionNavigation::resolveConferenceCall(
resolveConferenceCall({}, inviteMsgId, contextId);
}
[[nodiscard]] std::vector<not_null<UserData*>> ExtractParticipantsForInvite(
HistoryItem *item) {
if (!item) {
return {};
}
auto result = std::vector<not_null<UserData*>>();
const auto add = [&](not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) {
if (!user->isSelf()
&& !ranges::contains(result, not_null(user))) {
result.push_back(user);
}
}
};
add(item->from());
const auto media = item->media();
if (const auto call = media ? media->call() : nullptr) {
for (const auto &peer : call->otherParticipants) {
add(peer);
}
}
return result;
}
void SessionNavigation::resolveConferenceCall(
QString slug,
MsgId inviteMsgId,
@ -877,6 +901,7 @@ void SessionNavigation::resolveConferenceCall(
const auto slug = base::take(_conferenceCallSlug);
const auto inviteMsgId = base::take(_conferenceCallInviteMsgId);
const auto contextId = base::take(_conferenceCallResolveContextId);
const auto context = session().data().message(contextId);
result.data().vcall().match([&](const MTPDgroupCall &data) {
const auto call = session().data().sharedConferenceCall(
data.vid().v,
@ -896,14 +921,14 @@ void SessionNavigation::resolveConferenceCall(
close();
}
};
const auto context = session().data().message(contextId);
const auto inviter = context
? context->from()->asUser()
: nullptr;
if (inviteMsgId && call->participants().empty()) {
uiShow()->show(Calls::Group::PrepareInviteToEmptyBox(
call,
inviteMsgId));
inviteMsgId,
ExtractParticipantsForInvite(context)));
} else {
uiShow()->show(Box(
Calls::Group::ConferenceCallJoinConfirm,
@ -917,7 +942,8 @@ void SessionNavigation::resolveConferenceCall(
Calls::Group::PrepareCreateCallBox(
parentController(),
nullptr,
inviteMsgId));
inviteMsgId,
ExtractParticipantsForInvite(context)));
} else {
showToast(tr::lng_confcall_link_inactive(tr::now));
}
@ -925,14 +951,16 @@ void SessionNavigation::resolveConferenceCall(
}).fail([=] {
_conferenceCallRequestId = 0;
_conferenceCallSlug = QString();
_conferenceCallResolveContextId = FullMsgId();
const auto contextId = base::take(_conferenceCallResolveContextId);
const auto context = session().data().message(contextId);
const auto inviteMsgId = base::take(_conferenceCallInviteMsgId);
if (inviteMsgId) {
uiShow()->show(
Calls::Group::PrepareCreateCallBox(
parentController(),
nullptr,
inviteMsgId));
inviteMsgId,
ExtractParticipantsForInvite(context)));
} else {
showToast(tr::lng_confcall_link_inactive(tr::now));
}