tdesktop/Telegram/SourceFiles/data/data_messages.h

244 lines
5.9 KiB
C++

/*
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 {
FullMsgId fullId;
TimeId date = 0;
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);
}
};
struct MessagesRange {
MessagePosition from;
MessagePosition 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);
}
};
constexpr auto MinDate = TimeId(0);
constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
constexpr auto MinMessagePosition = MessagePosition{
.fullId = FullMsgId(PeerId(), 1),
.date = MinDate,
};
constexpr auto MaxMessagePosition = MessagePosition{
.fullId = FullMsgId(PeerId(), ServerMaxMsgId - 1),
.date = MaxDate,
};
constexpr auto FullMessagesRange = MessagesRange{
.from = MinMessagePosition,
.till = MaxMessagePosition,
};
constexpr auto UnreadMessagePosition = MessagePosition{
.fullId = FullMsgId(PeerId(), ShowAtUnreadMsgId),
.date = MinDate,
};
struct MessagesSlice {
std::vector<FullMsgId> ids;
FullMsgId nearestToAround;
std::optional<int> skippedBefore;
std::optional<int> skippedAfter;
std::optional<int> fullCount;
};
struct MessagesQuery {
MessagePosition aroundId;
int limitBefore = 0;
int limitAfter = 0;
};
struct MessagesResult {
std::optional<int> count;
std::optional<int> skippedBefore;
std::optional<int> skippedAfter;
base::flat_set<MessagePosition> messageIds;
};
struct MessagesSliceUpdate {
const base::flat_set<MessagePosition> *messages = nullptr;
MessagesRange range;
std::optional<int> count;
};
class MessagesList {
public:
void addOne(MessagePosition messageId);
void addNew(MessagePosition messageId);
void addSlice(
std::vector<MessagePosition> &&messageIds,
MessagesRange noSkipRange,
std::optional<int> count);
void removeOne(MessagePosition messageId);
void removeLessThan(MessagePosition messageId);
void invalidate();
void invalidateBottom();
[[nodiscard]] rpl::producer<MessagesResult> query(
MessagesQuery &&query) const;
[[nodiscard]] rpl::producer<MessagesSliceUpdate> sliceUpdated() const;
[[nodiscard]] MessagesResult snapshot(MessagesQuery &&query) const;
[[nodiscard]] rpl::producer<MessagesResult> viewer(
MessagesQuery &&query) const;
[[nodiscard]] bool empty() const;
private:
struct Slice {
Slice(
base::flat_set<MessagePosition> &&messages,
MessagesRange range);
template <typename Range>
void merge(
const Range &moreMessages,
MessagesRange moreNoSkipRange);
base::flat_set<MessagePosition> messages;
MessagesRange range;
inline bool operator<(const Slice &other) const {
return range.from < other.range.from;
}
};
template <typename Range>
int uniteAndAdd(
MessagesSliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MessagesRange noSkipRange);
template <typename Range>
int addRangeItemsAndCountNew(
MessagesSliceUpdate &update,
const Range &messages,
MessagesRange noSkipRange);
template <typename Range>
void addRange(
const Range &messages,
MessagesRange noSkipRange,
std::optional<int> count,
bool incrementCount = false);
MessagesResult queryFromSlice(
const MessagesQuery &query,
const Slice &slice) const;
MessagesResult queryCurrent(const MessagesQuery &query) const;
std::optional<int> _count;
base::flat_set<Slice> _slices;
rpl::event_stream<MessagesSliceUpdate> _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 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<int> count,
const base::flat_set<MessagePosition> &messageIds,
std::optional<int> skippedBefore = std::nullopt,
std::optional<int> skippedAfter = std::nullopt);
MessagePosition _key;
base::flat_set<MessagePosition> _ids;
MessagesRange _range;
std::optional<int> _fullCount;
std::optional<int> _skippedBefore;
std::optional<int> _skippedAfter;
int _limitBefore = 0;
int _limitAfter = 0;
rpl::event_stream<AroundData> _insufficientAround;
};
} // namespace Data