mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Allow inviting contacts to voice chats.
This commit is contained in:
parent
16c7ec5b05
commit
92bc278052
32 changed files with 989 additions and 230 deletions
|
@ -235,6 +235,8 @@ PRIVATE
|
|||
boxes/peer_list_box.h
|
||||
boxes/peer_list_controllers.cpp
|
||||
boxes/peer_list_controllers.h
|
||||
boxes/peer_lists_box.cpp
|
||||
boxes/peer_lists_box.h
|
||||
boxes/passcode_box.cpp
|
||||
boxes/passcode_box.h
|
||||
boxes/photo_crop_box.cpp
|
||||
|
|
|
@ -1835,6 +1835,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_group_call_invited_status" = "invited";
|
||||
"lng_group_call_invite_title" = "Invite members";
|
||||
"lng_group_call_invite_button" = "Invite";
|
||||
"lng_group_call_add_to_group_one" = "{user} isn't a member of «{group}» yet. Add them to the group?";
|
||||
"lng_group_call_add_to_group_some" = "Some of those users aren't members of «{group}» yet. Add them to the group?";
|
||||
"lng_group_call_add_to_group_all" = "Those users aren't members of «{group}» yet. Add them to the group?";
|
||||
"lng_group_call_invite_members" = "Group members";
|
||||
"lng_group_call_invite_search_results" = "Search results";
|
||||
"lng_group_call_new_muted" = "Mute new members";
|
||||
"lng_group_call_speakers" = "Speakers";
|
||||
"lng_group_call_microphone" = "Microphone";
|
||||
|
|
|
@ -3458,7 +3458,8 @@ void ApiWrap::checkForUnreadMentions(
|
|||
|
||||
void ApiWrap::addChatParticipants(
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<not_null<UserData*>> &users) {
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
Fn<void(bool)> done) {
|
||||
if (const auto chat = peer->asChat()) {
|
||||
for (const auto user : users) {
|
||||
request(MTPmessages_AddChatUser(
|
||||
|
@ -3467,8 +3468,10 @@ void ApiWrap::addChatParticipants(
|
|||
MTP_int(kForwardMessagesOnAdd)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result);
|
||||
if (done) done(true);
|
||||
}).fail([=](const RPCError &error) {
|
||||
ShowAddParticipantsError(error.type(), peer, { 1, user });
|
||||
if (done) done(false);
|
||||
}).afterDelay(crl::time(5)).send();
|
||||
}
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
|
@ -3480,14 +3483,17 @@ void ApiWrap::addChatParticipants(
|
|||
auto list = QVector<MTPInputUser>();
|
||||
list.reserve(qMin(int(users.size()), int(kMaxUsersPerInvite)));
|
||||
const auto send = [&] {
|
||||
const auto callback = base::take(done);
|
||||
request(MTPchannels_InviteToChannel(
|
||||
channel->inputChannel,
|
||||
MTP_vector<MTPInputUser>(list)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result);
|
||||
requestParticipantsCountDelayed(channel);
|
||||
if (callback) callback(true);
|
||||
}).fail([=](const RPCError &error) {
|
||||
ShowAddParticipantsError(error.type(), peer, users);
|
||||
if (callback) callback(false);
|
||||
}).afterDelay(crl::time(5)).send();
|
||||
};
|
||||
for (const auto user : users) {
|
||||
|
|
|
@ -366,7 +366,8 @@ public:
|
|||
Fn<void()> callbackNotModified = nullptr);
|
||||
void addChatParticipants(
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<not_null<UserData*>> &users);
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
Fn<void(bool)> done = nullptr);
|
||||
|
||||
rpl::producer<SendAction> sendActions() const {
|
||||
return _sendActions.events();
|
||||
|
|
|
@ -632,7 +632,7 @@ void GroupInfoBox::submit() {
|
|||
not_null<PeerListBox*> box) {
|
||||
auto create = [box, title, weak] {
|
||||
if (weak) {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
auto rows = box->collectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
weak->createGroup(box, title, rows);
|
||||
}
|
||||
|
@ -643,7 +643,8 @@ void GroupInfoBox::submit() {
|
|||
};
|
||||
Ui::show(
|
||||
Box<PeerListBox>(
|
||||
std::make_unique<AddParticipantsBoxController>(_navigation),
|
||||
std::make_unique<AddParticipantsBoxController>(
|
||||
&_navigation->session()),
|
||||
std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
|
|
@ -33,38 +33,36 @@ namespace {
|
|||
class PrivacyExceptionsBoxController : public ChatsListBoxController {
|
||||
public:
|
||||
PrivacyExceptionsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<QString> title,
|
||||
const std::vector<not_null<PeerData*>> &selected);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
std::vector<not_null<PeerData*>> getResult() const;
|
||||
|
||||
protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
||||
|
||||
private:
|
||||
not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<Main::Session*> _session;
|
||||
rpl::producer<QString> _title;
|
||||
std::vector<not_null<PeerData*>> _selected;
|
||||
|
||||
};
|
||||
|
||||
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<QString> title,
|
||||
const std::vector<not_null<PeerData*>> &selected)
|
||||
: ChatsListBoxController(navigation)
|
||||
, _navigation(navigation)
|
||||
: ChatsListBoxController(session)
|
||||
, _session(session)
|
||||
, _title(std::move(title))
|
||||
, _selected(selected) {
|
||||
}
|
||||
|
||||
Main::Session &PrivacyExceptionsBoxController::session() const {
|
||||
return _navigation->session();
|
||||
return *_session;
|
||||
}
|
||||
|
||||
void PrivacyExceptionsBoxController::prepareViewHook() {
|
||||
|
@ -72,10 +70,6 @@ void PrivacyExceptionsBoxController::prepareViewHook() {
|
|||
delegate()->peerListAddSelectedPeers(_selected);
|
||||
}
|
||||
|
||||
std::vector<not_null<PeerData*>> PrivacyExceptionsBoxController::getResult() const {
|
||||
return delegate()->peerListCollectSelectedRows();
|
||||
}
|
||||
|
||||
void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
const auto peer = row->peer();
|
||||
|
||||
|
@ -146,13 +140,13 @@ void EditPrivacyBox::editExceptions(
|
|||
Exception exception,
|
||||
Fn<void()> done) {
|
||||
auto controller = std::make_unique<PrivacyExceptionsBoxController>(
|
||||
_window,
|
||||
&_window->session(),
|
||||
_controller->exceptionBoxTitle(exception),
|
||||
exceptions(exception));
|
||||
auto initBox = [=, controller = controller.get()](
|
||||
not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_settings_save(), crl::guard(this, [=] {
|
||||
exceptions(exception) = controller->getResult();
|
||||
exceptions(exception) = box->collectSelectedRows();
|
||||
const auto type = [&] {
|
||||
switch (exception) {
|
||||
case Exception::Always: return Exception::Never;
|
||||
|
|
|
@ -320,7 +320,7 @@ void EditExceptions(
|
|||
const auto include = (options & Flag::Contacts) != Flags(0);
|
||||
const auto rules = data->current();
|
||||
auto controller = std::make_unique<EditFilterChatsListController>(
|
||||
window,
|
||||
&window->session(),
|
||||
(include
|
||||
? tr::lng_filters_include_title()
|
||||
: tr::lng_filters_exclude_title()),
|
||||
|
@ -331,7 +331,7 @@ void EditExceptions(
|
|||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->setCloseByOutsideClick(false);
|
||||
box->addButton(tr::lng_settings_save(), crl::guard(context, [=] {
|
||||
const auto peers = box->peerListCollectSelectedRows();
|
||||
const auto peers = box->collectSelectedRows();
|
||||
const auto rules = data->current();
|
||||
auto &&histories = ranges::view::all(
|
||||
peers
|
||||
|
|
|
@ -68,7 +68,6 @@ public:
|
|||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListScrollToTop() override;
|
||||
void peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) override;
|
||||
|
@ -209,11 +208,6 @@ int TypeDelegate::peerListSelectedRowsCount() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto TypeDelegate::peerListCollectSelectedRows()
|
||||
-> std::vector<not_null<PeerData*>> {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TypeDelegate::peerListScrollToTop() {
|
||||
}
|
||||
|
||||
|
@ -347,13 +341,13 @@ void PaintFilterChatsTypeIcon(
|
|||
}
|
||||
|
||||
EditFilterChatsListController::EditFilterChatsListController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<QString> title,
|
||||
Flags options,
|
||||
Flags selected,
|
||||
const base::flat_set<not_null<History*>> &peers)
|
||||
: ChatsListBoxController(navigation)
|
||||
, _navigation(navigation)
|
||||
: ChatsListBoxController(session)
|
||||
, _session(session)
|
||||
, _title(std::move(title))
|
||||
, _peers(peers)
|
||||
, _options(options)
|
||||
|
@ -361,7 +355,7 @@ EditFilterChatsListController::EditFilterChatsListController(
|
|||
}
|
||||
|
||||
Main::Session &EditFilterChatsListController::session() const {
|
||||
return _navigation->session();
|
||||
return *_session;
|
||||
}
|
||||
|
||||
void EditFilterChatsListController::rowClicked(not_null<PeerListRow*> row) {
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
using Flags = Data::ChatFilter::Flags;
|
||||
|
||||
EditFilterChatsListController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<QString> title,
|
||||
Flags options,
|
||||
Flags selected,
|
||||
|
@ -64,7 +64,7 @@ private:
|
|||
|
||||
void updateTitle();
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<Main::Session*> _session;
|
||||
rpl::producer<QString> _title;
|
||||
base::flat_set<not_null<History*>> _peers;
|
||||
Flags _options;
|
||||
|
|
|
@ -399,7 +399,7 @@ int PeerListBox::peerListSelectedRowsCount() {
|
|||
return _select ? _select->entity()->getItemsCount() : 0;
|
||||
}
|
||||
|
||||
auto PeerListBox::peerListCollectSelectedRows()
|
||||
auto PeerListBox::collectSelectedRows()
|
||||
-> std::vector<not_null<PeerData*>> {
|
||||
auto result = std::vector<not_null<PeerData*>>();
|
||||
auto items = _select
|
||||
|
@ -982,6 +982,18 @@ void PeerListContent::setAboveWidget(object_ptr<TWidget> widget) {
|
|||
}
|
||||
}
|
||||
|
||||
void PeerListContent::setAboveSearchWidget(object_ptr<TWidget> widget) {
|
||||
_aboveSearchWidget = std::move(widget);
|
||||
if (_aboveSearchWidget) {
|
||||
_aboveSearchWidget->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListContent::setHideEmpty(bool hide) {
|
||||
_hideEmpty = hide;
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
||||
void PeerListContent::setBelowWidget(object_ptr<TWidget> widget) {
|
||||
_belowWidget = std::move(widget);
|
||||
if (_belowWidget) {
|
||||
|
@ -990,6 +1002,9 @@ void PeerListContent::setBelowWidget(object_ptr<TWidget> widget) {
|
|||
}
|
||||
|
||||
int PeerListContent::labelHeight() const {
|
||||
if (_hideEmpty && !shownRowsCount()) {
|
||||
return 0;
|
||||
}
|
||||
auto computeLabelHeight = [](auto &label) {
|
||||
if (!label) {
|
||||
return 0;
|
||||
|
@ -1082,34 +1097,45 @@ void PeerListContent::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
|
||||
int PeerListContent::resizeGetHeight(int newWidth) {
|
||||
const auto rowsCount = shownRowsCount();
|
||||
const auto hideAll = !rowsCount && _hideEmpty;
|
||||
_aboveHeight = 0;
|
||||
if (_aboveWidget) {
|
||||
_aboveWidget->resizeToWidth(newWidth);
|
||||
_aboveWidget->moveToLeft(0, 0, newWidth);
|
||||
if (showingSearch()) {
|
||||
if (hideAll || showingSearch()) {
|
||||
_aboveWidget->hide();
|
||||
} else {
|
||||
_aboveWidget->show();
|
||||
_aboveHeight = _aboveWidget->height();
|
||||
}
|
||||
}
|
||||
const auto rowsCount = shownRowsCount();
|
||||
if (_aboveSearchWidget) {
|
||||
_aboveSearchWidget->resizeToWidth(newWidth);
|
||||
_aboveSearchWidget->moveToLeft(0, 0, newWidth);
|
||||
if (hideAll || !showingSearch()) {
|
||||
_aboveSearchWidget->hide();
|
||||
} else {
|
||||
_aboveSearchWidget->show();
|
||||
_aboveHeight = _aboveSearchWidget->height();
|
||||
}
|
||||
}
|
||||
const auto labelTop = rowsTop() + qMax(1, shownRowsCount()) * _rowHeight;
|
||||
const auto labelWidth = newWidth - 2 * st::contactsPadding.left();
|
||||
if (_description) {
|
||||
_description->resizeToWidth(labelWidth);
|
||||
_description->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
|
||||
_description->setVisible(!showingSearch());
|
||||
_description->setVisible(!hideAll && !showingSearch());
|
||||
}
|
||||
if (_searchNoResults) {
|
||||
_searchNoResults->resizeToWidth(labelWidth);
|
||||
_searchNoResults->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
|
||||
_searchNoResults->setVisible(showingSearch() && _filterResults.empty() && !_controller->isSearchLoading());
|
||||
_searchNoResults->setVisible(!hideAll && showingSearch() && _filterResults.empty() && !_controller->isSearchLoading());
|
||||
}
|
||||
if (_searchLoading) {
|
||||
_searchLoading->resizeToWidth(labelWidth);
|
||||
_searchLoading->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
|
||||
_searchLoading->setVisible(showingSearch() && _filterResults.empty() && _controller->isSearchLoading());
|
||||
_searchLoading->setVisible(!hideAll && showingSearch() && _filterResults.empty() && _controller->isSearchLoading());
|
||||
}
|
||||
const auto label = labelHeight();
|
||||
const auto belowTop = (label > 0 || rowsCount > 0)
|
||||
|
@ -1119,7 +1145,7 @@ int PeerListContent::resizeGetHeight(int newWidth) {
|
|||
if (_belowWidget) {
|
||||
_belowWidget->resizeToWidth(newWidth);
|
||||
_belowWidget->moveToLeft(0, belowTop, newWidth);
|
||||
if (showingSearch()) {
|
||||
if (hideAll || showingSearch()) {
|
||||
_belowWidget->hide();
|
||||
} else {
|
||||
_belowWidget->show();
|
||||
|
@ -1351,15 +1377,18 @@ crl::time PeerListContent::paintRow(
|
|||
return (refreshStatusAt - ms);
|
||||
}
|
||||
|
||||
void PeerListContent::selectSkip(int direction) {
|
||||
if (_pressed.index.value >= 0) {
|
||||
return;
|
||||
PeerListContent::SkipResult PeerListContent::selectSkip(int direction) {
|
||||
if (hasPressed()) {
|
||||
return { _selected.index.value, _selected.index.value };
|
||||
}
|
||||
_mouseSelection = false;
|
||||
_lastMousePosition = std::nullopt;
|
||||
|
||||
auto newSelectedIndex = _selected.index.value + direction;
|
||||
|
||||
auto result = SkipResult();
|
||||
result.shouldMoveTo = newSelectedIndex;
|
||||
|
||||
auto rowsCount = shownRowsCount();
|
||||
auto index = 0;
|
||||
auto firstEnabled = -1, lastEnabled = -1;
|
||||
|
@ -1415,14 +1444,36 @@ void PeerListContent::selectSkip(int direction) {
|
|||
}
|
||||
|
||||
update();
|
||||
|
||||
_selectedIndex = _selected.index.value;
|
||||
result.reallyMovedTo = _selected.index.value;
|
||||
return result;
|
||||
}
|
||||
|
||||
void PeerListContent::selectSkipPage(int height, int direction) {
|
||||
auto rowsToSkip = height / _rowHeight;
|
||||
if (!rowsToSkip) return;
|
||||
if (!rowsToSkip) {
|
||||
return;
|
||||
}
|
||||
selectSkip(rowsToSkip * direction);
|
||||
}
|
||||
|
||||
rpl::producer<int> PeerListContent::selectedIndexValue() const {
|
||||
return _selectedIndex.value();
|
||||
}
|
||||
|
||||
bool PeerListContent::hasSelection() const {
|
||||
return _selected.index.value >= 0;
|
||||
}
|
||||
|
||||
bool PeerListContent::hasPressed() const {
|
||||
return _pressed.index.value >= 0;
|
||||
}
|
||||
|
||||
void PeerListContent::clearSelection() {
|
||||
setSelected(Selected());
|
||||
}
|
||||
|
||||
void PeerListContent::loadProfilePhotos() {
|
||||
if (_visibleTop >= _visibleBottom) return;
|
||||
|
||||
|
@ -1569,14 +1620,17 @@ void PeerListContent::setSearchQuery(
|
|||
clearSearchRows();
|
||||
}
|
||||
|
||||
void PeerListContent::submitted() {
|
||||
bool PeerListContent::submitted() {
|
||||
if (const auto row = getRow(_selected.index)) {
|
||||
_controller->rowClicked(row);
|
||||
return true;
|
||||
} else if (showingSearch()) {
|
||||
if (const auto row = getRow(RowIndex(0))) {
|
||||
_controller->rowClicked(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PeerListContent::visibleTopBottomUpdated(
|
||||
|
@ -1590,11 +1644,14 @@ void PeerListContent::visibleTopBottomUpdated(
|
|||
|
||||
void PeerListContent::setSelected(Selected selected) {
|
||||
updateRow(_selected.index);
|
||||
if (_selected != selected) {
|
||||
_selected = selected;
|
||||
updateRow(_selected.index);
|
||||
setCursor(_selected.action ? style::cur_pointer : style::cur_default);
|
||||
if (_selected == selected) {
|
||||
return;
|
||||
}
|
||||
_selected = selected;
|
||||
updateRow(_selected.index);
|
||||
setCursor(_selected.action ? style::cur_pointer : style::cur_default);
|
||||
|
||||
_selectedIndex = _selected.index.value;
|
||||
}
|
||||
|
||||
void PeerListContent::setContexted(Selected contexted) {
|
||||
|
|
|
@ -254,10 +254,12 @@ class PeerListDelegate {
|
|||
public:
|
||||
virtual void peerListSetTitle(rpl::producer<QString> title) = 0;
|
||||
virtual void peerListSetAdditionalTitle(rpl::producer<QString> title) = 0;
|
||||
virtual void peerListSetHideEmpty(bool hide) = 0;
|
||||
virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
|
||||
virtual void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) = 0;
|
||||
virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
|
||||
virtual void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetAboveSearchWidget(object_ptr<TWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) = 0;
|
||||
virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0;
|
||||
virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0;
|
||||
|
@ -299,7 +301,6 @@ public:
|
|||
}
|
||||
|
||||
virtual int peerListSelectedRowsCount() = 0;
|
||||
virtual std::vector<not_null<PeerData*>> peerListCollectSelectedRows() = 0;
|
||||
virtual std::unique_ptr<PeerListState> peerListSaveState() const = 0;
|
||||
virtual void peerListRestoreState(
|
||||
std::unique_ptr<PeerListState> state) = 0;
|
||||
|
@ -499,13 +500,20 @@ public:
|
|||
QWidget *parent,
|
||||
not_null<PeerListController*> controller);
|
||||
|
||||
void selectSkip(int direction);
|
||||
struct SkipResult {
|
||||
int shouldMoveTo = 0;
|
||||
int reallyMovedTo = 0;
|
||||
};
|
||||
SkipResult selectSkip(int direction);
|
||||
void selectSkipPage(int height, int direction);
|
||||
|
||||
[[nodiscard]] rpl::producer<int> selectedIndexValue() const;
|
||||
[[nodiscard]] bool hasSelection() const;
|
||||
[[nodiscard]] bool hasPressed() const;
|
||||
void clearSelection();
|
||||
|
||||
void searchQueryChanged(QString query);
|
||||
void submitted();
|
||||
bool submitted();
|
||||
|
||||
// Interface for the controller.
|
||||
void appendRow(std::unique_ptr<PeerListRow> row);
|
||||
|
@ -525,7 +533,9 @@ public:
|
|||
void setSearchLoading(object_ptr<Ui::FlatLabel> loading);
|
||||
void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults);
|
||||
void setAboveWidget(object_ptr<TWidget> widget);
|
||||
void setAboveSearchWidget(object_ptr<TWidget> widget);
|
||||
void setBelowWidget(object_ptr<TWidget> width);
|
||||
void setHideEmpty(bool hide);
|
||||
void refreshRows();
|
||||
|
||||
void setSearchMode(PeerListSearchMode mode);
|
||||
|
@ -667,6 +677,7 @@ private:
|
|||
Selected _selected;
|
||||
Selected _pressed;
|
||||
Selected _contexted;
|
||||
rpl::variable<int> _selectedIndex = -1;
|
||||
bool _mouseSelection = false;
|
||||
std::optional<QPoint> _lastMousePosition;
|
||||
Qt::MouseButton _pressButton = Qt::LeftButton;
|
||||
|
@ -685,7 +696,9 @@ private:
|
|||
|
||||
int _aboveHeight = 0;
|
||||
int _belowHeight = 0;
|
||||
bool _hideEmpty = false;
|
||||
object_ptr<TWidget> _aboveWidget = { nullptr };
|
||||
object_ptr<TWidget> _aboveSearchWidget = { nullptr };
|
||||
object_ptr<TWidget> _belowWidget = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _description = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _searchNoResults = { nullptr };
|
||||
|
@ -703,6 +716,9 @@ public:
|
|||
_content = content;
|
||||
}
|
||||
|
||||
void peerListSetHideEmpty(bool hide) override {
|
||||
_content->setHideEmpty(hide);
|
||||
}
|
||||
void peerListAppendRow(
|
||||
std::unique_ptr<PeerListRow> row) override {
|
||||
_content->appendRow(std::move(row));
|
||||
|
@ -767,6 +783,9 @@ public:
|
|||
void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) override {
|
||||
_content->setAboveWidget(std::move(aboveWidget));
|
||||
}
|
||||
void peerListSetAboveSearchWidget(object_ptr<TWidget> aboveWidget) override {
|
||||
_content->setAboveSearchWidget(std::move(aboveWidget));
|
||||
}
|
||||
void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) override {
|
||||
_content->setBelowWidget(std::move(belowWidget));
|
||||
}
|
||||
|
@ -824,6 +843,8 @@ public:
|
|||
std::unique_ptr<PeerListController> controller,
|
||||
Fn<void(not_null<PeerListBox*>)> init);
|
||||
|
||||
[[nodiscard]] std::vector<not_null<PeerData*>> collectSelectedRows();
|
||||
|
||||
void peerListSetTitle(rpl::producer<QString> title) override {
|
||||
setTitle(std::move(title));
|
||||
}
|
||||
|
@ -840,7 +861,6 @@ public:
|
|||
anim::type animated) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListScrollToTop() override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "history/history.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_session_controller.h" // onShowAddContact()
|
||||
#include "facades.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_profile.h"
|
||||
|
@ -115,7 +115,8 @@ object_ptr<Ui::BoxContent> PrepareContactsBox(
|
|||
[=] { controller->widget()->onShowAddContact(); });
|
||||
};
|
||||
return Box<PeerListBox>(
|
||||
std::make_unique<ContactsBoxController>(controller),
|
||||
std::make_unique<ContactsBoxController>(
|
||||
&sessionController->session()),
|
||||
std::move(delegate));
|
||||
}
|
||||
|
||||
|
@ -159,9 +160,9 @@ void PeerListRowWithLink::paintAction(
|
|||
}
|
||||
|
||||
PeerListGlobalSearchController::PeerListGlobalSearchController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: _navigation(navigation)
|
||||
, _api(&_navigation->session().mtp()) {
|
||||
not_null<Main::Session*> session)
|
||||
: _session(session)
|
||||
, _api(&session->mtp()) {
|
||||
_timer.setCallback([this] { searchOnServer(); });
|
||||
}
|
||||
|
||||
|
@ -210,8 +211,8 @@ void PeerListGlobalSearchController::searchDone(
|
|||
auto &contacts = result.c_contacts_found();
|
||||
auto query = _query;
|
||||
if (requestId) {
|
||||
_navigation->session().data().processUsers(contacts.vusers());
|
||||
_navigation->session().data().processChats(contacts.vchats());
|
||||
_session->data().processUsers(contacts.vusers());
|
||||
_session->data().processChats(contacts.vchats());
|
||||
auto it = _queries.find(requestId);
|
||||
if (it != _queries.cend()) {
|
||||
query = it->second;
|
||||
|
@ -221,7 +222,7 @@ void PeerListGlobalSearchController::searchDone(
|
|||
}
|
||||
const auto feedList = [&](const MTPVector<MTPPeer> &list) {
|
||||
for (const auto &mtpPeer : list.v) {
|
||||
const auto peer = _navigation->session().data().peerLoaded(
|
||||
const auto peer = _session->data().peerLoaded(
|
||||
peerFromMTP(mtpPeer));
|
||||
if (peer) {
|
||||
delegate()->peerListSearchAddRow(peer);
|
||||
|
@ -246,9 +247,9 @@ ChatsListBoxController::Row::Row(not_null<History*> history)
|
|||
}
|
||||
|
||||
ChatsListBoxController::ChatsListBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
not_null<Main::Session*> session)
|
||||
: ChatsListBoxController(
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation)) {
|
||||
std::make_unique<PeerListGlobalSearchController>(session)) {
|
||||
}
|
||||
|
||||
ChatsListBoxController::ChatsListBoxController(
|
||||
|
@ -354,21 +355,21 @@ bool ChatsListBoxController::appendRow(not_null<History*> history) {
|
|||
}
|
||||
|
||||
ContactsBoxController::ContactsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: PeerListController(
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation))
|
||||
, _navigation(navigation) {
|
||||
not_null<Main::Session*> session)
|
||||
: ContactsBoxController(
|
||||
session,
|
||||
std::make_unique<PeerListGlobalSearchController>(session)) {
|
||||
}
|
||||
|
||||
ContactsBoxController::ContactsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
std::unique_ptr<PeerListSearchController> searchController)
|
||||
: PeerListController(std::move(searchController))
|
||||
, _navigation(navigation) {
|
||||
, _session(session) {
|
||||
}
|
||||
|
||||
Main::Session &ContactsBoxController::session() const {
|
||||
return _navigation->session();
|
||||
return *_session;
|
||||
}
|
||||
|
||||
void ContactsBoxController::prepare() {
|
||||
|
@ -435,26 +436,24 @@ bool ContactsBoxController::appendRow(not_null<UserData*> user) {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ContactsBoxController::createRow(not_null<UserData*> user) {
|
||||
std::unique_ptr<PeerListRow> ContactsBoxController::createRow(
|
||||
not_null<UserData*> user) {
|
||||
return std::make_unique<PeerListRow>(user);
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot) {
|
||||
void AddBotToGroupBoxController::Start(not_null<UserData*> bot) {
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_cancel(), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(
|
||||
std::make_unique<AddBotToGroupBoxController>(navigation, bot),
|
||||
std::make_unique<AddBotToGroupBoxController>(bot),
|
||||
std::move(initBox)));
|
||||
}
|
||||
|
||||
AddBotToGroupBoxController::AddBotToGroupBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot)
|
||||
: ChatsListBoxController(SharingBotGame(bot)
|
||||
? std::make_unique<PeerListGlobalSearchController>(navigation)
|
||||
? std::make_unique<PeerListGlobalSearchController>(&bot->session())
|
||||
: nullptr)
|
||||
, _bot(bot) {
|
||||
}
|
||||
|
@ -572,15 +571,15 @@ void AddBotToGroupBoxController::prepareViewHook() {
|
|||
}
|
||||
|
||||
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
FnMut<void(not_null<PeerData*>)> callback)
|
||||
: ChatsListBoxController(navigation)
|
||||
, _navigation(navigation)
|
||||
: ChatsListBoxController(session)
|
||||
, _session(session)
|
||||
, _callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
Main::Session &ChooseRecipientBoxController::session() const {
|
||||
return _navigation->session();
|
||||
return *_session;
|
||||
}
|
||||
|
||||
void ChooseRecipientBoxController::prepareViewHook() {
|
||||
|
|
|
@ -32,7 +32,6 @@ class History;
|
|||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||
|
@ -65,8 +64,7 @@ private:
|
|||
|
||||
class PeerListGlobalSearchController : public PeerListSearchController {
|
||||
public:
|
||||
PeerListGlobalSearchController(
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
explicit PeerListGlobalSearchController(not_null<Main::Session*> session);
|
||||
|
||||
void searchQuery(const QString &query) override;
|
||||
bool isLoading() override;
|
||||
|
@ -79,7 +77,7 @@ private:
|
|||
void searchOnServer();
|
||||
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
base::Timer _timer;
|
||||
QString _query;
|
||||
|
@ -104,7 +102,7 @@ public:
|
|||
|
||||
};
|
||||
|
||||
ChatsListBoxController(not_null<Window::SessionNavigation*> navigation);
|
||||
ChatsListBoxController(not_null<Main::Session*> session);
|
||||
ChatsListBoxController(
|
||||
std::unique_ptr<PeerListSearchController> searchController);
|
||||
|
||||
|
@ -127,15 +125,15 @@ private:
|
|||
|
||||
class ContactsBoxController : public PeerListController {
|
||||
public:
|
||||
explicit ContactsBoxController(not_null<Main::Session*> session);
|
||||
ContactsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
ContactsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
std::unique_ptr<PeerListSearchController> searchController);
|
||||
|
||||
Main::Session &session() const override;
|
||||
[[nodiscard]] Main::Session &session() const override;
|
||||
void prepare() override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(not_null<PeerData*> peer) override final;
|
||||
[[nodiscard]] std::unique_ptr<PeerListRow> createSearchRow(
|
||||
not_null<PeerData*> peer) override final;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
protected:
|
||||
|
@ -150,7 +148,7 @@ private:
|
|||
void checkForEmptyRows();
|
||||
bool appendRow(not_null<UserData*> user);
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
};
|
||||
|
||||
|
@ -158,13 +156,9 @@ class AddBotToGroupBoxController
|
|||
: public ChatsListBoxController
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
static void Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot);
|
||||
static void Start(not_null<UserData*> bot);
|
||||
|
||||
AddBotToGroupBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot);
|
||||
explicit AddBotToGroupBoxController(not_null<UserData*> bot);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
@ -186,7 +180,7 @@ private:
|
|||
void shareBotGame(not_null<PeerData*> chat);
|
||||
void addBotToGroup(not_null<PeerData*> chat);
|
||||
|
||||
not_null<UserData*> _bot;
|
||||
const not_null<UserData*> _bot;
|
||||
|
||||
};
|
||||
|
||||
|
@ -195,7 +189,7 @@ class ChooseRecipientBoxController
|
|||
, public base::has_weak_ptr {
|
||||
public:
|
||||
ChooseRecipientBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<Main::Session*> session,
|
||||
FnMut<void(not_null<PeerData*>)> callback);
|
||||
|
||||
Main::Session &session() const override;
|
||||
|
@ -210,7 +204,7 @@ protected:
|
|||
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
||||
|
||||
private:
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<Main::Session*> _session;
|
||||
FnMut<void(not_null<PeerData*>)> _callback;
|
||||
|
||||
};
|
||||
|
|
429
Telegram/SourceFiles/boxes/peer_lists_box.cpp
Normal file
429
Telegram/SourceFiles/boxes/peer_lists_box.cpp
Normal file
|
@ -0,0 +1,429 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/peer_lists_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
PeerListsBox::PeerListsBox(
|
||||
QWidget*,
|
||||
std::vector<std::unique_ptr<PeerListController>> controllers,
|
||||
Fn<void(not_null<PeerListsBox*>)> init)
|
||||
: _lists(makeLists(std::move(controllers)))
|
||||
, _init(std::move(init)) {
|
||||
Expects(!_lists.empty());
|
||||
}
|
||||
|
||||
auto PeerListsBox::collectSelectedRows()
|
||||
-> std::vector<not_null<PeerData*>> {
|
||||
auto result = std::vector<not_null<PeerData*>>();
|
||||
auto items = _select
|
||||
? _select->entity()->getItems()
|
||||
: QVector<uint64>();
|
||||
if (!items.empty()) {
|
||||
result.reserve(items.size());
|
||||
const auto session = &firstController()->session();
|
||||
for (const auto itemId : items) {
|
||||
const auto foreign = [&] {
|
||||
for (const auto &list : _lists) {
|
||||
if (list.controller->isForeignRow(itemId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
if (!foreign) {
|
||||
result.push_back(session->data().peer(itemId));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PeerListsBox::List PeerListsBox::makeList(
|
||||
std::unique_ptr<PeerListController> controller) {
|
||||
auto delegate = std::make_unique<Delegate>(this, controller.get());
|
||||
return {
|
||||
std::move(controller),
|
||||
std::move(delegate),
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<PeerListsBox::List> PeerListsBox::makeLists(
|
||||
std::vector<std::unique_ptr<PeerListController>> controllers) {
|
||||
auto result = std::vector<List>();
|
||||
result.reserve(controllers.size());
|
||||
for (auto &controller : controllers) {
|
||||
result.push_back(makeList(std::move(controller)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
not_null<PeerListController*> PeerListsBox::firstController() const {
|
||||
return _lists.front().controller.get();
|
||||
}
|
||||
|
||||
void PeerListsBox::createMultiSelect() {
|
||||
Expects(_select == nullptr);
|
||||
|
||||
auto entity = object_ptr<Ui::MultiSelect>(
|
||||
this,
|
||||
(firstController()->selectSt()
|
||||
? *firstController()->selectSt()
|
||||
: st::defaultMultiSelect),
|
||||
tr::lng_participant_filter());
|
||||
_select.create(this, std::move(entity));
|
||||
_select->heightValue(
|
||||
) | rpl::start_with_next(
|
||||
[this] { updateScrollSkips(); },
|
||||
lifetime());
|
||||
_select->entity()->setSubmittedCallback([=](Qt::KeyboardModifiers) {
|
||||
for (const auto &list : _lists) {
|
||||
if (list.content->submitted()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
_select->entity()->setQueryChangedCallback([=](const QString &query) {
|
||||
searchQueryChanged(query);
|
||||
});
|
||||
_select->entity()->setItemRemovedCallback([=](uint64 itemId) {
|
||||
for (const auto &list : _lists) {
|
||||
if (list.controller->handleDeselectForeignRow(itemId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto session = &firstController()->session();
|
||||
if (const auto peer = session->data().peerLoaded(itemId)) {
|
||||
const auto id = peer->id;
|
||||
for (const auto &list : _lists) {
|
||||
if (const auto row = list.delegate->peerListFindRow(id)) {
|
||||
list.content->changeCheckState(
|
||||
row,
|
||||
false,
|
||||
anim::type::normal);
|
||||
update();
|
||||
}
|
||||
list.controller->itemDeselectedHook(peer);
|
||||
}
|
||||
}
|
||||
});
|
||||
_select->resizeToWidth(firstController()->contentWidth());
|
||||
_select->moveToLeft(0, 0);
|
||||
}
|
||||
|
||||
int PeerListsBox::getTopScrollSkip() const {
|
||||
auto result = 0;
|
||||
if (_select && !_select->isHidden()) {
|
||||
result += _select->height();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void PeerListsBox::updateScrollSkips() {
|
||||
// If we show / hide the search field scroll top is fixed.
|
||||
// If we resize search field by bubbles scroll bottom is fixed.
|
||||
setInnerTopSkip(getTopScrollSkip(), _scrollBottomFixed);
|
||||
if (!_select->animating()) {
|
||||
_scrollBottomFixed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::prepare() {
|
||||
auto rows = setInnerWidget(
|
||||
object_ptr<Ui::VerticalLayout>(this),
|
||||
st::boxScroll);
|
||||
for (auto &list : _lists) {
|
||||
const auto content = rows->add(object_ptr<PeerListContent>(
|
||||
rows,
|
||||
list.controller.get()));
|
||||
list.content = content;
|
||||
list.delegate->setContent(content);
|
||||
list.controller->setDelegate(list.delegate.get());
|
||||
|
||||
content->scrollToRequests(
|
||||
) | rpl::start_with_next([=](Ui::ScrollToRequest request) {
|
||||
const auto skip = content->y();
|
||||
onScrollToY(
|
||||
skip + request.ymin,
|
||||
(request.ymax >= 0) ? (skip + request.ymax) : request.ymax);
|
||||
}, lifetime());
|
||||
|
||||
content->selectedIndexValue(
|
||||
) | rpl::filter([=](int index) {
|
||||
return (index >= 0);
|
||||
}) | rpl::start_with_next([=] {
|
||||
for (const auto &list : _lists) {
|
||||
if (list.content && list.content != content) {
|
||||
list.content->clearSelection();
|
||||
}
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
rows->resizeToWidth(firstController()->contentWidth());
|
||||
|
||||
setDimensions(firstController()->contentWidth(), st::boxMaxListHeight);
|
||||
if (_select) {
|
||||
_select->finishAnimating();
|
||||
Ui::SendPendingMoveResizeEvents(_select);
|
||||
_scrollBottomFixed = true;
|
||||
onScrollToY(0);
|
||||
}
|
||||
|
||||
if (_init) {
|
||||
_init(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::keyPressEvent(QKeyEvent *e) {
|
||||
const auto skipRows = [&](int rows) {
|
||||
if (rows == 0) {
|
||||
return;
|
||||
}
|
||||
for (const auto &list : _lists) {
|
||||
if (list.content->hasPressed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto from = begin(_lists), till = end(_lists);
|
||||
auto i = from;
|
||||
for (; i != till; ++i) {
|
||||
if (i->content->hasSelection()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == till && rows < 0) {
|
||||
return;
|
||||
}
|
||||
if (rows > 0) {
|
||||
if (i == till) {
|
||||
i = from;
|
||||
}
|
||||
for (; i != till; ++i) {
|
||||
const auto result = i->content->selectSkip(rows);
|
||||
if (result.shouldMoveTo - result.reallyMovedTo >= rows) {
|
||||
continue;
|
||||
} else if (result.reallyMovedTo >= result.shouldMoveTo) {
|
||||
return;
|
||||
} else {
|
||||
rows = result.shouldMoveTo - result.reallyMovedTo;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (++i; i != from;) {
|
||||
const auto result = (--i)->content->selectSkip(rows);
|
||||
if (result.shouldMoveTo - result.reallyMovedTo <= rows) {
|
||||
continue;
|
||||
} else if (result.reallyMovedTo <= result.shouldMoveTo) {
|
||||
return;
|
||||
} else {
|
||||
rows = result.shouldMoveTo - result.reallyMovedTo;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto rowsInPage = [&] {
|
||||
const auto rowHeight = firstController()->computeListSt().item.height;
|
||||
return height() / rowHeight;
|
||||
};
|
||||
if (e->key() == Qt::Key_Down) {
|
||||
skipRows(1);
|
||||
} else if (e->key() == Qt::Key_Up) {
|
||||
skipRows(-1);
|
||||
} else if (e->key() == Qt::Key_PageDown) {
|
||||
skipRows(rowsInPage());
|
||||
} else if (e->key() == Qt::Key_PageUp) {
|
||||
skipRows(-rowsInPage());
|
||||
} else if (e->key() == Qt::Key_Escape && _select && !_select->entity()->getQuery().isEmpty()) {
|
||||
_select->entity()->clearQuery();
|
||||
} else {
|
||||
BoxContent::keyPressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::searchQueryChanged(const QString &query) {
|
||||
onScrollToY(0);
|
||||
for (const auto &list : _lists) {
|
||||
list.content->searchQueryChanged(query);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::resizeEvent(QResizeEvent *e) {
|
||||
BoxContent::resizeEvent(e);
|
||||
|
||||
if (_select) {
|
||||
_select->resizeToWidth(width());
|
||||
_select->moveToLeft(0, 0);
|
||||
|
||||
updateScrollSkips();
|
||||
}
|
||||
|
||||
for (const auto &list : _lists) {
|
||||
list.content->resizeToWidth(width());
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto &bg = (firstController()->listSt()
|
||||
? *firstController()->listSt()
|
||||
: st::peerListBox).bg;
|
||||
for (const auto rect : e->region()) {
|
||||
p.fillRect(rect, bg);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::setInnerFocus() {
|
||||
if (!_select || !_select->toggled()) {
|
||||
_lists.front().content->setFocus();
|
||||
} else {
|
||||
_select->entity()->setInnerFocus();
|
||||
}
|
||||
}
|
||||
|
||||
PeerListsBox::Delegate::Delegate(
|
||||
not_null<PeerListsBox*> box,
|
||||
not_null<PeerListController*> controller)
|
||||
: _box(box)
|
||||
, _controller(controller) {
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListSetTitle(rpl::producer<QString> title) {
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListSetAdditionalTitle(
|
||||
rpl::producer<QString> title) {
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListSetRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked) {
|
||||
if (checked) {
|
||||
_box->addSelectItem(row, anim::type::normal);
|
||||
PeerListContentDelegate::peerListSetRowChecked(row, checked);
|
||||
peerListUpdateRow(row);
|
||||
|
||||
// This call deletes row from _searchRows.
|
||||
_box->_select->entity()->clearQuery();
|
||||
} else {
|
||||
// The itemRemovedCallback will call changeCheckState() here.
|
||||
_box->_select->entity()->removeItem(row->id());
|
||||
peerListUpdateRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListSetForeignRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked,
|
||||
anim::type animated) {
|
||||
if (checked) {
|
||||
_box->addSelectItem(row, animated);
|
||||
|
||||
// This call deletes row from _searchRows.
|
||||
_box->_select->entity()->clearQuery();
|
||||
} else {
|
||||
// The itemRemovedCallback will call changeCheckState() here.
|
||||
_box->_select->entity()->removeItem(row->id());
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListScrollToTop() {
|
||||
_box->onScrollToY(0);
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListSetSearchMode(PeerListSearchMode mode) {
|
||||
PeerListContentDelegate::peerListSetSearchMode(mode);
|
||||
_box->setSearchMode(mode);
|
||||
}
|
||||
|
||||
void PeerListsBox::setSearchMode(PeerListSearchMode mode) {
|
||||
auto selectVisible = (mode != PeerListSearchMode::Disabled);
|
||||
if (selectVisible && !_select) {
|
||||
createMultiSelect();
|
||||
_select->toggle(!selectVisible, anim::type::instant);
|
||||
}
|
||||
if (_select) {
|
||||
_select->toggle(selectVisible, anim::type::normal);
|
||||
_scrollBottomFixed = false;
|
||||
setInnerFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListsBox::Delegate::peerListFinishSelectedRowsBunch() {
|
||||
Expects(_box->_select != nullptr);
|
||||
|
||||
_box->_select->entity()->finishItemsBunch();
|
||||
}
|
||||
|
||||
bool PeerListsBox::Delegate::peerListIsRowChecked(
|
||||
not_null<PeerListRow*> row) {
|
||||
return _box->_select
|
||||
? _box->_select->entity()->hasItem(row->id())
|
||||
: false;
|
||||
}
|
||||
|
||||
int PeerListsBox::Delegate::peerListSelectedRowsCount() {
|
||||
return _box->_select ? _box->_select->entity()->getItemsCount() : 0;
|
||||
}
|
||||
|
||||
void PeerListsBox::addSelectItem(
|
||||
not_null<PeerData*> peer,
|
||||
anim::type animated) {
|
||||
addSelectItem(
|
||||
peer->id,
|
||||
peer->shortName(),
|
||||
PaintUserpicCallback(peer, false),
|
||||
animated);
|
||||
}
|
||||
|
||||
void PeerListsBox::addSelectItem(
|
||||
not_null<PeerListRow*> row,
|
||||
anim::type animated) {
|
||||
addSelectItem(
|
||||
row->id(),
|
||||
row->generateShortName(),
|
||||
row->generatePaintUserpicCallback(),
|
||||
animated);
|
||||
}
|
||||
|
||||
void PeerListsBox::addSelectItem(
|
||||
uint64 itemId,
|
||||
const QString &text,
|
||||
Ui::MultiSelect::PaintRoundImage paintUserpic,
|
||||
anim::type animated) {
|
||||
if (!_select) {
|
||||
createMultiSelect();
|
||||
_select->hide(anim::type::instant);
|
||||
}
|
||||
const auto &activeBg = (firstController()->selectSt()
|
||||
? *firstController()->selectSt()
|
||||
: st::defaultMultiSelect).item.textActiveBg;
|
||||
if (animated == anim::type::instant) {
|
||||
_select->entity()->addItemInBunch(
|
||||
itemId,
|
||||
text,
|
||||
activeBg,
|
||||
std::move(paintUserpic));
|
||||
} else {
|
||||
_select->entity()->addItem(
|
||||
itemId,
|
||||
text,
|
||||
activeBg,
|
||||
std::move(paintUserpic));
|
||||
}
|
||||
}
|
101
Telegram/SourceFiles/boxes/peer_lists_box.h
Normal file
101
Telegram/SourceFiles/boxes/peer_lists_box.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/peer_list_box.h"
|
||||
|
||||
class PeerListsBox : public Ui::BoxContent {
|
||||
public:
|
||||
PeerListsBox(
|
||||
QWidget*,
|
||||
std::vector<std::unique_ptr<PeerListController>> controllers,
|
||||
Fn<void(not_null<PeerListsBox*>)> init);
|
||||
|
||||
[[nodiscard]] std::vector<not_null<PeerData*>> collectSelectedRows();
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
class Delegate final : public PeerListContentDelegate {
|
||||
public:
|
||||
Delegate(
|
||||
not_null<PeerListsBox*> box,
|
||||
not_null<PeerListController*> controller);
|
||||
|
||||
void peerListSetTitle(rpl::producer<QString> title) override;
|
||||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
void peerListSetSearchMode(PeerListSearchMode mode) override;
|
||||
void peerListSetRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked) override;
|
||||
void peerListSetForeignRowChecked(
|
||||
not_null<PeerListRow*> row,
|
||||
bool checked,
|
||||
anim::type animated) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
void peerListScrollToTop() override;
|
||||
|
||||
void peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) override {
|
||||
_box->addSelectItem(peer, anim::type::instant);
|
||||
}
|
||||
void peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) override {
|
||||
_box->addSelectItem(row, anim::type::instant);
|
||||
}
|
||||
void peerListFinishSelectedRowsBunch() override;
|
||||
|
||||
private:
|
||||
const not_null<PeerListsBox*> _box;
|
||||
const not_null<PeerListController*> _controller;
|
||||
|
||||
};
|
||||
struct List {
|
||||
std::unique_ptr<PeerListController> controller;
|
||||
std::unique_ptr<Delegate> delegate;
|
||||
PeerListContent *content = nullptr;
|
||||
};
|
||||
|
||||
friend class Delegate;
|
||||
|
||||
[[nodiscard]] List makeList(
|
||||
std::unique_ptr<PeerListController> controller);
|
||||
[[nodiscard]] std::vector<List> makeLists(
|
||||
std::vector<std::unique_ptr<PeerListController>> controllers);
|
||||
|
||||
[[nodiscard]] not_null<PeerListController*> firstController() const;
|
||||
|
||||
void addSelectItem(
|
||||
not_null<PeerData*> peer,
|
||||
anim::type animated);
|
||||
void addSelectItem(
|
||||
not_null<PeerListRow*> row,
|
||||
anim::type animated);
|
||||
void addSelectItem(
|
||||
uint64 itemId,
|
||||
const QString &text,
|
||||
PaintRoundImageCallback paintUserpic,
|
||||
anim::type animated);
|
||||
void setSearchMode(PeerListSearchMode mode);
|
||||
void createMultiSelect();
|
||||
int getTopScrollSkip() const;
|
||||
void updateScrollSkips();
|
||||
void searchQueryChanged(const QString &query);
|
||||
|
||||
object_ptr<Ui::SlideWrap<Ui::MultiSelect>> _select = { nullptr };
|
||||
|
||||
std::vector<List> _lists;
|
||||
Fn<void(PeerListsBox*)> _init;
|
||||
bool _scrollBottomFixed = false;
|
||||
|
||||
};
|
|
@ -51,28 +51,21 @@ base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
|
|||
} // namespace
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: ContactsBoxController(
|
||||
navigation,
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation)) {
|
||||
not_null<Main::Session*> session)
|
||||
: ContactsBoxController(session) {
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer)
|
||||
: AddParticipantsBoxController(
|
||||
navigation,
|
||||
peer,
|
||||
GetAlreadyInFromPeer(peer)) {
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn)
|
||||
: ContactsBoxController(
|
||||
navigation,
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation))
|
||||
: ContactsBoxController(&peer->session())
|
||||
, _peer(peer)
|
||||
, _alreadyIn(std::move(alreadyIn)) {
|
||||
subscribeToMigration();
|
||||
|
@ -179,7 +172,7 @@ bool AddParticipantsBoxController::inviteSelectedUsers(
|
|||
not_null<PeerListBox*> box) const {
|
||||
Expects(_peer != nullptr);
|
||||
|
||||
const auto rows = box->peerListCollectSelectedRows();
|
||||
const auto rows = box->collectSelectedRows();
|
||||
const auto users = ranges::view::all(
|
||||
rows
|
||||
) | ranges::view::transform([](not_null<PeerData*> peer) {
|
||||
|
@ -198,9 +191,7 @@ bool AddParticipantsBoxController::inviteSelectedUsers(
|
|||
void AddParticipantsBoxController::Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChatData*> chat) {
|
||||
auto controller = std::make_unique<AddParticipantsBoxController>(
|
||||
navigation,
|
||||
chat);
|
||||
auto controller = std::make_unique<AddParticipantsBoxController>(chat);
|
||||
const auto weak = controller.get();
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_participant_invite(), [=] {
|
||||
|
@ -223,7 +214,6 @@ void AddParticipantsBoxController::Start(
|
|||
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated) {
|
||||
auto controller = std::make_unique<AddParticipantsBoxController>(
|
||||
navigation,
|
||||
channel,
|
||||
std::move(alreadyIn));
|
||||
const auto weak = controller.get();
|
||||
|
|
|
@ -27,16 +27,16 @@ public:
|
|||
not_null<ChannelData*> channel,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
explicit AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
explicit AddParticipantsBoxController(not_null<Main::Session*> session);
|
||||
explicit AddParticipantsBoxController(not_null<PeerData*> peer);
|
||||
AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer);
|
||||
AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
[[nodiscard]] not_null<PeerData*> peer() const {
|
||||
return _peer;
|
||||
}
|
||||
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
void itemDeselectedHook(not_null<PeerData*> peer) override;
|
||||
|
||||
|
|
|
@ -477,11 +477,17 @@ groupCallMembersListItem: PeerListItem(defaultPeerListItem) {
|
|||
groupCallMembersList: PeerList(defaultPeerList) {
|
||||
bg: groupCallMembersBg;
|
||||
about: FlatLabel(defaultPeerListAbout) {
|
||||
textFg: groupCallMemberInactiveStatus;
|
||||
textFg: groupCallMemberNotJoinedStatus;
|
||||
}
|
||||
item: groupCallMembersListItem;
|
||||
}
|
||||
groupCallInviteDividerLabel: FlatLabel(defaultFlatLabel) {
|
||||
textFg: groupCallMemberNotJoinedStatus;
|
||||
}
|
||||
groupCallInviteDividerPadding: margins(17px, 7px, 17px, 7px);
|
||||
|
||||
groupCallInviteMembersList: PeerList(groupCallMembersList) {
|
||||
padding: margins(0px, 10px, 0px, 10px);
|
||||
item: PeerListItem(groupCallMembersListItem) {
|
||||
statusFg: groupCallMemberNotJoinedStatus;
|
||||
statusFgOver: groupCallMemberNotJoinedStatus;
|
||||
|
|
|
@ -1410,6 +1410,9 @@ void GroupMembers::peerListSetTitle(rpl::producer<QString> title) {
|
|||
void GroupMembers::peerListSetAdditionalTitle(rpl::producer<QString> title) {
|
||||
}
|
||||
|
||||
void GroupMembers::peerListSetHideEmpty(bool hide) {
|
||||
}
|
||||
|
||||
bool GroupMembers::peerListIsRowChecked(not_null<PeerListRow*> row) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1421,10 +1424,6 @@ int GroupMembers::peerListSelectedRowsCount() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::vector<not_null<PeerData*>> GroupMembers::peerListCollectSelectedRows() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void GroupMembers::peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) {
|
||||
Unexpected("Item selection in Calls::GroupMembers.");
|
||||
}
|
||||
|
|
|
@ -57,10 +57,10 @@ private:
|
|||
// PeerListContentDelegate interface.
|
||||
void peerListSetTitle(rpl::producer<QString> title) override;
|
||||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
void peerListSetHideEmpty(bool hide) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
void peerListScrollToTop() override;
|
||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) override;
|
||||
void peerListAddSelectedRowInBunch(
|
||||
|
|
|
@ -29,6 +29,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
#include "boxes/peer_lists_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "app.h"
|
||||
#include "apiwrap.h" // api().kickParticipant.
|
||||
#include "styles/style_calls.h"
|
||||
|
@ -51,8 +54,7 @@ class InviteController final : public ParticipantsBoxController {
|
|||
public:
|
||||
InviteController(
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> alreadyIn,
|
||||
int fullInCount);
|
||||
base::flat_set<not_null<UserData*>> alreadyIn);
|
||||
|
||||
void prepare() override;
|
||||
|
||||
|
@ -63,34 +65,81 @@ public:
|
|||
|
||||
void itemDeselectedHook(not_null<PeerData*> peer) override;
|
||||
|
||||
std::variant<int, not_null<UserData*>> inviteSelectedUsers(
|
||||
not_null<PeerListBox*> box,
|
||||
not_null<GroupCall*> call) const;
|
||||
[[nodiscard]] auto peersWithRows() const
|
||||
-> not_null<const base::flat_set<not_null<UserData*>>*>;
|
||||
[[nodiscard]] rpl::producer<not_null<UserData*>> rowAdded() const;
|
||||
|
||||
[[nodiscard]] bool hasRowFor(not_null<PeerData*> peer) const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] int alreadyInCount() const;
|
||||
[[nodiscard]] bool isAlreadyIn(not_null<UserData*> user) const;
|
||||
[[nodiscard]] int fullCount() const;
|
||||
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) const override;
|
||||
|
||||
not_null<PeerData*> _peer;
|
||||
const base::flat_set<not_null<UserData*>> _alreadyIn;
|
||||
const int _fullInCount = 0;
|
||||
mutable base::flat_set<not_null<UserData*>> _skippedUsers;
|
||||
mutable base::flat_set<not_null<UserData*>> _inGroup;
|
||||
rpl::event_stream<not_null<UserData*>> _rowAdded;
|
||||
|
||||
};
|
||||
|
||||
class InviteContactsController final : public AddParticipantsBoxController {
|
||||
public:
|
||||
InviteContactsController(
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> alreadyIn,
|
||||
not_null<const base::flat_set<not_null<UserData*>>*> inGroup,
|
||||
rpl::producer<not_null<UserData*>> discoveredInGroup);
|
||||
|
||||
private:
|
||||
void prepareViewHook() override;
|
||||
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) override;
|
||||
|
||||
const not_null<const base::flat_set<not_null<UserData*>>*> _inGroup;
|
||||
rpl::producer<not_null<UserData*>> _discoveredInGroup;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> CreateSectionSubtitle(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> text) {
|
||||
auto result = object_ptr<Ui::FixedHeightWidget>(
|
||||
parent,
|
||||
st::searchedBarHeight);
|
||||
|
||||
const auto raw = result.data();
|
||||
raw->paintRequest(
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(raw);
|
||||
p.fillRect(clip, st::groupCallMembersBgOver);
|
||||
}, raw->lifetime());
|
||||
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(text),
|
||||
st::groupCallBoxLabel);
|
||||
raw->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
const auto padding = st::groupCallInviteDividerPadding;
|
||||
const auto available = width - padding.left() - padding.right();
|
||||
label->resizeToNaturalWidth(available);
|
||||
label->moveToLeft(padding.left(), padding.top(), width);
|
||||
}, label->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
InviteController::InviteController(
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> alreadyIn,
|
||||
int fullInCount)
|
||||
base::flat_set<not_null<UserData*>> alreadyIn)
|
||||
: ParticipantsBoxController(CreateTag{}, nullptr, peer, Role::Members)
|
||||
, _peer(peer)
|
||||
, _alreadyIn(std::move(alreadyIn))
|
||||
, _fullInCount(std::max(fullInCount, int(_alreadyIn.size()))) {
|
||||
_skippedUsers.emplace(peer->session().user());
|
||||
, _alreadyIn(std::move(alreadyIn)) {
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
lifetime(),
|
||||
|
@ -98,8 +147,14 @@ InviteController::InviteController(
|
|||
}
|
||||
|
||||
void InviteController::prepare() {
|
||||
delegate()->peerListSetHideEmpty(true);
|
||||
ParticipantsBoxController::prepare();
|
||||
delegate()->peerListSetTitle(tr::lng_group_call_invite_title());
|
||||
delegate()->peerListSetAboveWidget(CreateSectionSubtitle(
|
||||
nullptr,
|
||||
tr::lng_group_call_invite_members()));
|
||||
delegate()->peerListSetAboveSearchWidget(CreateSectionSubtitle(
|
||||
nullptr,
|
||||
tr::lng_group_call_invite_members()));
|
||||
}
|
||||
|
||||
void InviteController::rowClicked(not_null<PeerListRow*> row) {
|
||||
|
@ -115,44 +170,71 @@ base::unique_qptr<Ui::PopupMenu> InviteController::rowContextMenu(
|
|||
void InviteController::itemDeselectedHook(not_null<PeerData*> peer) {
|
||||
}
|
||||
|
||||
int InviteController::alreadyInCount() const {
|
||||
return std::max(_fullInCount, int(_alreadyIn.size()));
|
||||
bool InviteController::hasRowFor(not_null<PeerData*> peer) const {
|
||||
return (delegate()->peerListFindRow(peer->id) != nullptr);
|
||||
}
|
||||
|
||||
bool InviteController::isAlreadyIn(not_null<UserData*> user) const {
|
||||
return _alreadyIn.contains(user);
|
||||
}
|
||||
|
||||
int InviteController::fullCount() const {
|
||||
return alreadyInCount() + delegate()->peerListSelectedRowsCount();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> InviteController::createRow(
|
||||
not_null<UserData*> user) const {
|
||||
if (user->isSelf() || user->isBot()) {
|
||||
_skippedUsers.emplace(user);
|
||||
return nullptr;
|
||||
}
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
_rowAdded.fire_copy(user);
|
||||
_inGroup.emplace(user);
|
||||
if (isAlreadyIn(user)) {
|
||||
result->setDisabledState(PeerListRow::State::DisabledChecked);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::variant<int, not_null<UserData*>> InviteController::inviteSelectedUsers(
|
||||
not_null<PeerListBox*> box,
|
||||
not_null<GroupCall*> call) const {
|
||||
const auto rows = box->peerListCollectSelectedRows();
|
||||
const auto users = ranges::view::all(
|
||||
rows
|
||||
) | ranges::view::transform([](not_null<PeerData*> peer) {
|
||||
Expects(peer->isUser());
|
||||
Expects(!peer->isSelf());
|
||||
auto InviteController::peersWithRows() const
|
||||
-> not_null<const base::flat_set<not_null<UserData*>>*> {
|
||||
return &_inGroup;
|
||||
}
|
||||
|
||||
return not_null<UserData*>(peer->asUser());
|
||||
}) | ranges::to_vector;
|
||||
return call->inviteUsers(users);
|
||||
rpl::producer<not_null<UserData*>> InviteController::rowAdded() const {
|
||||
return _rowAdded.events();
|
||||
}
|
||||
|
||||
InviteContactsController::InviteContactsController(
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> alreadyIn,
|
||||
not_null<const base::flat_set<not_null<UserData*>>*> inGroup,
|
||||
rpl::producer<not_null<UserData*>> discoveredInGroup)
|
||||
: AddParticipantsBoxController(peer, std::move(alreadyIn))
|
||||
, _inGroup(inGroup)
|
||||
, _discoveredInGroup(std::move(discoveredInGroup)) {
|
||||
}
|
||||
|
||||
void InviteContactsController::prepareViewHook() {
|
||||
AddParticipantsBoxController::prepareViewHook();
|
||||
|
||||
delegate()->peerListSetAboveWidget(CreateSectionSubtitle(
|
||||
nullptr,
|
||||
tr::lng_contacts_header()));
|
||||
delegate()->peerListSetAboveSearchWidget(CreateSectionSubtitle(
|
||||
nullptr,
|
||||
tr::lng_group_call_invite_search_results()));
|
||||
|
||||
std::move(
|
||||
_discoveredInGroup
|
||||
) | rpl::start_with_next([=](not_null<UserData*> user) {
|
||||
if (auto row = delegate()->peerListFindRow(user->id)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> InviteContactsController::createRow(
|
||||
not_null<UserData*> user) {
|
||||
return _inGroup->contains(user)
|
||||
? nullptr
|
||||
: AddParticipantsBoxController::createRow(user);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -197,6 +279,21 @@ void LeaveGroupCallBox(
|
|||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
void GroupCallConfirmBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
const QString &text,
|
||||
rpl::producer<QString> button,
|
||||
Fn<void()> callback) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
text,
|
||||
st::groupCallBoxLabel),
|
||||
st::boxPadding);
|
||||
box->addButton(std::move(button), callback);
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
GroupPanel::GroupPanel(not_null<GroupCall*> call)
|
||||
: _call(call)
|
||||
, _peer(call->peer())
|
||||
|
@ -362,7 +459,7 @@ void GroupPanel::initControls() {
|
|||
});
|
||||
|
||||
_settings->setText(tr::lng_menu_settings());
|
||||
_hangup->setText(tr::lng_box_leave());
|
||||
_hangup->setText(tr::lng_group_call_leave());
|
||||
|
||||
_members->desiredHeightValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
@ -460,52 +557,131 @@ void GroupPanel::addMembers() {
|
|||
alreadyIn.emplace(_peer->session().user());
|
||||
auto controller = std::make_unique<InviteController>(
|
||||
_peer,
|
||||
std::move(alreadyIn),
|
||||
real->fullCount());
|
||||
alreadyIn);
|
||||
controller->setStyleOverrides(
|
||||
&st::groupCallInviteMembersList,
|
||||
&st::groupCallMultiSelect);
|
||||
|
||||
const auto weak = base::make_weak(_call);
|
||||
auto initBox = [=, controller = controller.get()](
|
||||
not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_group_call_invite_button(), [=] {
|
||||
if (const auto call = weak.get()) {
|
||||
const auto result = controller->inviteSelectedUsers(box, call);
|
||||
auto contactsController = std::make_unique<InviteContactsController>(
|
||||
_peer,
|
||||
std::move(alreadyIn),
|
||||
controller->peersWithRows(),
|
||||
controller->rowAdded());
|
||||
contactsController->setStyleOverrides(
|
||||
&st::groupCallInviteMembersList,
|
||||
&st::groupCallMultiSelect);
|
||||
|
||||
if (const auto user = std::get_if<not_null<UserData*>>(&result)) {
|
||||
Ui::Toast::Show(
|
||||
widget(),
|
||||
Ui::Toast::Config{
|
||||
.text = tr::lng_group_call_invite_done_user(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold((*user)->firstName),
|
||||
Ui::Text::WithEntities),
|
||||
.st = &st::defaultToast,
|
||||
});
|
||||
} else if (const auto count = std::get_if<int>(&result)) {
|
||||
if (*count > 0) {
|
||||
Ui::Toast::Show(
|
||||
widget(),
|
||||
Ui::Toast::Config{
|
||||
.text = tr::lng_group_call_invite_done_many(
|
||||
tr::now,
|
||||
lt_count,
|
||||
*count,
|
||||
Ui::Text::RichLangValue),
|
||||
.st = &st::defaultToast,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Unexpected("Result in GroupCall::inviteUsers.");
|
||||
}
|
||||
const auto weak = base::make_weak(_call);
|
||||
const auto invite = [=](const std::vector<not_null<UserData*>> &users) {
|
||||
const auto call = weak.get();
|
||||
if (!call) {
|
||||
return;
|
||||
}
|
||||
const auto result = call->inviteUsers(users);
|
||||
if (const auto user = std::get_if<not_null<UserData*>>(&result)) {
|
||||
Ui::Toast::Show(
|
||||
widget(),
|
||||
Ui::Toast::Config{
|
||||
.text = tr::lng_group_call_invite_done_user(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold((*user)->firstName),
|
||||
Ui::Text::WithEntities),
|
||||
.st = &st::defaultToast,
|
||||
});
|
||||
} else if (const auto count = std::get_if<int>(&result)) {
|
||||
if (*count > 0) {
|
||||
Ui::Toast::Show(
|
||||
widget(),
|
||||
Ui::Toast::Config{
|
||||
.text = tr::lng_group_call_invite_done_many(
|
||||
tr::now,
|
||||
lt_count,
|
||||
*count,
|
||||
Ui::Text::RichLangValue),
|
||||
.st = &st::defaultToast,
|
||||
});
|
||||
}
|
||||
box->closeBox();
|
||||
} else {
|
||||
Unexpected("Result in GroupCall::inviteUsers.");
|
||||
}
|
||||
};
|
||||
const auto inviteWithAdd = [=](
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
const std::vector<not_null<UserData*>> &nonMembers,
|
||||
Fn<void()> finish) {
|
||||
_peer->session().api().addChatParticipants(
|
||||
_peer,
|
||||
nonMembers,
|
||||
[=](bool) { invite(users); finish(); });
|
||||
};
|
||||
const auto inviteWithConfirmation = [=](
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
const std::vector<not_null<UserData*>> &nonMembers,
|
||||
Fn<void()> finish) {
|
||||
if (nonMembers.empty()) {
|
||||
invite(users);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
const auto name = _peer->name;
|
||||
const auto text = (nonMembers.size() == 1)
|
||||
? tr::lng_group_call_add_to_group_one(
|
||||
tr::now,
|
||||
lt_user,
|
||||
nonMembers.front()->shortName(),
|
||||
lt_group,
|
||||
name)
|
||||
: (nonMembers.size() < users.size())
|
||||
? tr::lng_group_call_add_to_group_some(tr::now, lt_group, name)
|
||||
: tr::lng_group_call_add_to_group_all(tr::now, lt_group, name);
|
||||
const auto shared = std::make_shared<QPointer<Ui::GenericBox>>();
|
||||
const auto finishWithConfirm = [=] {
|
||||
if (*shared) {
|
||||
(*shared)->closeBox();
|
||||
}
|
||||
finish();
|
||||
};
|
||||
auto box = Box(
|
||||
GroupCallConfirmBox,
|
||||
text,
|
||||
tr::lng_participant_invite(),
|
||||
[=] { inviteWithAdd(users, nonMembers, finishWithConfirm); });
|
||||
*shared = box.data();
|
||||
_layerBg->showBox(std::move(box));
|
||||
};
|
||||
auto initBox = [=, controller = controller.get()](
|
||||
not_null<PeerListsBox*> box) {
|
||||
box->setTitle(tr::lng_group_call_invite_title());
|
||||
box->addButton(tr::lng_group_call_invite_button(), [=] {
|
||||
const auto rows = box->collectSelectedRows();
|
||||
|
||||
const auto users = ranges::view::all(
|
||||
rows
|
||||
) | ranges::view::transform([](not_null<PeerData*> peer) {
|
||||
return not_null<UserData*>(peer->asUser());
|
||||
}) | ranges::to_vector;
|
||||
|
||||
const auto nonMembers = ranges::view::all(
|
||||
users
|
||||
) | ranges::view::filter([&](not_null<UserData*> user) {
|
||||
return !controller->hasRowFor(user);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
const auto finish = [box = Ui::MakeWeak(box)]() {
|
||||
if (box) {
|
||||
box->closeBox();
|
||||
}
|
||||
};
|
||||
inviteWithConfirmation(users, nonMembers, finish);
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
};
|
||||
_layerBg->showBox(Box<PeerListBox>(std::move(controller), initBox));
|
||||
|
||||
auto controllers = std::vector<std::unique_ptr<PeerListController>>();
|
||||
controllers.push_back(std::move(controller));
|
||||
controllers.push_back(std::move(contactsController));
|
||||
_layerBg->showBox(Box<PeerListsBox>(std::move(controllers), initBox));
|
||||
}
|
||||
|
||||
void GroupPanel::kickMember(not_null<UserData*> user) {
|
||||
|
|
|
@ -33,7 +33,7 @@ GroupCall::GroupCall(
|
|||
uint64 accessHash)
|
||||
: _id(id)
|
||||
, _accessHash(accessHash)
|
||||
, _peer(peer) // #TODO calls migration
|
||||
, _peer(peer)
|
||||
, _speakingByActiveFinishTimer([=] { checkFinishSpeakingByActive(); }) {
|
||||
}
|
||||
|
||||
|
|
|
@ -258,10 +258,6 @@ int InnerWidget::peerListSelectedRowsCount() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::vector<not_null<PeerData*>> InnerWidget::peerListCollectSelectedRows() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void InnerWidget::peerListScrollToTop() {
|
||||
_scrollToRequests.fire({ -1, -1 });
|
||||
}
|
||||
|
|
|
@ -52,7 +52,6 @@ private:
|
|||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListScrollToTop() override;
|
||||
void peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) override;
|
||||
|
|
|
@ -57,7 +57,6 @@ public:
|
|||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListScrollToTop() override;
|
||||
void peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) override;
|
||||
|
@ -130,11 +129,6 @@ int ListDelegate::peerListSelectedRowsCount() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto ListDelegate::peerListCollectSelectedRows()
|
||||
-> std::vector<not_null<PeerData*>> {
|
||||
return {};
|
||||
}
|
||||
|
||||
void ListDelegate::peerListScrollToTop() {
|
||||
}
|
||||
|
||||
|
|
|
@ -493,7 +493,7 @@ void ActionsFiller::addInviteToGroupAction(
|
|||
_wrap,
|
||||
tr::lng_profile_invite_to_group(),
|
||||
CanInviteBotToGroupValue(user),
|
||||
[=] { AddBotToGroupBoxController::Start(controller, user); });
|
||||
[=] { AddBotToGroupBoxController::Start(user); });
|
||||
}
|
||||
|
||||
void ActionsFiller::addShareContactAction(not_null<UserData*> user) {
|
||||
|
|
|
@ -423,10 +423,6 @@ int Members::peerListSelectedRowsCount() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::vector<not_null<PeerData*>> Members::peerListCollectSelectedRows() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void Members::peerListScrollToTop() {
|
||||
_scrollToRequests.fire({ -1, -1 });
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ private:
|
|||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
|
||||
void peerListScrollToTop() override;
|
||||
void peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) override;
|
||||
|
|
|
@ -42,6 +42,7 @@ void init() {
|
|||
u"rstrtmgr.dll"_q,
|
||||
u"psapi.dll"_q,
|
||||
u"user32.dll"_q,
|
||||
u"thai.dll"_q,
|
||||
};
|
||||
for (const auto &lib : list) {
|
||||
SafeLoadLibrary(lib);
|
||||
|
|
|
@ -51,8 +51,7 @@ class BlockPeerBoxController
|
|||
: public ChatsListBoxController
|
||||
, private base::Subscriber {
|
||||
public:
|
||||
explicit BlockPeerBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
explicit BlockPeerBoxController(not_null<Main::Session*> session);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
@ -72,19 +71,19 @@ protected:
|
|||
private:
|
||||
void updateIsBlocked(not_null<PeerListRow*> row, PeerData *peer) const;
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<Main::Session*> _session;
|
||||
Fn<void(not_null<PeerData*> peer)> _blockPeerCallback;
|
||||
|
||||
};
|
||||
|
||||
BlockPeerBoxController::BlockPeerBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: ChatsListBoxController(navigation)
|
||||
, _navigation(navigation) {
|
||||
not_null<Main::Session*> session)
|
||||
: ChatsListBoxController(session)
|
||||
, _session(session) {
|
||||
}
|
||||
|
||||
Main::Session &BlockPeerBoxController::session() const {
|
||||
return _navigation->session();
|
||||
return *_session;
|
||||
}
|
||||
|
||||
void BlockPeerBoxController::prepareViewHook() {
|
||||
|
@ -294,7 +293,8 @@ void BlockedBoxController::handleBlockedEvent(not_null<PeerData*> user) {
|
|||
|
||||
void BlockedBoxController::BlockNewPeer(
|
||||
not_null<Window::SessionController*> window) {
|
||||
auto controller = std::make_unique<BlockPeerBoxController>(window);
|
||||
auto controller = std::make_unique<BlockPeerBoxController>(
|
||||
&window->session());
|
||||
auto initBox = [=, controller = controller.get()](
|
||||
not_null<PeerListBox*> box) {
|
||||
controller->setBlockPeerCallback([=](not_null<PeerData*> peer) {
|
||||
|
|
|
@ -483,7 +483,7 @@ void Filler::addUserActions(not_null<UserData*> user) {
|
|||
using AddBotToGroup = AddBotToGroupBoxController;
|
||||
_addAction(
|
||||
tr::lng_profile_invite_to_group(tr::now),
|
||||
[=] { AddBotToGroup::Start(controller, user); });
|
||||
[=] { AddBotToGroup::Start(user); });
|
||||
}
|
||||
addPollAction(user);
|
||||
if (user->canExportChatHistory()) {
|
||||
|
@ -803,7 +803,7 @@ void PeerMenuShareContactBox(
|
|||
};
|
||||
*weak = Ui::show(Box<PeerListBox>(
|
||||
std::make_unique<ChooseRecipientBoxController>(
|
||||
navigation,
|
||||
&navigation->session(),
|
||||
std::move(callback)),
|
||||
[](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
|
@ -1010,7 +1010,7 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
|||
};
|
||||
*weak = Ui::show(Box<PeerListBox>(
|
||||
std::make_unique<ChooseRecipientBoxController>(
|
||||
navigation,
|
||||
&navigation->session(),
|
||||
std::move(callback)),
|
||||
std::move(initBox)), Ui::LayerOption::KeepOther);
|
||||
return weak->data();
|
||||
|
|
|
@ -194,7 +194,7 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
const auto user = peer->asUser();
|
||||
if (user && user->isBot() && !info.startToken.isEmpty()) {
|
||||
user->botInfo->shareGameShortName = info.startToken;
|
||||
AddBotToGroupBoxController::Start(this, user);
|
||||
AddBotToGroupBoxController::Start(user);
|
||||
} else {
|
||||
crl::on_main(this, [=] {
|
||||
showPeerHistory(peer->id, params);
|
||||
|
@ -207,7 +207,7 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
&& !user->botInfo->cantJoinGroups
|
||||
&& !info.startToken.isEmpty()) {
|
||||
user->botInfo->startGroupToken = info.startToken;
|
||||
AddBotToGroupBoxController::Start(this, user);
|
||||
AddBotToGroupBoxController::Start(user);
|
||||
} else if (user && user->isBot()) {
|
||||
// Always open bot chats, even from mention links.
|
||||
crl::on_main(this, [=] {
|
||||
|
|
Loading…
Add table
Reference in a new issue