mirror of https://github.com/procxx/kepka.git
Read feed while scrolling.
This commit is contained in:
parent
a7f67c4bc9
commit
0c5efb935d
|
@ -1024,6 +1024,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_user_action_upload_file" = "{user} is sending a file";
|
"lng_user_action_upload_file" = "{user} is sending a file";
|
||||||
"lng_unread_bar#one" = "{count} unread message";
|
"lng_unread_bar#one" = "{count} unread message";
|
||||||
"lng_unread_bar#other" = "{count} unread messages";
|
"lng_unread_bar#other" = "{count} unread messages";
|
||||||
|
"lng_unread_bar_some" = "Unread messages";
|
||||||
|
|
||||||
"lng_maps_point" = "Location";
|
"lng_maps_point" = "Location";
|
||||||
"lng_save_photo" = "Save image";
|
"lng_save_photo" = "Save image";
|
||||||
|
|
|
@ -55,6 +55,7 @@ constexpr auto kSharedMediaLimit = 100;
|
||||||
constexpr auto kFeedMessagesLimit = 50;
|
constexpr auto kFeedMessagesLimit = 50;
|
||||||
constexpr auto kReadFeaturedSetsTimeout = TimeMs(1000);
|
constexpr auto kReadFeaturedSetsTimeout = TimeMs(1000);
|
||||||
constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
|
constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
|
||||||
|
constexpr auto kFeedReadTimeout = TimeMs(1000);
|
||||||
|
|
||||||
bool IsSilentPost(not_null<HistoryItem*> item, bool silent) {
|
bool IsSilentPost(not_null<HistoryItem*> item, bool silent) {
|
||||||
const auto history = item->history();
|
const auto history = item->history();
|
||||||
|
@ -133,7 +134,8 @@ ApiWrap::ApiWrap(not_null<AuthSession*> session)
|
||||||
, _webPagesTimer([this] { resolveWebPages(); })
|
, _webPagesTimer([this] { resolveWebPages(); })
|
||||||
, _draftsSaveTimer([this] { saveDraftsToCloud(); })
|
, _draftsSaveTimer([this] { saveDraftsToCloud(); })
|
||||||
, _featuredSetsReadTimer([this] { readFeaturedSets(); })
|
, _featuredSetsReadTimer([this] { readFeaturedSets(); })
|
||||||
, _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout)) {
|
, _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout))
|
||||||
|
, _feedReadTimer([this] { readFeeds(); }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::requestChangelog(
|
void ApiWrap::requestChangelog(
|
||||||
|
@ -3076,9 +3078,12 @@ void ApiWrap::feedMessagesDone(
|
||||||
if (data.has_read_max_position()) {
|
if (data.has_read_max_position()) {
|
||||||
return Data::FeedPositionFromMTP(data.vread_max_position);
|
return Data::FeedPositionFromMTP(data.vread_max_position);
|
||||||
} else if (!messageId) {
|
} else if (!messageId) {
|
||||||
return ids.empty()
|
const auto result = ids.empty()
|
||||||
? noSkipRange.till
|
? noSkipRange.till
|
||||||
: ids.back();
|
: ids.back();
|
||||||
|
return Data::MessagePosition(
|
||||||
|
result.date,
|
||||||
|
FullMsgId(result.fullId.channel, result.fullId.msg - 1));
|
||||||
}
|
}
|
||||||
return Data::MessagePosition();
|
return Data::MessagePosition();
|
||||||
}();
|
}();
|
||||||
|
@ -3712,6 +3717,56 @@ void ApiWrap::readServerHistoryForce(not_null<History*> history) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::readFeed(
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
Data::MessagePosition position) {
|
||||||
|
const auto already = feed->unreadPosition();
|
||||||
|
if (already && already >= position) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
feed->setUnreadPosition(position);
|
||||||
|
if (!_feedReadsDelayed.contains(feed)) {
|
||||||
|
if (_feedReadsDelayed.empty()) {
|
||||||
|
_feedReadTimer.callOnce(kFeedReadTimeout);
|
||||||
|
}
|
||||||
|
_feedReadsDelayed.emplace(feed, getms(true) + kFeedReadTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::readFeeds() {
|
||||||
|
auto delay = kFeedReadTimeout;
|
||||||
|
const auto now = getms(true);
|
||||||
|
for (auto i = begin(_feedReadsDelayed); i != end(_feedReadsDelayed);) {
|
||||||
|
const auto [feed, time] = *i;
|
||||||
|
if (time > now) {
|
||||||
|
accumulate_min(delay, time - now);
|
||||||
|
++i;
|
||||||
|
} else if (_feedReadRequests.contains(feed)) {
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
const auto position = feed->unreadPosition();
|
||||||
|
const auto requestId = request(MTPchannels_ReadFeed(
|
||||||
|
MTP_int(feed->id()),
|
||||||
|
MTP_feedPosition(
|
||||||
|
MTP_int(position.date),
|
||||||
|
MTP_peerChannel(MTP_int(position.fullId.channel)),
|
||||||
|
MTP_int(position.fullId.msg))
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
applyUpdates(result);
|
||||||
|
_feedReadRequests.remove(feed);
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
_feedReadRequests.remove(feed);
|
||||||
|
}).send();
|
||||||
|
_feedReadRequests.emplace(feed, requestId);
|
||||||
|
|
||||||
|
i = _feedReadsDelayed.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_feedReadRequests.empty()) {
|
||||||
|
_feedReadTimer.callOnce(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::sendReadRequest(not_null<PeerData*> peer, MsgId upTo) {
|
void ApiWrap::sendReadRequest(not_null<PeerData*> peer, MsgId upTo) {
|
||||||
const auto requestId = [&] {
|
const auto requestId = [&] {
|
||||||
const auto finished = [=] {
|
const auto finished = [=] {
|
||||||
|
|
|
@ -226,6 +226,9 @@ public:
|
||||||
void shareContact(not_null<UserData*> user, const SendOptions &options);
|
void shareContact(not_null<UserData*> user, const SendOptions &options);
|
||||||
void readServerHistory(not_null<History*> history);
|
void readServerHistory(not_null<History*> history);
|
||||||
void readServerHistoryForce(not_null<History*> history);
|
void readServerHistoryForce(not_null<History*> history);
|
||||||
|
void readFeed(
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
Data::MessagePosition position);
|
||||||
|
|
||||||
void sendVoiceMessage(
|
void sendVoiceMessage(
|
||||||
QByteArray result,
|
QByteArray result,
|
||||||
|
@ -399,6 +402,8 @@ private:
|
||||||
bool silent,
|
bool silent,
|
||||||
uint64 randomId);
|
uint64 randomId);
|
||||||
|
|
||||||
|
void readFeeds();
|
||||||
|
|
||||||
not_null<AuthSession*> _session;
|
not_null<AuthSession*> _session;
|
||||||
|
|
||||||
MessageDataRequests _messageDataRequests;
|
MessageDataRequests _messageDataRequests;
|
||||||
|
@ -511,6 +516,7 @@ private:
|
||||||
};
|
};
|
||||||
base::flat_map<not_null<PeerData*>, ReadRequest> _readRequests;
|
base::flat_map<not_null<PeerData*>, ReadRequest> _readRequests;
|
||||||
base::flat_map<not_null<PeerData*>, MsgId> _readRequestsPending;
|
base::flat_map<not_null<PeerData*>, MsgId> _readRequestsPending;
|
||||||
|
|
||||||
std::unique_ptr<TaskQueue> _fileLoader;
|
std::unique_ptr<TaskQueue> _fileLoader;
|
||||||
base::flat_map<uint64, std::shared_ptr<SendingAlbum>> _sendingAlbums;
|
base::flat_map<uint64, std::shared_ptr<SendingAlbum>> _sendingAlbums;
|
||||||
|
|
||||||
|
@ -518,4 +524,8 @@ private:
|
||||||
|
|
||||||
rpl::event_stream<uint64> _stickerSetInstalled;
|
rpl::event_stream<uint64> _stickerSetInstalled;
|
||||||
|
|
||||||
|
base::flat_map<not_null<Data::Feed*>, TimeMs> _feedReadsDelayed;
|
||||||
|
base::flat_map<not_null<Data::Feed*>, mtpRequestId> _feedReadRequests;
|
||||||
|
base::Timer _feedReadTimer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "storage/storage_feed_messages.h"
|
#include "storage/storage_feed_messages.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
@ -241,6 +242,40 @@ void Widget::listSelectionChanged(HistoryView::SelectedItems &&items) {
|
||||||
_topBar->showSelected(state);
|
_topBar->showSelected(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::listVisibleItemsChanged(HistoryItemsList &&items) {
|
||||||
|
const auto reversed = ranges::view::reverse(items);
|
||||||
|
const auto good = ranges::find_if(reversed, [](auto item) {
|
||||||
|
return IsServerMsgId(item->id);
|
||||||
|
});
|
||||||
|
if (good != end(reversed)) {
|
||||||
|
Auth().api().readFeed(_feed, (*good)->position());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base::optional<int> Widget::listUnreadBarView(
|
||||||
|
const std::vector<not_null<Element*>> &elements) {
|
||||||
|
const auto position = _feed->unreadPosition();
|
||||||
|
if (!position || elements.empty()) {
|
||||||
|
return base::none;
|
||||||
|
}
|
||||||
|
const auto minimal = ranges::upper_bound(
|
||||||
|
elements,
|
||||||
|
position,
|
||||||
|
std::less<>(),
|
||||||
|
[](auto view) { return view->data()->position(); });
|
||||||
|
if (minimal == end(elements)) {
|
||||||
|
return base::none;
|
||||||
|
}
|
||||||
|
const auto view = *minimal;
|
||||||
|
const auto unreadMessagesHeight = elements.back()->y()
|
||||||
|
+ elements.back()->height()
|
||||||
|
- view->y();
|
||||||
|
if (unreadMessagesHeight < _scroll->height()) {
|
||||||
|
return base::none;
|
||||||
|
}
|
||||||
|
return base::make_optional(int(minimal - begin(elements)));
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||||
auto result = std::make_unique<Memento>(_feed);
|
auto result = std::make_unique<Memento>(_feed);
|
||||||
saveState(result.get());
|
saveState(result.get());
|
||||||
|
@ -272,7 +307,9 @@ void Widget::resizeEvent(QResizeEvent *e) {
|
||||||
void Widget::updateControlsGeometry() {
|
void Widget::updateControlsGeometry() {
|
||||||
const auto contentWidth = width();
|
const auto contentWidth = width();
|
||||||
|
|
||||||
const auto newScrollTop = _scroll->scrollTop() + topDelta();
|
const auto newScrollTop = _scroll->isHidden()
|
||||||
|
? base::none
|
||||||
|
: base::make_optional(_scroll->scrollTop() + topDelta());
|
||||||
_topBar->resizeToWidth(contentWidth);
|
_topBar->resizeToWidth(contentWidth);
|
||||||
_topBarShadow->resize(contentWidth, st::lineWidth);
|
_topBarShadow->resize(contentWidth, st::lineWidth);
|
||||||
|
|
||||||
|
@ -282,14 +319,14 @@ void Widget::updateControlsGeometry() {
|
||||||
- _showNext->height();
|
- _showNext->height();
|
||||||
const auto scrollSize = QSize(contentWidth, scrollHeight);
|
const auto scrollSize = QSize(contentWidth, scrollHeight);
|
||||||
if (_scroll->size() != scrollSize) {
|
if (_scroll->size() != scrollSize) {
|
||||||
|
_skipScrollEvent = true;
|
||||||
_scroll->resize(scrollSize);
|
_scroll->resize(scrollSize);
|
||||||
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
|
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
|
||||||
//_inner->restoreScrollPosition();
|
_skipScrollEvent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_scroll->isHidden()) {
|
if (!_scroll->isHidden()) {
|
||||||
if (topDelta()) {
|
if (newScrollTop) {
|
||||||
_scroll->scrollToY(newScrollTop);
|
_scroll->scrollToY(*newScrollTop);
|
||||||
}
|
}
|
||||||
updateInnerVisibleArea();
|
updateInnerVisibleArea();
|
||||||
}
|
}
|
||||||
|
@ -320,12 +357,16 @@ void Widget::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::onScroll() {
|
void Widget::onScroll() {
|
||||||
|
if (_skipScrollEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
updateInnerVisibleArea();
|
updateInnerVisibleArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::updateInnerVisibleArea() {
|
void Widget::updateInnerVisibleArea() {
|
||||||
const auto scrollTop = _scroll->scrollTop();
|
const auto scrollTop = _scroll->scrollTop();
|
||||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::showAnimatedHook(
|
void Widget::showAnimatedHook(
|
||||||
|
|
|
@ -21,6 +21,7 @@ class FlatButton;
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
class ListWidget;
|
class ListWidget;
|
||||||
class TopBarWidget;
|
class TopBarWidget;
|
||||||
|
class Element;
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
||||||
namespace HistoryFeed {
|
namespace HistoryFeed {
|
||||||
|
@ -31,6 +32,8 @@ class Widget final
|
||||||
: public Window::SectionWidget
|
: public Window::SectionWidget
|
||||||
, public HistoryView::ListDelegate {
|
, public HistoryView::ListDelegate {
|
||||||
public:
|
public:
|
||||||
|
using Element = HistoryView::Element;
|
||||||
|
|
||||||
Widget(
|
Widget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::Controller*> controller,
|
not_null<Window::Controller*> controller,
|
||||||
|
@ -75,6 +78,9 @@ public:
|
||||||
not_null<HistoryItem*> second) override;
|
not_null<HistoryItem*> second) override;
|
||||||
void listSelectionChanged(
|
void listSelectionChanged(
|
||||||
HistoryView::SelectedItems &&items) override;
|
HistoryView::SelectedItems &&items) override;
|
||||||
|
void listVisibleItemsChanged(HistoryItemsList &&items) override;
|
||||||
|
base::optional<int> listUnreadBarView(
|
||||||
|
const std::vector<not_null<Element*>> &elements) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
@ -104,6 +110,7 @@ private:
|
||||||
object_ptr<HistoryView::TopBarWidget> _topBar;
|
object_ptr<HistoryView::TopBarWidget> _topBar;
|
||||||
object_ptr<Ui::PlainShadow> _topBarShadow;
|
object_ptr<Ui::PlainShadow> _topBarShadow;
|
||||||
object_ptr<Ui::FlatButton> _showNext;
|
object_ptr<Ui::FlatButton> _showNext;
|
||||||
|
bool _skipScrollEvent = false;
|
||||||
bool _undefinedAroundPosition = false;
|
bool _undefinedAroundPosition = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,11 +57,14 @@ TextSelection ShiftItemSelection(
|
||||||
return ShiftItemSelection(selection, byText.length());
|
return ShiftItemSelection(selection, byText.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnreadBar::init(int count) {
|
void UnreadBar::init(int newCount) {
|
||||||
if (freezed) {
|
if (freezed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
text = lng_unread_bar(lt_count, count);
|
count = newCount;
|
||||||
|
text = (count == kCountUnknown)
|
||||||
|
? lang(lng_unread_bar_some)
|
||||||
|
: lng_unread_bar(lt_count, count);
|
||||||
width = st::semiboldFont->width(text);
|
width = st::semiboldFont->width(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,8 +314,6 @@ void Element::destroyUnreadBar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Element::setUnreadBarCount(int count) {
|
void Element::setUnreadBarCount(int count) {
|
||||||
Expects(count > 0);
|
|
||||||
|
|
||||||
const auto changed = AddComponents(UnreadBar::Bit());
|
const auto changed = AddComponents(UnreadBar::Bit());
|
||||||
const auto bar = Get<UnreadBar>();
|
const auto bar = Get<UnreadBar>();
|
||||||
if (bar->freezed) {
|
if (bar->freezed) {
|
||||||
|
|
|
@ -61,15 +61,18 @@ TextSelection ShiftItemSelection(
|
||||||
// Any HistoryView::Element can have this Component for
|
// Any HistoryView::Element can have this Component for
|
||||||
// displaying the unread messages bar above the message.
|
// displaying the unread messages bar above the message.
|
||||||
struct UnreadBar : public RuntimeComponent<UnreadBar, Element> {
|
struct UnreadBar : public RuntimeComponent<UnreadBar, Element> {
|
||||||
void init(int count);
|
void init(int newCount);
|
||||||
|
|
||||||
static int height();
|
static int height();
|
||||||
static int marginTop();
|
static int marginTop();
|
||||||
|
|
||||||
void paint(Painter &p, int y, int w) const;
|
void paint(Painter &p, int y, int w) const;
|
||||||
|
|
||||||
|
static constexpr auto kCountUnknown = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
QString text;
|
QString text;
|
||||||
int width = 0;
|
int width = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
// If unread bar is freezed the new messages do not
|
// If unread bar is freezed the new messages do not
|
||||||
// increment the counter displayed by this bar.
|
// increment the counter displayed by this bar.
|
||||||
|
@ -150,8 +153,6 @@ public:
|
||||||
|
|
||||||
bool computeIsAttachToPrevious(not_null<Element*> previous);
|
bool computeIsAttachToPrevious(not_null<Element*> previous);
|
||||||
|
|
||||||
// count > 0 - creates the unread bar if necessary and
|
|
||||||
// sets unread messages count if bar is not freezed yet
|
|
||||||
void setUnreadBarCount(int count);
|
void setUnreadBarCount(int count);
|
||||||
void destroyUnreadBar();
|
void destroyUnreadBar();
|
||||||
|
|
||||||
|
|
|
@ -238,6 +238,7 @@ ListWidget::ListWidget(
|
||||||
, _context(_delegate->listContext())
|
, _context(_delegate->listContext())
|
||||||
, _itemAverageHeight(itemMinimalHeight())
|
, _itemAverageHeight(itemMinimalHeight())
|
||||||
, _scrollDateCheck([this] { scrollDateCheck(); })
|
, _scrollDateCheck([this] { scrollDateCheck(); })
|
||||||
|
, _applyUpdatedScrollState([this] { applyUpdatedScrollState(); })
|
||||||
, _selectEnabled(_delegate->listAllowsMultiSelect()) {
|
, _selectEnabled(_delegate->listAllowsMultiSelect()) {
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); });
|
_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); });
|
||||||
|
@ -320,10 +321,21 @@ void ListWidget::refreshRows() {
|
||||||
updateAroundPositionFromRows();
|
updateAroundPositionFromRows();
|
||||||
|
|
||||||
updateItemsGeometry();
|
updateItemsGeometry();
|
||||||
|
checkUnreadBarCreation();
|
||||||
restoreScrollState();
|
restoreScrollState();
|
||||||
mouseActionUpdate(QCursor::pos());
|
mouseActionUpdate(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListWidget::checkUnreadBarCreation() {
|
||||||
|
if (!_unreadBarElement) {
|
||||||
|
if (const auto index = _delegate->listUnreadBarView(_items)) {
|
||||||
|
_unreadBarElement = _items[*index].get();
|
||||||
|
_unreadBarElement->setUnreadBarCount(UnreadBar::kCountUnknown);
|
||||||
|
refreshAttachmentsAtIndex(*index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ListWidget::saveScrollState() {
|
void ListWidget::saveScrollState() {
|
||||||
if (!_scrollTopState.item) {
|
if (!_scrollTopState.item) {
|
||||||
_scrollTopState = countScrollState();
|
_scrollTopState = countScrollState();
|
||||||
|
@ -331,9 +343,16 @@ void ListWidget::saveScrollState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::restoreScrollState() {
|
void ListWidget::restoreScrollState() {
|
||||||
if (_items.empty() || !_scrollTopState.item) {
|
if (_items.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!_scrollTopState.item) {
|
||||||
|
if (!_unreadBarElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_scrollTopState.item = _unreadBarElement->data()->position();
|
||||||
|
_scrollTopState.shift = st::lineWidth + st::historyUnreadBarMargin;
|
||||||
|
}
|
||||||
const auto index = findNearestItem(_scrollTopState.item);
|
const auto index = findNearestItem(_scrollTopState.item);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
const auto view = _items[index];
|
const auto view = _items[index];
|
||||||
|
@ -393,21 +412,57 @@ int ListWidget::findNearestItem(Data::MessagePosition position) const {
|
||||||
: int(after - begin(_items));
|
: int(after - begin(_items));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HistoryItemsList ListWidget::collectVisibleItems() const {
|
||||||
|
auto result = HistoryItemsList();
|
||||||
|
const auto from = std::lower_bound(
|
||||||
|
begin(_items),
|
||||||
|
end(_items),
|
||||||
|
_visibleTop,
|
||||||
|
[this](auto &elem, int top) {
|
||||||
|
return this->itemTop(elem) + elem->height() <= top;
|
||||||
|
});
|
||||||
|
const auto to = std::lower_bound(
|
||||||
|
begin(_items),
|
||||||
|
end(_items),
|
||||||
|
_visibleBottom,
|
||||||
|
[this](auto &elem, int bottom) {
|
||||||
|
return this->itemTop(elem) < bottom;
|
||||||
|
});
|
||||||
|
result.reserve(to - from);
|
||||||
|
for (auto i = from; i != to; ++i) {
|
||||||
|
result.push_back((*i)->data());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void ListWidget::visibleTopBottomUpdated(
|
void ListWidget::visibleTopBottomUpdated(
|
||||||
int visibleTop,
|
int visibleTop,
|
||||||
int visibleBottom) {
|
int visibleBottom) {
|
||||||
auto scrolledUp = (visibleTop < _visibleTop);
|
if (!(visibleTop < visibleBottom)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto initializing = !(_visibleTop < _visibleBottom);
|
||||||
|
const auto scrolledUp = (visibleTop < _visibleTop);
|
||||||
_visibleTop = visibleTop;
|
_visibleTop = visibleTop;
|
||||||
_visibleBottom = visibleBottom;
|
_visibleBottom = visibleBottom;
|
||||||
|
|
||||||
|
if (initializing) {
|
||||||
|
checkUnreadBarCreation();
|
||||||
|
}
|
||||||
updateVisibleTopItem();
|
updateVisibleTopItem();
|
||||||
checkMoveToOtherViewer();
|
|
||||||
if (scrolledUp) {
|
if (scrolledUp) {
|
||||||
_scrollDateCheck.call();
|
_scrollDateCheck.call();
|
||||||
} else {
|
} else {
|
||||||
scrollDateHideByTimer();
|
scrollDateHideByTimer();
|
||||||
}
|
}
|
||||||
_controller->floatPlayerAreaUpdated().notify(true);
|
_controller->floatPlayerAreaUpdated().notify(true);
|
||||||
|
_applyUpdatedScrollState.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListWidget::applyUpdatedScrollState() {
|
||||||
|
checkMoveToOtherViewer();
|
||||||
|
_delegate->listVisibleItemsChanged(collectVisibleItems());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::updateVisibleTopItem() {
|
void ListWidget::updateVisibleTopItem() {
|
||||||
|
@ -938,11 +993,16 @@ void ListWidget::updateItemsGeometry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::updateSize() {
|
void ListWidget::updateSize() {
|
||||||
TWidget::resizeToWidth(width());
|
resizeToWidth(width(), _minHeight);
|
||||||
restoreScrollPosition();
|
|
||||||
updateVisibleTopItem();
|
updateVisibleTopItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListWidget::resizeToWidth(int newWidth, int minHeight) {
|
||||||
|
_minHeight = minHeight;
|
||||||
|
TWidget::resizeToWidth(newWidth);
|
||||||
|
restoreScrollPosition();
|
||||||
|
}
|
||||||
|
|
||||||
int ListWidget::resizeGetHeight(int newWidth) {
|
int ListWidget::resizeGetHeight(int newWidth) {
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -963,7 +1023,9 @@ int ListWidget::resizeGetHeight(int newWidth) {
|
||||||
}
|
}
|
||||||
_itemsWidth = newWidth;
|
_itemsWidth = newWidth;
|
||||||
_itemsHeight = newHeight;
|
_itemsHeight = newHeight;
|
||||||
_itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom) ? (_minHeight - _itemsHeight - st::historyPaddingBottom) : 0;
|
_itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom)
|
||||||
|
? (_minHeight - _itemsHeight - st::historyPaddingBottom)
|
||||||
|
: 0;
|
||||||
return _itemsTop + _itemsHeight + st::historyPaddingBottom;
|
return _itemsTop + _itemsHeight + st::historyPaddingBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2131,6 +2193,18 @@ void ListWidget::viewReplaced(not_null<const Element*> was, Element *now) {
|
||||||
if (_visibleTopItem == was) _visibleTopItem = now;
|
if (_visibleTopItem == was) _visibleTopItem = now;
|
||||||
if (_scrollDateLastItem == was) _scrollDateLastItem = now;
|
if (_scrollDateLastItem == was) _scrollDateLastItem = now;
|
||||||
if (_overElement == was) _overElement = now;
|
if (_overElement == was) _overElement = now;
|
||||||
|
if (_unreadBarElement == was) {
|
||||||
|
const auto bar = _unreadBarElement->Get<UnreadBar>();
|
||||||
|
const auto count = bar ? bar->count : 0;
|
||||||
|
const auto freezed = bar ? bar->freezed : false;
|
||||||
|
_unreadBarElement = now;
|
||||||
|
if (now && count) {
|
||||||
|
_unreadBarElement->setUnreadBarCount(count);
|
||||||
|
if (freezed) {
|
||||||
|
_unreadBarElement->setUnreadBarFreezed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
|
|
|
@ -61,6 +61,9 @@ public:
|
||||||
not_null<HistoryItem*> first,
|
not_null<HistoryItem*> first,
|
||||||
not_null<HistoryItem*> second) = 0;
|
not_null<HistoryItem*> second) = 0;
|
||||||
virtual void listSelectionChanged(SelectedItems &&items) = 0;
|
virtual void listSelectionChanged(SelectedItems &&items) = 0;
|
||||||
|
virtual void listVisibleItemsChanged(HistoryItemsList &&items) = 0;
|
||||||
|
virtual base::optional<int> listUnreadBarView(
|
||||||
|
const std::vector<not_null<Element*>> &elements) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,10 +130,7 @@ public:
|
||||||
// Set the correct scroll position after being resized.
|
// Set the correct scroll position after being resized.
|
||||||
void restoreScrollPosition();
|
void restoreScrollPosition();
|
||||||
|
|
||||||
void resizeToWidth(int newWidth, int minHeight) {
|
void resizeToWidth(int newWidth, int minHeight);
|
||||||
_minHeight = minHeight;
|
|
||||||
return TWidget::resizeToWidth(newWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveState(not_null<ListMemento*> memento);
|
void saveState(not_null<ListMemento*> memento);
|
||||||
void restoreState(not_null<ListMemento*> memento);
|
void restoreState(not_null<ListMemento*> memento);
|
||||||
|
@ -264,6 +264,7 @@ private:
|
||||||
Element *strictFindItemByY(int y) const;
|
Element *strictFindItemByY(int y) const;
|
||||||
int findNearestItem(Data::MessagePosition position) const;
|
int findNearestItem(Data::MessagePosition position) const;
|
||||||
void viewReplaced(not_null<const Element*> was, Element *now);
|
void viewReplaced(not_null<const Element*> was, Element *now);
|
||||||
|
HistoryItemsList collectVisibleItems() const;
|
||||||
|
|
||||||
void checkMoveToOtherViewer();
|
void checkMoveToOtherViewer();
|
||||||
void updateVisibleTopItem();
|
void updateVisibleTopItem();
|
||||||
|
@ -352,6 +353,8 @@ private:
|
||||||
TextSelection computeRenderSelection(
|
TextSelection computeRenderSelection(
|
||||||
not_null<const SelectedMap*> selected,
|
not_null<const SelectedMap*> selected,
|
||||||
not_null<const Element*> view) const;
|
not_null<const Element*> view) const;
|
||||||
|
void checkUnreadBarCreation();
|
||||||
|
void applyUpdatedScrollState();
|
||||||
|
|
||||||
// This function finds all history items that are displayed and calls template method
|
// This function finds all history items that are displayed and calls template method
|
||||||
// for each found message (in given direction) in the passed history with passed top offset.
|
// for each found message (in given direction) in the passed history with passed top offset.
|
||||||
|
@ -409,6 +412,9 @@ private:
|
||||||
base::Timer _scrollDateHideTimer;
|
base::Timer _scrollDateHideTimer;
|
||||||
Element *_scrollDateLastItem = nullptr;
|
Element *_scrollDateLastItem = nullptr;
|
||||||
int _scrollDateLastItemTop = 0;
|
int _scrollDateLastItemTop = 0;
|
||||||
|
SingleQueuedInvokation _applyUpdatedScrollState;
|
||||||
|
|
||||||
|
Element *_unreadBarElement = nullptr;
|
||||||
|
|
||||||
MouseAction _mouseAction = MouseAction::None;
|
MouseAction _mouseAction = MouseAction::None;
|
||||||
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
||||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
|
#include "data/data_feed.h"
|
||||||
#include "ui/special_buttons.h"
|
#include "ui/special_buttons.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
|
@ -4676,7 +4677,17 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
|
||||||
case mtpc_updateReadFeed: {
|
case mtpc_updateReadFeed: {
|
||||||
const auto &d = update.c_updateReadFeed();
|
const auto &d = update.c_updateReadFeed();
|
||||||
const auto feedId = d.vfeed_id.v;
|
const auto feedId = d.vfeed_id.v;
|
||||||
// #TODO feeds
|
if (const auto feed = Auth().data().feedLoaded(feedId)) {
|
||||||
|
feed->setUnreadPosition(
|
||||||
|
Data::FeedPositionFromMTP(d.vmax_position));
|
||||||
|
if (d.has_unread_count() && d.has_unread_muted_count()) {
|
||||||
|
feed->setUnreadCounts(
|
||||||
|
d.vunread_count.v,
|
||||||
|
d.vunread_muted_count.v);
|
||||||
|
} else {
|
||||||
|
Auth().api().requestDialogEntry(feed);
|
||||||
|
}
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
// Deleted messages.
|
// Deleted messages.
|
||||||
|
|
Loading…
Reference in New Issue