mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Show stories segments in contacts list.
This commit is contained in:
parent
451c4e3101
commit
9a29807276
4 changed files with 197 additions and 35 deletions
|
@ -261,15 +261,23 @@ void PeerListBox::peerListSetRowChecked(
|
|||
not_null<PeerListRow*> row,
|
||||
bool checked) {
|
||||
if (checked) {
|
||||
addSelectItem(row, anim::type::normal);
|
||||
if (_controller->trackSelectedList()) {
|
||||
addSelectItem(row, anim::type::normal);
|
||||
}
|
||||
PeerListContentDelegate::peerListSetRowChecked(row, checked);
|
||||
peerListUpdateRow(row);
|
||||
|
||||
// This call deletes row from _searchRows.
|
||||
_select->entity()->clearQuery();
|
||||
if (_select) {
|
||||
_select->entity()->clearQuery();
|
||||
}
|
||||
} else {
|
||||
// The itemRemovedCallback will call changeCheckState() here.
|
||||
_select->entity()->removeItem(row->id());
|
||||
if (_select) {
|
||||
_select->entity()->removeItem(row->id());
|
||||
} else {
|
||||
PeerListContentDelegate::peerListSetRowChecked(row, checked);
|
||||
}
|
||||
peerListUpdateRow(row);
|
||||
}
|
||||
}
|
||||
|
@ -1131,6 +1139,24 @@ PeerListRow *PeerListContent::findRow(PeerListRowId id) {
|
|||
return (it == _rowsById.cend()) ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
std::optional<QPoint> PeerListContent::lastRowMousePosition() const {
|
||||
if (!_lastMousePosition) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto point = mapFromGlobal(*_lastMousePosition);
|
||||
auto in = parentWidget()->rect().contains(
|
||||
parentWidget()->mapFromGlobal(*_lastMousePosition));
|
||||
auto rowsPointY = point.y() - rowsTop();
|
||||
const auto index = (in
|
||||
&& rowsPointY >= 0
|
||||
&& rowsPointY < shownRowsCount() * _rowHeight)
|
||||
? (rowsPointY / _rowHeight)
|
||||
: -1;
|
||||
return (index >= 0 && index == _selected.index.value)
|
||||
? QPoint(point.x(), rowsPointY)
|
||||
: std::optional<QPoint>();
|
||||
}
|
||||
|
||||
void PeerListContent::removeRow(not_null<PeerListRow*> row) {
|
||||
auto index = row->absoluteIndex();
|
||||
auto isSearchResult = row->isSearchResult();
|
||||
|
@ -1998,10 +2024,12 @@ void PeerListContent::setSearchQuery(
|
|||
|
||||
bool PeerListContent::submitted() {
|
||||
if (const auto row = getRow(_selected.index)) {
|
||||
_lastMousePosition = std::nullopt;
|
||||
_controller->rowClicked(row);
|
||||
return true;
|
||||
} else if (showingSearch()) {
|
||||
if (const auto row = getRow(RowIndex(0))) {
|
||||
_lastMousePosition = std::nullopt;
|
||||
_controller->rowClicked(row);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -327,6 +327,7 @@ public:
|
|||
virtual void peerListScrollToTop() = 0;
|
||||
virtual int peerListFullRowsCount() = 0;
|
||||
virtual PeerListRow *peerListFindRow(PeerListRowId id) = 0;
|
||||
virtual std::optional<QPoint> peerListLastRowMousePosition() = 0;
|
||||
virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0;
|
||||
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
|
||||
virtual void peerListShowBox(
|
||||
|
@ -503,6 +504,9 @@ public:
|
|||
return delegate()->peerListIsRowChecked(row);
|
||||
}
|
||||
|
||||
virtual bool trackSelectedList() {
|
||||
return true;
|
||||
}
|
||||
virtual bool searchInLocal() {
|
||||
return true;
|
||||
}
|
||||
|
@ -612,6 +616,7 @@ public:
|
|||
void prependRow(std::unique_ptr<PeerListRow> row);
|
||||
void prependRowFromSearchResult(not_null<PeerListRow*> row);
|
||||
PeerListRow *findRow(PeerListRowId id);
|
||||
std::optional<QPoint> lastRowMousePosition() const;
|
||||
void updateRow(not_null<PeerListRow*> row) {
|
||||
updateRow(row, RowIndex());
|
||||
}
|
||||
|
@ -866,6 +871,9 @@ public:
|
|||
PeerListRow *peerListFindRow(PeerListRowId id) override {
|
||||
return _content->findRow(id);
|
||||
}
|
||||
std::optional<QPoint> peerListLastRowMousePosition() override {
|
||||
return _content->lastRowMousePosition();
|
||||
}
|
||||
void peerListUpdateRow(not_null<PeerListRow*> row) override {
|
||||
_content->updateRow(row);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/ui_utility.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -109,6 +110,46 @@ private:
|
|||
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<Ui::RoundImageCheckboxSegment> PrepareSegments(
|
||||
int count,
|
||||
int unread,
|
||||
const QBrush &unreadBrush) {
|
||||
Expects(unread <= count);
|
||||
Expects(count > 0);
|
||||
|
||||
auto result = std::vector<Ui::RoundImageCheckboxSegment>();
|
||||
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();
|
||||
auto gradient = QLinearGradient(
|
||||
QPoint(left + st.photoSize, top),
|
||||
QPoint(left, top + st.photoSize));
|
||||
gradient.setStops({
|
||||
{ 0., st::groupCallLive1->c },
|
||||
{ 1., st::groupCallMuted1->c },
|
||||
});
|
||||
return QBrush(gradient);
|
||||
}
|
||||
|
||||
StoriesRow::StoriesRow(
|
||||
not_null<Main::Session*> session,
|
||||
const QBrush &unread,
|
||||
|
@ -133,26 +174,8 @@ void StoriesRow::updateGradient(QBrush unread) {
|
|||
}
|
||||
|
||||
void StoriesRow::refreshSegments() {
|
||||
Expects(_unreadCount <= _count);
|
||||
Expects(_count > 0);
|
||||
|
||||
auto segments = std::vector<Ui::RoundImageCheckboxSegment>();
|
||||
const auto add = [&](bool unread) {
|
||||
segments.push_back({
|
||||
.brush = unread ? _unread : st::dialogsUnreadBgMuted->b,
|
||||
.width = (unread
|
||||
? st::dialogsStoriesFull.lineTwice / 2.
|
||||
: st::dialogsStoriesFull.lineReadTwice / 2.),
|
||||
});
|
||||
};
|
||||
segments.reserve(_count);
|
||||
for (auto i = 0, count = _count - _unreadCount; i != count; ++i) {
|
||||
add(false);
|
||||
}
|
||||
for (auto i = 0; i != _unreadCount; ++i) {
|
||||
add(true);
|
||||
}
|
||||
setCustomizedCheckSegments(std::move(segments));
|
||||
setCustomizedCheckSegments(
|
||||
PrepareSegments(_count, _unreadCount, _unread));
|
||||
}
|
||||
|
||||
StoriesController::StoriesController(
|
||||
|
@ -164,20 +187,10 @@ StoriesController::StoriesController(
|
|||
, _content(std::move(content))
|
||||
, _open(std::move(open))
|
||||
, _loadMore(std::move(loadMore)) {
|
||||
const auto createGradient = [=] {
|
||||
auto gradient = QLinearGradient(
|
||||
QPoint(10, 0),
|
||||
QPoint(0, 10));
|
||||
gradient.setStops({
|
||||
{ 0., st::groupCallLive1->c },
|
||||
{ 1., st::groupCallMuted1->c },
|
||||
});
|
||||
_unread = QBrush(gradient);
|
||||
};
|
||||
createGradient();
|
||||
_unread = CreateStoriesGradient();
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
createGradient();
|
||||
_unread = CreateStoriesGradient();
|
||||
for (auto i = 0, count = int(delegate()->peerListFullRowsCount())
|
||||
; i != count
|
||||
; ++i) {
|
||||
|
@ -248,6 +261,7 @@ void StoriesController::refresh(const Content &content) {
|
|||
delegate()->peerListAppendRow(std::move(added));
|
||||
delegate()->peerListSetRowChecked(raw, true);
|
||||
raw->applySegments(element);
|
||||
raw->finishCheckedAnimation();
|
||||
}
|
||||
++position;
|
||||
}
|
||||
|
@ -351,6 +365,7 @@ object_ptr<Ui::BoxContent> PrepareContactsBox(
|
|||
auto controller = std::make_unique<ContactsBoxController>(
|
||||
&sessionController->session());
|
||||
controller->setStyleOverrides(&st::contactsWithStories);
|
||||
controller->setStoriesShown(true);
|
||||
const auto raw = controller.get();
|
||||
auto init = [=](not_null<PeerListBox*> box) {
|
||||
using namespace Dialogs::Stories;
|
||||
|
@ -647,6 +662,33 @@ void ContactsBoxController::prepare() {
|
|||
|
||||
prepareViewHook();
|
||||
|
||||
if (_storiesShown) {
|
||||
_storiesUnread = CreateStoriesGradient();
|
||||
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(
|
||||
) | rpl::start_with_next([=] {
|
||||
rebuildRows();
|
||||
|
@ -692,6 +734,14 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createSearchRow(
|
|||
void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
const auto peer = row->peer();
|
||||
if (const auto window = peer->session().tryResolveWindow()) {
|
||||
if (_storiesShown) {
|
||||
const auto point = delegate()->peerListLastRowMousePosition();
|
||||
const auto &st = st::contactsWithStories.item;
|
||||
if (point && point->x() < st.photoPosition.x() + st.photoSize) {
|
||||
window->openPeerStories(peer->id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
window->showPeerHistory(row->peer());
|
||||
}
|
||||
}
|
||||
|
@ -717,6 +767,55 @@ void ContactsBoxController::setSortMode(SortMode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
void ContactsBoxController::setStoriesShown(bool shown) {
|
||||
_storiesShown = shown;
|
||||
}
|
||||
|
||||
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() {
|
||||
switch (_sortMode) {
|
||||
case SortMode::Alphabet: sortByName(); break;
|
||||
|
@ -762,7 +861,15 @@ bool ContactsBoxController::appendRow(not_null<UserData*> user) {
|
|||
return false;
|
||||
}
|
||||
if (auto row = createRow(user)) {
|
||||
const auto raw = row.get();
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
if (_storiesShown) {
|
||||
const auto stories = &session().data().stories();
|
||||
if (const auto source = stories->source(user->id)) {
|
||||
const auto info = source->info();
|
||||
applyRowStories(raw, info.count, info.unreadCount, true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -128,12 +128,16 @@ public:
|
|||
[[nodiscard]] std::unique_ptr<PeerListRow> createSearchRow(
|
||||
not_null<PeerData*> peer) override final;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
bool trackSelectedList() override {
|
||||
return !_storiesShown;
|
||||
}
|
||||
|
||||
enum class SortMode {
|
||||
Alphabet,
|
||||
Online,
|
||||
};
|
||||
void setSortMode(SortMode mode);
|
||||
void setStoriesShown(bool shown);
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user);
|
||||
|
@ -143,18 +147,33 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
struct StoriesCount {
|
||||
int count = 0;
|
||||
int unread = 0;
|
||||
};
|
||||
void sort();
|
||||
void sortByName();
|
||||
void sortByOnline();
|
||||
void rebuildRows();
|
||||
void updateStories();
|
||||
void checkForEmptyRows();
|
||||
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;
|
||||
SortMode _sortMode = SortMode::Alphabet;
|
||||
base::Timer _sortByOnlineTimer;
|
||||
rpl::lifetime _sortByOnlineLifetime;
|
||||
|
||||
QBrush _storiesUnread;
|
||||
base::flat_map<uint64, StoriesCount> _storiesCounts;
|
||||
bool _storiesShown = false;
|
||||
|
||||
};
|
||||
|
||||
class ChooseRecipientBoxController
|
||||
|
|
Loading…
Add table
Reference in a new issue