Add stories outline to group participants list.
This commit is contained in:
parent
fad05e8b35
commit
f31b40f6ce
|
@ -889,9 +889,11 @@ void PeerListRow::createCheckbox(
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListRow::setCheckedInternal(bool checked, anim::type animated) {
|
void PeerListRow::setCheckedInternal(bool checked, anim::type animated) {
|
||||||
Expects(_checkbox != nullptr);
|
Expects(!checked || _checkbox != nullptr);
|
||||||
|
|
||||||
_checkbox->setChecked(checked, animated);
|
if (_checkbox) {
|
||||||
|
_checkbox->setChecked(checked, animated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListRow::setCustomizedCheckSegments(
|
void PeerListRow::setCustomizedCheckSegments(
|
||||||
|
|
|
@ -54,40 +54,6 @@ namespace {
|
||||||
constexpr auto kSortByOnlineThrottle = 3 * crl::time(1000);
|
constexpr auto kSortByOnlineThrottle = 3 * crl::time(1000);
|
||||||
constexpr auto kSearchPerPage = 50;
|
constexpr auto kSearchPerPage = 50;
|
||||||
|
|
||||||
[[nodiscard]] std::vector<Ui::OutlineSegment> PrepareSegments(
|
|
||||||
int count,
|
|
||||||
int unread,
|
|
||||||
const QBrush &unreadBrush) {
|
|
||||||
Expects(unread <= count);
|
|
||||||
Expects(count > 0);
|
|
||||||
|
|
||||||
auto result = std::vector<Ui::OutlineSegment>();
|
|
||||||
const auto add = [&](bool unread) {
|
|
||||||
result.push_back({
|
|
||||||
.brush = unread ? unreadBrush : st::dialogsUnreadBgMuted->b,
|
|
||||||
.width = (unread
|
|
||||||
? st::dialogsStoriesFull.lineTwice / 2.
|
|
||||||
: st::dialogsStoriesFull.lineReadTwice / 2.),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
result.reserve(count);
|
|
||||||
for (auto i = 0, till = count - unread; i != till; ++i) {
|
|
||||||
add(false);
|
|
||||||
}
|
|
||||||
for (auto i = 0; i != unread; ++i) {
|
|
||||||
add(true);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] QBrush CreateStoriesGradient() {
|
|
||||||
const auto &st = st::contactsWithStories.item;
|
|
||||||
const auto left = st.photoPosition.x();
|
|
||||||
const auto top = st.photoPosition.y();
|
|
||||||
const auto size = st.photoSize;
|
|
||||||
return Ui::UnreadStoryOutlineGradient(QRectF(left, top, size, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
object_ptr<Ui::BoxContent> PrepareContactsBox(
|
object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||||
|
@ -124,6 +90,39 @@ object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||||
return Box<PeerListBox>(std::move(controller), std::move(init));
|
return Box<PeerListBox>(std::move(controller), std::move(init));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QBrush PeerListStoriesGradient(const style::PeerList &st) {
|
||||||
|
const auto left = st.item.photoPosition.x();
|
||||||
|
const auto top = st.item.photoPosition.y();
|
||||||
|
const auto size = st.item.photoSize;
|
||||||
|
return Ui::UnreadStoryOutlineGradient(QRectF(left, top, size, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Ui::OutlineSegment> PeerListStoriesSegments(
|
||||||
|
int count,
|
||||||
|
int unread,
|
||||||
|
const QBrush &unreadBrush) {
|
||||||
|
Expects(unread <= count);
|
||||||
|
Expects(count > 0);
|
||||||
|
|
||||||
|
auto result = std::vector<Ui::OutlineSegment>();
|
||||||
|
const auto add = [&](bool unread) {
|
||||||
|
result.push_back({
|
||||||
|
.brush = unread ? unreadBrush : st::dialogsUnreadBgMuted->b,
|
||||||
|
.width = (unread
|
||||||
|
? st::dialogsStoriesFull.lineTwice / 2.
|
||||||
|
: st::dialogsStoriesFull.lineReadTwice / 2.),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
result.reserve(count);
|
||||||
|
for (auto i = 0, till = count - unread; i != till; ++i) {
|
||||||
|
add(false);
|
||||||
|
}
|
||||||
|
for (auto i = 0; i != unread; ++i) {
|
||||||
|
add(true);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void PeerListRowWithLink::setActionLink(const QString &action) {
|
void PeerListRowWithLink::setActionLink(const QString &action) {
|
||||||
_action = action;
|
_action = action;
|
||||||
refreshActionLink();
|
refreshActionLink();
|
||||||
|
@ -359,6 +358,115 @@ bool ChatsListBoxController::appendRow(not_null<History*> history) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PeerListStories::PeerListStories(
|
||||||
|
not_null<PeerListController*> controller,
|
||||||
|
not_null<Main::Session*> session)
|
||||||
|
: _controller(controller)
|
||||||
|
, _session(session) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerListStories::updateColors() {
|
||||||
|
for (auto i = begin(_counts); i != end(_counts); ++i) {
|
||||||
|
if (const auto row = _delegate->peerListFindRow(i->first)) {
|
||||||
|
if (i->second.count >= 0 && i->second.unread >= 0) {
|
||||||
|
applyForRow(row, i->second.count, i->second.unread, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerListStories::updateFor(
|
||||||
|
uint64 id,
|
||||||
|
int count,
|
||||||
|
int unread) {
|
||||||
|
if (const auto row = _delegate->peerListFindRow(id)) {
|
||||||
|
applyForRow(row, count, unread);
|
||||||
|
_delegate->peerListUpdateRow(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerListStories::process(not_null<PeerListRow*> row) {
|
||||||
|
const auto user = row->peer()->asUser();
|
||||||
|
if (!user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto stories = &_session->data().stories();
|
||||||
|
const auto source = stories->source(user->id);
|
||||||
|
const auto count = source
|
||||||
|
? int(source->ids.size())
|
||||||
|
: user->hasActiveStories()
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
const auto unread = source
|
||||||
|
? source->info().unreadCount
|
||||||
|
: user->hasUnreadStories()
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
applyForRow(row, count, unread, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerListStories::handleClick(not_null<PeerData*> peer) {
|
||||||
|
const auto point = _delegate->peerListLastRowMousePosition();
|
||||||
|
const auto &st = _controller->listSt()->item;
|
||||||
|
if (point && point->x() < st.photoPosition.x() + st.photoSize) {
|
||||||
|
if (const auto window = peer->session().tryResolveWindow()) {
|
||||||
|
if (const auto user = peer->asUser()) {
|
||||||
|
if (user->hasActiveStories()) {
|
||||||
|
window->openPeerStories(peer->id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerListStories::prepare(not_null<PeerListDelegate*> delegate) {
|
||||||
|
_delegate = delegate;
|
||||||
|
|
||||||
|
_unreadBrush = PeerListStoriesGradient(*_controller->listSt());
|
||||||
|
style::PaletteChanged() | rpl::start_with_next([=] {
|
||||||
|
_unreadBrush = PeerListStoriesGradient(*_controller->listSt());
|
||||||
|
updateColors();
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
_session->changes().peerUpdates(
|
||||||
|
Data::PeerUpdate::Flag::StoriesState
|
||||||
|
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||||
|
const auto id = update.peer->id.value;
|
||||||
|
if (const auto row = _delegate->peerListFindRow(id)) {
|
||||||
|
process(row);
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
const auto stories = &_session->data().stories();
|
||||||
|
stories->sourceChanged() | rpl::start_with_next([=](PeerId id) {
|
||||||
|
const auto source = stories->source(id);
|
||||||
|
const auto info = source
|
||||||
|
? source->info()
|
||||||
|
: Data::StoriesSourceInfo();
|
||||||
|
updateFor(id.value, info.count, info.unreadCount);
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerListStories::applyForRow(
|
||||||
|
not_null<PeerListRow*> row,
|
||||||
|
int count,
|
||||||
|
int unread,
|
||||||
|
bool force) {
|
||||||
|
auto &counts = _counts[row->id()];
|
||||||
|
if (!force && counts.count == count && counts.unread == unread) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
counts.count = count;
|
||||||
|
counts.unread = unread;
|
||||||
|
_delegate->peerListSetRowChecked(row, count > 0);
|
||||||
|
if (count > 0) {
|
||||||
|
row->setCustomizedCheckSegments(
|
||||||
|
PeerListStoriesSegments(count, unread, _unreadBrush));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContactsBoxController::ContactsBoxController(
|
ContactsBoxController::ContactsBoxController(
|
||||||
not_null<Main::Session*> session)
|
not_null<Main::Session*> session)
|
||||||
: ContactsBoxController(
|
: ContactsBoxController(
|
||||||
|
@ -385,31 +493,8 @@ void ContactsBoxController::prepare() {
|
||||||
|
|
||||||
prepareViewHook();
|
prepareViewHook();
|
||||||
|
|
||||||
if (_storiesShown) {
|
if (_stories) {
|
||||||
_storiesUnread = CreateStoriesGradient();
|
_stories->prepare(delegate());
|
||||||
style::PaletteChanged() | rpl::start_with_next([=] {
|
|
||||||
_storiesUnread = CreateStoriesGradient();
|
|
||||||
for (auto &entry : _storiesCounts) {
|
|
||||||
entry.second.count = entry.second.unread = -1;
|
|
||||||
}
|
|
||||||
updateStories();
|
|
||||||
}, lifetime());
|
|
||||||
|
|
||||||
const auto stories = &session().data().stories();
|
|
||||||
rpl::merge(
|
|
||||||
rpl::single(rpl::empty),
|
|
||||||
stories->sourcesChanged(Data::StorySourcesList::NotHidden),
|
|
||||||
stories->sourcesChanged(Data::StorySourcesList::Hidden)
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
updateStories();
|
|
||||||
}, lifetime());
|
|
||||||
stories->sourceChanged() | rpl::start_with_next([=](PeerId id) {
|
|
||||||
const auto source = stories->source(id);
|
|
||||||
const auto info = source
|
|
||||||
? source->info()
|
|
||||||
: Data::StoriesSourceInfo();
|
|
||||||
updateStoriesFor(id.value, info.count, info.unreadCount);
|
|
||||||
}, lifetime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
session().data().contactsLoaded().value(
|
session().data().contactsLoaded().value(
|
||||||
|
@ -456,16 +541,10 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(
|
||||||
|
|
||||||
void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
const auto peer = row->peer();
|
const auto peer = row->peer();
|
||||||
if (const auto window = peer->session().tryResolveWindow()) {
|
if (_stories && _stories->handleClick(peer)) {
|
||||||
if (_storiesShown) {
|
return;
|
||||||
const auto point = delegate()->peerListLastRowMousePosition();
|
} else if (const auto window = peer->session().tryResolveWindow()) {
|
||||||
const auto &st = st::contactsWithStories.item;
|
window->showPeerHistory(peer);
|
||||||
if (point && point->x() < st.photoPosition.x() + st.photoSize) {
|
|
||||||
window->openPeerStories(peer->id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
window->showPeerHistory(row->peer());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,52 +570,7 @@ void ContactsBoxController::setSortMode(SortMode mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContactsBoxController::setStoriesShown(bool shown) {
|
void ContactsBoxController::setStoriesShown(bool shown) {
|
||||||
_storiesShown = shown;
|
_stories = std::make_unique<PeerListStories>(this, _session);
|
||||||
}
|
|
||||||
|
|
||||||
void ContactsBoxController::updateStories() {
|
|
||||||
const auto stories = &_session->data().stories();
|
|
||||||
const auto &a = stories->sources(Data::StorySourcesList::NotHidden);
|
|
||||||
const auto &b = stories->sources(Data::StorySourcesList::Hidden);
|
|
||||||
auto checked = base::flat_set<PeerListRowId>();
|
|
||||||
for (const auto &info : ranges::views::concat(a, b)) {
|
|
||||||
const auto id = info.id.value;
|
|
||||||
checked.emplace(id);
|
|
||||||
updateStoriesFor(id, info.count, info.unreadCount);
|
|
||||||
}
|
|
||||||
for (auto i = begin(_storiesCounts); i != end(_storiesCounts); ++i) {
|
|
||||||
if (i->second.count && !checked.contains(i->first)) {
|
|
||||||
updateStoriesFor(i->first, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContactsBoxController::updateStoriesFor(
|
|
||||||
uint64 id,
|
|
||||||
int count,
|
|
||||||
int unread) {
|
|
||||||
if (const auto row = delegate()->peerListFindRow(id)) {
|
|
||||||
applyRowStories(row, count, unread);
|
|
||||||
delegate()->peerListUpdateRow(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContactsBoxController::applyRowStories(
|
|
||||||
not_null<PeerListRow*> row,
|
|
||||||
int count,
|
|
||||||
int unread,
|
|
||||||
bool force) {
|
|
||||||
auto &counts = _storiesCounts[row->id()];
|
|
||||||
if (!force && counts.count == count && counts.unread == unread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
counts.count = count;
|
|
||||||
counts.unread = unread;
|
|
||||||
delegate()->peerListSetRowChecked(row, count > 0);
|
|
||||||
if (count > 0) {
|
|
||||||
row->setCustomizedCheckSegments(
|
|
||||||
PrepareSegments(count, unread, _storiesUnread));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContactsBoxController::sort() {
|
void ContactsBoxController::sort() {
|
||||||
|
@ -586,12 +620,8 @@ bool ContactsBoxController::appendRow(not_null<UserData*> user) {
|
||||||
if (auto row = createRow(user)) {
|
if (auto row = createRow(user)) {
|
||||||
const auto raw = row.get();
|
const auto raw = row.get();
|
||||||
delegate()->peerListAppendRow(std::move(row));
|
delegate()->peerListAppendRow(std::move(row));
|
||||||
if (_storiesShown) {
|
if (_stories) {
|
||||||
const auto stories = &session().data().stories();
|
_stories->process(raw);
|
||||||
if (const auto source = stories->source(user->id)) {
|
|
||||||
const auto info = source->info();
|
|
||||||
applyRowStories(raw, info.count, info.unreadCount, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,21 @@ class Forum;
|
||||||
class ForumTopic;
|
class ForumTopic;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
struct OutlineSegment;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class SessionController;
|
class SessionController;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
||||||
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareContactsBox(
|
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareContactsBox(
|
||||||
not_null<Window::SessionController*> sessionController);
|
not_null<Window::SessionController*> sessionController);
|
||||||
|
[[nodiscard]] QBrush PeerListStoriesGradient(const style::PeerList &st);
|
||||||
|
[[nodiscard]] std::vector<Ui::OutlineSegment> PeerListStoriesSegments(
|
||||||
|
int count,
|
||||||
|
int unread,
|
||||||
|
const QBrush &unreadBrush);
|
||||||
|
|
||||||
class PeerListRowWithLink : public PeerListRow {
|
class PeerListRowWithLink : public PeerListRow {
|
||||||
public:
|
public:
|
||||||
|
@ -116,6 +125,41 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PeerListStories final {
|
||||||
|
public:
|
||||||
|
PeerListStories(
|
||||||
|
not_null<PeerListController*> controller,
|
||||||
|
not_null<Main::Session*> session);
|
||||||
|
|
||||||
|
void prepare(not_null<PeerListDelegate*> delegate);
|
||||||
|
|
||||||
|
void process(not_null<PeerListRow*> row);
|
||||||
|
bool handleClick(not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Counts {
|
||||||
|
int count = 0;
|
||||||
|
int unread = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void updateColors();
|
||||||
|
void updateFor(uint64 id, int count, int unread);
|
||||||
|
void applyForRow(
|
||||||
|
not_null<PeerListRow*> row,
|
||||||
|
int count,
|
||||||
|
int unread,
|
||||||
|
bool force = false);
|
||||||
|
|
||||||
|
const not_null<PeerListController*> _controller;
|
||||||
|
const not_null<Main::Session*> _session;
|
||||||
|
PeerListDelegate *_delegate = nullptr;
|
||||||
|
|
||||||
|
QBrush _unreadBrush;
|
||||||
|
base::flat_map<uint64, Counts> _counts;
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class ContactsBoxController : public PeerListController {
|
class ContactsBoxController : public PeerListController {
|
||||||
public:
|
public:
|
||||||
explicit ContactsBoxController(not_null<Main::Session*> session);
|
explicit ContactsBoxController(not_null<Main::Session*> session);
|
||||||
|
@ -129,7 +173,7 @@ public:
|
||||||
not_null<PeerData*> peer) override final;
|
not_null<PeerData*> peer) override final;
|
||||||
void rowClicked(not_null<PeerListRow*> row) override;
|
void rowClicked(not_null<PeerListRow*> row) override;
|
||||||
bool trackSelectedList() override {
|
bool trackSelectedList() override {
|
||||||
return !_storiesShown;
|
return !_stories;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SortMode {
|
enum class SortMode {
|
||||||
|
@ -147,32 +191,19 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct StoriesCount {
|
|
||||||
int count = 0;
|
|
||||||
int unread = 0;
|
|
||||||
};
|
|
||||||
void sort();
|
void sort();
|
||||||
void sortByName();
|
void sortByName();
|
||||||
void sortByOnline();
|
void sortByOnline();
|
||||||
void rebuildRows();
|
void rebuildRows();
|
||||||
void updateStories();
|
|
||||||
void checkForEmptyRows();
|
void checkForEmptyRows();
|
||||||
bool appendRow(not_null<UserData*> user);
|
bool appendRow(not_null<UserData*> user);
|
||||||
void updateStoriesFor(uint64 id, int count, int unread);
|
|
||||||
void applyRowStories(
|
|
||||||
not_null<PeerListRow*> row,
|
|
||||||
int count,
|
|
||||||
int unread,
|
|
||||||
bool force = false);
|
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
SortMode _sortMode = SortMode::Alphabet;
|
SortMode _sortMode = SortMode::Alphabet;
|
||||||
base::Timer _sortByOnlineTimer;
|
base::Timer _sortByOnlineTimer;
|
||||||
rpl::lifetime _sortByOnlineLifetime;
|
rpl::lifetime _sortByOnlineLifetime;
|
||||||
|
|
||||||
QBrush _storiesUnread;
|
std::unique_ptr<PeerListStories> _stories;
|
||||||
base::flat_map<uint64, StoriesCount> _storiesCounts;
|
|
||||||
bool _storiesShown = false;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "dialogs/dialogs_indexed_list.h"
|
#include "dialogs/dialogs_indexed_list.h"
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
|
#include "ui/effects/outline_segments.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "info/profile/info_profile_values.h"
|
#include "info/profile/info_profile_values.h"
|
||||||
|
@ -900,7 +902,11 @@ void ParticipantsBoxController::setupListChangeViewers() {
|
||||||
return (row.peer() == user);
|
return (row.peer() == user);
|
||||||
});
|
});
|
||||||
} else if (auto row = createRow(user)) {
|
} else if (auto row = createRow(user)) {
|
||||||
|
const auto raw = row.get();
|
||||||
delegate()->peerListPrependRow(std::move(row));
|
delegate()->peerListPrependRow(std::move(row));
|
||||||
|
if (_stories) {
|
||||||
|
_stories->process(raw);
|
||||||
|
}
|
||||||
refreshRows();
|
refreshRows();
|
||||||
if (_onlineSorter) {
|
if (_onlineSorter) {
|
||||||
_onlineSorter->sort();
|
_onlineSorter->sort();
|
||||||
|
@ -1160,8 +1166,14 @@ void ParticipantsBoxController::restoreState(
|
||||||
loadMoreRows();
|
loadMoreRows();
|
||||||
}
|
}
|
||||||
PeerListController::restoreState(std::move(state));
|
PeerListController::restoreState(std::move(state));
|
||||||
if (delegate()->peerListFullRowsCount() > 0 || _allLoaded) {
|
const auto count = delegate()->peerListFullRowsCount();
|
||||||
|
if (count > 0 || _allLoaded) {
|
||||||
refreshDescription();
|
refreshDescription();
|
||||||
|
if (_stories) {
|
||||||
|
for (auto i = 0; i != count; ++i) {
|
||||||
|
_stories->process(delegate()->peerListRowAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (_onlineSorter) {
|
if (_onlineSorter) {
|
||||||
_onlineSorter->sort();
|
_onlineSorter->sort();
|
||||||
|
@ -1177,14 +1189,21 @@ rpl::producer<int> ParticipantsBoxController::fullCountValue() const {
|
||||||
return _fullCountValue.value();
|
return _fullCountValue.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParticipantsBoxController::setStoriesShown(bool shown) {
|
||||||
|
_stories = std::make_unique<PeerListStories>(
|
||||||
|
this,
|
||||||
|
&_navigation->session());
|
||||||
|
}
|
||||||
|
|
||||||
void ParticipantsBoxController::prepare() {
|
void ParticipantsBoxController::prepare() {
|
||||||
auto title = [&] {
|
auto title = [&] {
|
||||||
switch (_role) {
|
switch (_role) {
|
||||||
case Role::Admins: return tr::lng_channel_admins();
|
case Role::Admins: return tr::lng_channel_admins();
|
||||||
case Role::Profile:
|
case Role::Profile:
|
||||||
case Role::Members: return (_peer->isChannel() && !_peer->isMegagroup()
|
case Role::Members:
|
||||||
? tr::lng_profile_subscribers_section()
|
return ((_peer->isChannel() && !_peer->isMegagroup())
|
||||||
: tr::lng_profile_participants_section());
|
? tr::lng_profile_subscribers_section()
|
||||||
|
: tr::lng_profile_participants_section());
|
||||||
case Role::Restricted: return tr::lng_exceptions_list_title();
|
case Role::Restricted: return tr::lng_exceptions_list_title();
|
||||||
case Role::Kicked: return tr::lng_removed_list_title();
|
case Role::Kicked: return tr::lng_removed_list_title();
|
||||||
}
|
}
|
||||||
|
@ -1207,6 +1226,10 @@ void ParticipantsBoxController::prepare() {
|
||||||
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
||||||
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
||||||
|
|
||||||
|
if (_stories) {
|
||||||
|
_stories->prepare(delegate());
|
||||||
|
}
|
||||||
|
|
||||||
if (_role == Role::Profile) {
|
if (_role == Role::Profile) {
|
||||||
auto visible = _peer->isMegagroup()
|
auto visible = _peer->isMegagroup()
|
||||||
? Info::Profile::CanViewParticipantsValue(_peer->asMegagroup())
|
? Info::Profile::CanViewParticipantsValue(_peer->asMegagroup())
|
||||||
|
@ -1319,7 +1342,11 @@ void ParticipantsBoxController::rebuildChatParticipants(
|
||||||
}
|
}
|
||||||
for (const auto &user : participants) {
|
for (const auto &user : participants) {
|
||||||
if (auto row = createRow(user)) {
|
if (auto row = createRow(user)) {
|
||||||
|
const auto raw = row.get();
|
||||||
delegate()->peerListAppendRow(std::move(row));
|
delegate()->peerListAppendRow(std::move(row));
|
||||||
|
if (_stories) {
|
||||||
|
_stories->process(raw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_onlineSorter->sort();
|
_onlineSorter->sort();
|
||||||
|
@ -1373,7 +1400,11 @@ void ParticipantsBoxController::rebuildChatAdmins(
|
||||||
}
|
}
|
||||||
for (const auto user : list) {
|
for (const auto user : list) {
|
||||||
if (auto row = createRow(user)) {
|
if (auto row = createRow(user)) {
|
||||||
|
const auto raw = row.get();
|
||||||
delegate()->peerListAppendRow(std::move(row));
|
delegate()->peerListAppendRow(std::move(row));
|
||||||
|
if (_stories) {
|
||||||
|
_stories->process(raw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,6 +1574,11 @@ bool ParticipantsBoxController::feedMegagroupLastParticipants() {
|
||||||
void ParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
void ParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
const auto participant = row->peer();
|
const auto participant = row->peer();
|
||||||
const auto user = participant->asUser();
|
const auto user = participant->asUser();
|
||||||
|
|
||||||
|
if (_stories && _stories->handleClick(participant)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_role == Role::Admins) {
|
if (_role == Role::Admins) {
|
||||||
Assert(user != nullptr);
|
Assert(user != nullptr);
|
||||||
showAdmin(user);
|
showAdmin(user);
|
||||||
|
@ -1890,7 +1926,11 @@ bool ParticipantsBoxController::appendRow(not_null<PeerData*> participant) {
|
||||||
recomputeTypeFor(participant);
|
recomputeTypeFor(participant);
|
||||||
return false;
|
return false;
|
||||||
} else if (auto row = createRow(participant)) {
|
} else if (auto row = createRow(participant)) {
|
||||||
|
const auto raw = row.get();
|
||||||
delegate()->peerListAppendRow(std::move(row));
|
delegate()->peerListAppendRow(std::move(row));
|
||||||
|
if (_stories) {
|
||||||
|
_stories->process(raw);
|
||||||
|
}
|
||||||
if (_role != Role::Kicked) {
|
if (_role != Role::Kicked) {
|
||||||
setDescriptionText(QString());
|
setDescriptionText(QString());
|
||||||
}
|
}
|
||||||
|
@ -1906,10 +1946,17 @@ bool ParticipantsBoxController::prependRow(not_null<PeerData*> participant) {
|
||||||
if (_role == Role::Admins) {
|
if (_role == Role::Admins) {
|
||||||
// Perhaps we've added a new admin from search.
|
// Perhaps we've added a new admin from search.
|
||||||
delegate()->peerListPrependRowFromSearchResult(row);
|
delegate()->peerListPrependRowFromSearchResult(row);
|
||||||
|
if (_stories) {
|
||||||
|
_stories->process(row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else if (auto row = createRow(participant)) {
|
} else if (auto row = createRow(participant)) {
|
||||||
|
const auto raw = row.get();
|
||||||
delegate()->peerListPrependRow(std::move(row));
|
delegate()->peerListPrependRow(std::move(row));
|
||||||
|
if (_stories) {
|
||||||
|
_stories->process(raw);
|
||||||
|
}
|
||||||
if (_role != Role::Kicked) {
|
if (_role != Role::Kicked) {
|
||||||
setDescriptionText(QString());
|
setDescriptionText(QString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
#include "info/profile/info_profile_members_controllers.h"
|
#include "info/profile/info_profile_members_controllers.h"
|
||||||
|
|
||||||
|
class PeerListStories;
|
||||||
struct ChatAdminRightsInfo;
|
struct ChatAdminRightsInfo;
|
||||||
struct ChatRestrictionsInfo;
|
struct ChatRestrictionsInfo;
|
||||||
|
|
||||||
|
@ -174,6 +175,9 @@ public:
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PeerListRow*> row) override;
|
not_null<PeerListRow*> row) override;
|
||||||
void loadMoreRows() override;
|
void loadMoreRows() override;
|
||||||
|
bool trackSelectedList() override {
|
||||||
|
return !_stories;
|
||||||
|
}
|
||||||
|
|
||||||
void peerListSearchAddRow(not_null<PeerData*> peer) override;
|
void peerListSearchAddRow(not_null<PeerData*> peer) override;
|
||||||
std::unique_ptr<PeerListRow> createSearchRow(
|
std::unique_ptr<PeerListRow> createSearchRow(
|
||||||
|
@ -187,6 +191,8 @@ public:
|
||||||
[[nodiscard]] rpl::producer<int> onlineCountValue() const;
|
[[nodiscard]] rpl::producer<int> onlineCountValue() const;
|
||||||
[[nodiscard]] rpl::producer<int> fullCountValue() const;
|
[[nodiscard]] rpl::producer<int> fullCountValue() const;
|
||||||
|
|
||||||
|
void setStoriesShown(bool shown);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Allow child controllers not providing navigation.
|
// Allow child controllers not providing navigation.
|
||||||
// This is their responsibility to override all methods that use it.
|
// This is their responsibility to override all methods that use it.
|
||||||
|
@ -288,6 +294,8 @@ private:
|
||||||
Ui::BoxPointer _addBox;
|
Ui::BoxPointer _addBox;
|
||||||
QPointer<Ui::BoxContent> _editParticipantBox;
|
QPointer<Ui::BoxContent> _editParticipantBox;
|
||||||
|
|
||||||
|
std::unique_ptr<PeerListStories> _stories;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Members, banned and restricted users server side search.
|
// Members, banned and restricted users server side search.
|
||||||
|
|
|
@ -85,26 +85,27 @@ struct PeerUpdate {
|
||||||
SupportInfo = (1ULL << 23),
|
SupportInfo = (1ULL << 23),
|
||||||
IsBot = (1ULL << 24),
|
IsBot = (1ULL << 24),
|
||||||
EmojiStatus = (1ULL << 25),
|
EmojiStatus = (1ULL << 25),
|
||||||
|
StoriesState = (1ULL << 26),
|
||||||
|
|
||||||
// For chats and channels
|
// For chats and channels
|
||||||
InviteLinks = (1ULL << 26),
|
InviteLinks = (1ULL << 27),
|
||||||
Members = (1ULL << 27),
|
Members = (1ULL << 28),
|
||||||
Admins = (1ULL << 28),
|
Admins = (1ULL << 29),
|
||||||
BannedUsers = (1ULL << 29),
|
BannedUsers = (1ULL << 30),
|
||||||
Rights = (1ULL << 30),
|
Rights = (1ULL << 31),
|
||||||
PendingRequests = (1ULL << 31),
|
PendingRequests = (1ULL << 32),
|
||||||
Reactions = (1ULL << 32),
|
Reactions = (1ULL << 33),
|
||||||
|
|
||||||
// For channels
|
// For channels
|
||||||
ChannelAmIn = (1ULL << 33),
|
ChannelAmIn = (1ULL << 34),
|
||||||
StickersSet = (1ULL << 34),
|
StickersSet = (1ULL << 35),
|
||||||
ChannelLinkedChat = (1ULL << 35),
|
ChannelLinkedChat = (1ULL << 36),
|
||||||
ChannelLocation = (1ULL << 36),
|
ChannelLocation = (1ULL << 37),
|
||||||
Slowmode = (1ULL << 37),
|
Slowmode = (1ULL << 38),
|
||||||
GroupCall = (1ULL << 38),
|
GroupCall = (1ULL << 39),
|
||||||
|
|
||||||
// For iteration
|
// For iteration
|
||||||
LastUsedBit = (1ULL << 38),
|
LastUsedBit = (1ULL << 39),
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||||
|
|
|
@ -155,6 +155,7 @@ void UserData::setStoriesState(StoriesState state) {
|
||||||
if (const auto history = owner().historyLoaded(this)) {
|
if (const auto history = owner().historyLoaded(this)) {
|
||||||
history->updateChatListEntryPostponed();
|
history->updateChatListEntryPostponed();
|
||||||
}
|
}
|
||||||
|
session().changes().peerUpdated(this, UpdateFlag::StoriesState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -488,6 +488,15 @@ infoMembersList: PeerList(defaultPeerList) {
|
||||||
photoPosition: point(18px, 6px);
|
photoPosition: point(18px, 6px);
|
||||||
namePosition: point(79px, 11px);
|
namePosition: point(79px, 11px);
|
||||||
statusPosition: point(79px, 31px);
|
statusPosition: point(79px, 31px);
|
||||||
|
checkbox: RoundImageCheckbox(defaultPeerListCheckbox) {
|
||||||
|
selectExtendTwice: 1px;
|
||||||
|
imageRadius: 21px;
|
||||||
|
imageSmallRadius: 19px;
|
||||||
|
check: RoundCheckbox(defaultPeerListCheck) {
|
||||||
|
size: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nameFgChecked: contactsNameFg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infoMembersButtonPosition: point(12px, 0px);
|
infoMembersButtonPosition: point(12px, 0px);
|
||||||
|
|
|
@ -50,6 +50,7 @@ Members::Members(
|
||||||
, _controller(controller)
|
, _controller(controller)
|
||||||
, _peer(_controller->key().peer())
|
, _peer(_controller->key().peer())
|
||||||
, _listController(CreateMembersController(controller, _peer)) {
|
, _listController(CreateMembersController(controller, _peer)) {
|
||||||
|
_listController->setStoriesShown(true);
|
||||||
setupHeader();
|
setupHeader();
|
||||||
setupList();
|
setupList();
|
||||||
setContent(_list.data());
|
setContent(_list.data());
|
||||||
|
@ -232,6 +233,7 @@ void Members::setupButtons() {
|
||||||
void Members::setupList() {
|
void Members::setupList() {
|
||||||
auto topSkip = _header ? _header->height() : 0;
|
auto topSkip = _header ? _header->height() : 0;
|
||||||
_listController->setStyleOverrides(&st::infoMembersList);
|
_listController->setStyleOverrides(&st::infoMembersList);
|
||||||
|
_listController->setStoriesShown(true);
|
||||||
_list = object_ptr<ListWidget>(
|
_list = object_ptr<ListWidget>(
|
||||||
this,
|
this,
|
||||||
_listController.get());
|
_listController.get());
|
||||||
|
|
Loading…
Reference in New Issue
Block a user