/* 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 namespace Data { enum class LoadDirection : char { Around, Before, After, }; struct MessagePosition { constexpr MessagePosition() = default; constexpr MessagePosition(TimeId date, FullMsgId fullId) : fullId(fullId) , date(date) { } explicit operator bool() const { return (fullId.msg != 0); } inline constexpr bool operator<(const MessagePosition &other) const { if (date < other.date) { return true; } else if (other.date < date) { return false; } return (fullId < other.fullId); } inline constexpr bool operator>(const MessagePosition &other) const { return other < *this; } inline constexpr bool operator<=(const MessagePosition &other) const { return !(other < *this); } inline constexpr bool operator>=(const MessagePosition &other) const { return !(*this < other); } inline constexpr bool operator==(const MessagePosition &other) const { return (date == other.date) && (fullId == other.fullId); } inline constexpr bool operator!=(const MessagePosition &other) const { return !(*this == other); } FullMsgId fullId; TimeId date = 0; }; struct MessagesRange { constexpr MessagesRange() = default; constexpr MessagesRange(MessagePosition from, MessagePosition till) : from(from) , till(till) { } inline constexpr bool operator==(const MessagesRange &other) const { return (from == other.from) && (till == other.till); } inline constexpr bool operator!=(const MessagesRange &other) const { return !(*this == other); } MessagePosition from; MessagePosition till; }; constexpr auto MinDate = TimeId(0); constexpr auto MaxDate = std::numeric_limits::max(); constexpr auto MinMessagePosition = MessagePosition( MinDate, FullMsgId(NoChannel, 1)); constexpr auto MaxMessagePosition = MessagePosition( MaxDate, FullMsgId(NoChannel, ServerMaxMsgId - 1)); constexpr auto FullMessagesRange = MessagesRange( MinMessagePosition, MaxMessagePosition); constexpr auto UnreadMessagePosition = MessagePosition( MinDate, FullMsgId(NoChannel, ShowAtUnreadMsgId)); struct MessagesSlice { std::vector ids; std::optional skippedBefore; std::optional skippedAfter; std::optional fullCount; }; struct MessagesQuery { MessagesQuery( MessagePosition aroundId, int limitBefore, int limitAfter) : aroundId(aroundId) , limitBefore(limitBefore) , limitAfter(limitAfter) { } MessagePosition aroundId; int limitBefore = 0; int limitAfter = 0; }; struct MessagesResult { std::optional count; std::optional skippedBefore; std::optional skippedAfter; base::flat_set messageIds; }; struct MessagesSliceUpdate { const base::flat_set *messages = nullptr; MessagesRange range; std::optional count; }; class MessagesList { public: void addNew(MessagePosition messageId); void addSlice( std::vector &&messageIds, MessagesRange noSkipRange, std::optional count); void removeOne(MessagePosition messageId); void removeAll(ChannelId channelId); void invalidate(); void invalidateBottom(); rpl::producer query(MessagesQuery &&query) const; rpl::producer sliceUpdated() const; private: struct Slice { Slice( base::flat_set &&messages, MessagesRange range); template void merge( const Range &moreMessages, MessagesRange moreNoSkipRange); base::flat_set messages; MessagesRange range; inline bool operator<(const Slice &other) const { return range.from < other.range.from; } }; template int uniteAndAdd( MessagesSliceUpdate &update, base::flat_set::iterator uniteFrom, base::flat_set::iterator uniteTill, const Range &messages, MessagesRange noSkipRange); template int addRangeItemsAndCountNew( MessagesSliceUpdate &update, const Range &messages, MessagesRange noSkipRange); template void addRange( const Range &messages, MessagesRange noSkipRange, std::optional count, bool incrementCount = false); MessagesResult queryFromSlice( const MessagesQuery &query, const Slice &slice) const; std::optional _count; base::flat_set _slices; rpl::event_stream _sliceUpdated; }; class MessagesSliceBuilder { public: using Key = MessagePosition; MessagesSliceBuilder(Key key, int limitBefore, int limitAfter); bool applyInitial(const MessagesResult &result); bool applyUpdate(const MessagesSliceUpdate &update); bool removeOne(MessagePosition messageId); bool removeFromChannel(ChannelId channelId); bool removeAll(); bool invalidated(); bool bottomInvalidated(); void checkInsufficient(); struct AroundData { MessagePosition aroundId; LoadDirection direction = LoadDirection::Around; inline bool operator<(const AroundData &other) const { return (aroundId < other.aroundId) || ((aroundId == other.aroundId) && (direction < other.direction)); } }; auto insufficientAround() const { return _insufficientAround.events(); } MessagesSlice snapshot() const; private: enum class RequestDirection { Before, After, }; void requestMessages(RequestDirection direction); void requestMessagesCount(); void fillSkippedAndSliceToLimits(); void sliceToLimits(); void mergeSliceData( std::optional count, const base::flat_set &messageIds, std::optional skippedBefore = std::nullopt, std::optional skippedAfter = std::nullopt); MessagePosition _key; base::flat_set _ids; MessagesRange _range; std::optional _fullCount; std::optional _skippedBefore; std::optional _skippedAfter; int _limitBefore = 0; int _limitAfter = 0; rpl::event_stream _insufficientAround; }; } // namespace Data