Implement chats preview, show from unread.

This commit is contained in:
John Preston 2024-05-03 14:47:08 +04:00
parent 68df8448a2
commit f223ae7eee
16 changed files with 570 additions and 96 deletions

View file

@ -539,6 +539,8 @@ PRIVATE
data/data_groups.h
data/data_histories.cpp
data/data_histories.h
data/data_history_messages.cpp
data/data_history_messages.h
data/data_lastseen_status.h
data/data_location.cpp
data/data_location.h

View file

@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_chat_filters.h"
#include "data/data_histories.h"
#include "data/data_history_messages.h"
#include "core/core_cloud_password.h"
#include "core/application.h"
#include "base/unixtime.h"
@ -3078,6 +3079,46 @@ void ApiWrap::resolveJumpToHistoryDate(
}
}
void ApiWrap::requestHistory(
not_null<History*> history,
MsgId messageId,
SliceType slice) {
const auto peer = history->peer;
const auto key = HistoryRequest{
peer,
messageId,
slice,
};
if (_historyRequests.contains(key)) {
return;
}
const auto prepared = Api::PrepareHistoryRequest(peer, messageId, slice);
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::History;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
return request(
std::move(prepared)
).done([=](const Api::HistoryRequestResult &result) {
_historyRequests.remove(key);
auto parsed = Api::ParseHistoryResult(
peer,
messageId,
slice,
result);
history->messages().addSlice(
std::move(parsed.messageIds),
parsed.noSkipRange,
parsed.fullCount);
finish();
}).fail([=] {
_historyRequests.remove(key);
finish();
}).send();
});
_historyRequests.emplace(key);
}
void ApiWrap::requestSharedMedia(
not_null<PeerData*> peer,
MsgId topicRootId,

View file

@ -274,6 +274,10 @@ public:
Fn<void(not_null<PeerData*>, MsgId)> callback);
using SliceType = Data::LoadDirection;
void requestHistory(
not_null<History*> history,
MsgId messageId,
SliceType slice);
void requestSharedMedia(
not_null<PeerData*> peer,
MsgId topicRootId,
@ -511,7 +515,8 @@ private:
not_null<PeerData*> peer,
bool justClear,
bool revoke);
void applyAffectedMessages(const MTPmessages_AffectedMessages &result) const;
void applyAffectedMessages(
const MTPmessages_AffectedMessages &result) const;
void deleteAllFromParticipantSend(
not_null<ChannelData*> channel,
@ -645,6 +650,17 @@ private:
};
base::flat_set<SharedMediaRequest> _sharedMediaRequests;
struct HistoryRequest {
not_null<PeerData*> peer;
MsgId aroundId = 0;
SliceType sliceType = {};
friend inline auto operator<=>(
const HistoryRequest&,
const HistoryRequest&) = default;
};
base::flat_set<HistoryRequest> _historyRequests;
std::unique_ptr<DialogsLoadState> _dialogsLoadState;
TimeId _dialogsLoadTill = 0;
rpl::variable<bool> _dialogsLoadMayBlockByDate = false;

View file

@ -0,0 +1,212 @@
/*
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
*/
#include "data/data_history_messages.h"
#include "apiwrap.h"
#include "data/data_chat.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_sparse_ids.h"
#include "history/history.h"
#include "main/main_session.h"
namespace Data {
void HistoryMessages::addNew(MsgId messageId) {
_chat.addNew(messageId);
}
void HistoryMessages::addExisting(MsgId messageId, MsgRange noSkipRange) {
_chat.addExisting(messageId, noSkipRange);
}
void HistoryMessages::addSlice(
std::vector<MsgId> &&messageIds,
MsgRange noSkipRange,
std::optional<int> count) {
_chat.addSlice(std::move(messageIds), noSkipRange, count);
}
void HistoryMessages::removeOne(MsgId messageId) {
_chat.removeOne(messageId);
_oneRemoved.fire_copy(messageId);
}
void HistoryMessages::removeAll() {
_chat.removeAll();
_allRemoved.fire({});
}
void HistoryMessages::invalidateBottom() {
_chat.invalidateBottom();
_bottomInvalidated.fire({});
}
Storage::SparseIdsListResult HistoryMessages::snapshot(
const Storage::SparseIdsListQuery &query) const {
return _chat.snapshot(query);
}
auto HistoryMessages::sliceUpdated() const
-> rpl::producer<Storage::SparseIdsSliceUpdate> {
return _chat.sliceUpdated();
}
rpl::producer<MsgId> HistoryMessages::oneRemoved() const {
return _oneRemoved.events();
}
rpl::producer<> HistoryMessages::allRemoved() const {
return _allRemoved.events();
}
rpl::producer<> HistoryMessages::bottomInvalidated() const {
return _bottomInvalidated.events();
}
rpl::producer<SparseIdsSlice> HistoryViewer(
not_null<History*> history,
MsgId aroundId,
int limitBefore,
int limitAfter) {
Expects(IsServerMsgId(aroundId) || (aroundId == 0));
Expects((aroundId != 0) || (limitBefore == 0 && limitAfter == 0));
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto messages = &history->messages();
auto builder = lifetime.make_state<SparseIdsSliceBuilder>(
aroundId,
limitBefore,
limitAfter);
using RequestAroundInfo = SparseIdsSliceBuilder::AroundData;
builder->insufficientAround(
) | rpl::start_with_next([=](const RequestAroundInfo &info) {
history->session().api().requestHistory(
history,
info.aroundId,
info.direction);
}, lifetime);
auto pushNextSnapshot = [=] {
consumer.put_next(builder->snapshot());
};
using SliceUpdate = Storage::SparseIdsSliceUpdate;
messages->sliceUpdated(
) | rpl::filter([=](const SliceUpdate &update) {
return builder->applyUpdate(update);
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
messages->oneRemoved(
) | rpl::filter([=](MsgId messageId) {
return builder->removeOne(messageId);
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
messages->allRemoved(
) | rpl::filter([=] {
return builder->removeAll();
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
messages->bottomInvalidated(
) | rpl::filter([=] {
return builder->invalidateBottom();
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
const auto snapshot = messages->snapshot({
aroundId,
limitBefore,
limitAfter,
});
if (snapshot.count || !snapshot.messageIds.empty()) {
if (builder->applyInitial(snapshot)) {
pushNextSnapshot();
}
}
builder->checkInsufficient();
return lifetime;
};
}
rpl::producer<SparseIdsMergedSlice> HistoryMergedViewer(
not_null<History*> history,
/*Universal*/MsgId universalAroundId,
int limitBefore,
int limitAfter) {
const auto migrateFrom = history->peer->migrateFrom();
auto createSimpleViewer = [=](
PeerId peerId,
MsgId topicRootId,
SparseIdsSlice::Key simpleKey,
int limitBefore,
int limitAfter) {
const auto chosen = (history->peer->id == peerId)
? history
: history->owner().history(peerId);
return HistoryViewer(history, simpleKey, limitBefore, limitAfter);
};
const auto peerId = history->peer->id;
const auto topicRootId = MsgId();
const auto migratedPeerId = migrateFrom ? migrateFrom->id : peerId;
using Key = SparseIdsMergedSlice::Key;
return SparseIdsMergedSlice::CreateViewer(
Key(peerId, topicRootId, migratedPeerId, universalAroundId),
limitBefore,
limitAfter,
std::move(createSimpleViewer));
}
rpl::producer<MessagesSlice> HistoryMessagesViewer(
not_null<History*> history,
MessagePosition aroundId,
int limitBefore,
int limitAfter) {
const auto computeUnreadAroundId = [&] {
if (const auto migrated = history->migrateFrom()) {
if (const auto around = migrated->loadAroundId()) {
return MsgId(around - ServerMaxMsgId);
}
}
if (const auto around = history->loadAroundId()) {
return around;
}
return MsgId(ServerMaxMsgId - 1);
};
const auto messageId = (aroundId.fullId.msg == ShowAtUnreadMsgId)
? computeUnreadAroundId()
: (aroundId.fullId.msg == ShowAtTheEndMsgId)
? (ServerMaxMsgId - 1)
: (aroundId.fullId.peer == history->peer->id)
? aroundId.fullId.msg
: (aroundId.fullId.msg - ServerMaxMsgId);
return HistoryMergedViewer(
history,
messageId,
limitBefore,
limitAfter
) | rpl::map([=](SparseIdsMergedSlice &&slice) {
auto result = Data::MessagesSlice();
result.fullCount = slice.fullCount();
result.skippedAfter = slice.skippedAfter();
result.skippedBefore = slice.skippedBefore();
const auto count = slice.size();
result.ids.reserve(count);
if (const auto msgId = slice.nearest(messageId)) {
result.nearestToAround = *msgId;
}
for (auto i = 0; i != count; ++i) {
result.ids.push_back(slice[i]);
}
return result;
});
}
} // namespace Data

View file

@ -0,0 +1,67 @@
/*
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
#include "storage/storage_sparse_ids_list.h"
class History;
class SparseIdsSlice;
class SparseIdsMergedSlice;
namespace Data {
struct MessagesSlice;
struct MessagePosition;
class HistoryMessages final {
public:
void addNew(MsgId messageId);
void addExisting(MsgId messageId, MsgRange noSkipRange);
void addSlice(
std::vector<MsgId> &&messageIds,
MsgRange noSkipRange,
std::optional<int> count);
void removeOne(MsgId messageId);
void removeAll();
void invalidateBottom();
[[nodiscard]] Storage::SparseIdsListResult snapshot(
const Storage::SparseIdsListQuery &query) const;
[[nodiscard]] auto sliceUpdated() const
-> rpl::producer<Storage::SparseIdsSliceUpdate>;
[[nodiscard]] rpl::producer<MsgId> oneRemoved() const;
[[nodiscard]] rpl::producer<> allRemoved() const;
[[nodiscard]] rpl::producer<> bottomInvalidated() const;
private:
Storage::SparseIdsList _chat;
rpl::event_stream<MsgId> _oneRemoved;
rpl::event_stream<> _allRemoved;
rpl::event_stream<> _bottomInvalidated;
};
[[nodiscard]] rpl::producer<SparseIdsSlice> HistoryViewer(
not_null<History*> history,
MsgId aroundId,
int limitBefore,
int limitAfter);
[[nodiscard]] rpl::producer<SparseIdsMergedSlice> HistoryMergedViewer(
not_null<History*> history,
/*Universal*/MsgId universalAroundId,
int limitBefore,
int limitAfter);
[[nodiscard]] rpl::producer<MessagesSlice> HistoryMessagesViewer(
not_null<History*> history,
MessagePosition aroundId,
int limitBefore,
int limitAfter);
} // namespace Data

View file

@ -78,10 +78,6 @@ private:
[[nodiscard]] Histories &histories();
void subscribeToUpdates();
[[nodiscard]] rpl::producer<MessagesSlice> sourceFromServer(
MessagePosition aroundId,
int limitBefore,
int limitAfter);
void appendClientSideMessages(MessagesSlice &slice);
[[nodiscard]] bool buildFromData(not_null<Viewer*> viewer);

View file

@ -21,6 +21,7 @@ namespace {
constexpr auto kSharedMediaLimit = 100;
constexpr auto kFirstSharedMediaLimit = 0;
constexpr auto kHistoryLimit = 50;
constexpr auto kDefaultSearchTimeoutMs = crl::time(200);
} // namespace
@ -199,6 +200,60 @@ SearchResult ParseSearchResult(
return result;
}
HistoryRequest PrepareHistoryRequest(
not_null<PeerData*> peer,
MsgId messageId,
Data::LoadDirection direction) {
const auto minId = 0;
const auto maxId = 0;
const auto limit = kHistoryLimit;
const auto offsetId = [&] {
switch (direction) {
case Data::LoadDirection::Before:
case Data::LoadDirection::Around: return messageId;
case Data::LoadDirection::After: return messageId + 1;
}
Unexpected("Direction in PrepareSearchRequest");
}();
const auto addOffset = [&] {
switch (direction) {
case Data::LoadDirection::Before: return 0;
case Data::LoadDirection::Around: return -limit / 2;
case Data::LoadDirection::After: return -limit;
}
Unexpected("Direction in PrepareSearchRequest");
}();
const auto hash = uint64(0);
const auto offsetDate = int32(0);
const auto mtpOffsetId = int(std::clamp(
offsetId.bare,
int64(0),
int64(0x3FFFFFFF)));
return MTPmessages_GetHistory(
peer->input,
MTP_int(mtpOffsetId),
MTP_int(offsetDate),
MTP_int(addOffset),
MTP_int(limit),
MTP_int(maxId),
MTP_int(minId),
MTP_long(hash));
}
HistoryResult ParseHistoryResult(
not_null<PeerData*> peer,
MsgId messageId,
Data::LoadDirection direction,
const HistoryRequestResult &data) {
return ParseSearchResult(
peer,
Storage::SharedMediaType::kCount,
messageId,
direction,
data);
}
SearchController::CacheEntry::CacheEntry(
not_null<Main::Session*> session,
const Query &query)

View file

@ -32,7 +32,11 @@ struct SearchResult {
using SearchRequest = MTPmessages_Search;
using SearchRequestResult = MTPmessages_Messages;
std::optional<SearchRequest> PrepareSearchRequest(
using HistoryResult = SearchResult;
using HistoryRequest = MTPmessages_GetHistory;
using HistoryRequestResult = MTPmessages_Messages;
[[nodiscard]] std::optional<SearchRequest> PrepareSearchRequest(
not_null<PeerData*> peer,
MsgId topicRootId,
Storage::SharedMediaType type,
@ -40,13 +44,24 @@ std::optional<SearchRequest> PrepareSearchRequest(
MsgId messageId,
Data::LoadDirection direction);
SearchResult ParseSearchResult(
[[nodiscard]] SearchResult ParseSearchResult(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
MsgId messageId,
Data::LoadDirection direction,
const SearchRequestResult &data);
[[nodiscard]] HistoryRequest PrepareHistoryRequest(
not_null<PeerData*> peer,
MsgId messageId,
Data::LoadDirection direction);
[[nodiscard]] HistoryResult ParseHistoryResult(
not_null<PeerData*> peer,
MsgId messageId,
Data::LoadDirection direction,
const HistoryRequestResult &data);
class SearchController final {
public:
using IdsList = Storage::SparseIdsList;

View file

@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_document.h"
#include "data/data_histories.h"
#include "data/data_history_messages.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "api/api_chat_participants.h"
@ -483,7 +484,7 @@ not_null<HistoryItem*> History::insertItem(
std::unique_ptr<HistoryItem> item) {
Expects(item != nullptr);
const auto &[i, ok] = _messages.insert(std::move(item));
const auto &[i, ok] = _items.insert(std::move(item));
const auto result = i->get();
owner().registerMessage(result);
@ -500,6 +501,9 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
// All this must be done for all items manually in History::clear()!
item->destroyHistoryEntry();
if (item->isRegular()) {
if (const auto messages = _messages.get()) {
messages->removeOne(item->id);
}
if (const auto types = item->sharedMediaTypes()) {
session().storage().remove(Storage::SharedMediaRemoveOne(
peerId,
@ -524,11 +528,11 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
Core::App().notifications().clearFromItem(item);
auto hack = std::unique_ptr<HistoryItem>(item.get());
const auto i = _messages.find(hack);
const auto i = _items.find(hack);
hack.release();
Assert(i != end(_messages));
_messages.erase(i);
Assert(i != end(_items));
_items.erase(i);
if (documentToCancel) {
session().data().documentMessageRemoved(documentToCancel);
@ -537,8 +541,8 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) {
auto toDestroy = std::vector<not_null<HistoryItem*>>();
toDestroy.reserve(_messages.size());
for (const auto &message : _messages) {
toDestroy.reserve(_items.size());
for (const auto &message : _items) {
if (message->isRegular()
&& message->date() > minDate
&& message->date() < maxDate) {
@ -552,8 +556,8 @@ void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) {
void History::destroyMessagesByTopic(MsgId topicRootId) {
auto toDestroy = std::vector<not_null<HistoryItem*>>();
toDestroy.reserve(_messages.size());
for (const auto &message : _messages) {
toDestroy.reserve(_items.size());
for (const auto &message : _items) {
if (message->topicRootId() == topicRootId) {
toDestroy.push_back(message.get());
}
@ -575,7 +579,7 @@ void History::unpinMessagesFor(MsgId topicRootId) {
topic->setHasPinnedMessages(false);
});
}
for (const auto &item : _messages) {
for (const auto &item : _items) {
if (item->isPinned()) {
item->setIsPinned(false);
}
@ -589,7 +593,7 @@ void History::unpinMessagesFor(MsgId topicRootId) {
if (const auto topic = peer->forumTopicFor(topicRootId)) {
topic->setHasPinnedMessages(false);
}
for (const auto &item : _messages) {
for (const auto &item : _items) {
if (item->isPinned() && item->topicRootId() == topicRootId) {
item->setIsPinned(false);
}
@ -781,9 +785,12 @@ not_null<HistoryItem*> History::addNewToBack(
addItemToBlock(item);
if (!unread && item->isRegular()) {
const auto from = loadedAtTop() ? 0 : minMsgId();
const auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
if (_messages) {
_messages->addExisting(item->id, { from, till });
}
if (const auto types = item->sharedMediaTypes()) {
auto from = loadedAtTop() ? 0 : minMsgId();
auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
auto &storage = session().storage();
storage.add(Storage::SharedMediaAddExisting(
peer->id,
@ -1190,6 +1197,7 @@ void History::mainViewRemoved(
void History::newItemAdded(not_null<HistoryItem*> item) {
item->indexAsNewItem();
item->addToMessagesIndex();
if (const auto from = item->from() ? item->from()->asUser() : nullptr) {
if (from == item->author()) {
_sendActionPainter.clear(from);
@ -2385,6 +2393,9 @@ void History::setNotLoadedAtBottom() {
session().storage().invalidate(
Storage::SharedMediaInvalidateBottom(peer->id));
if (const auto messages = _messages.get()) {
messages->invalidateBottom();
}
}
void History::clearSharedMedia() {
@ -3092,6 +3103,46 @@ MsgRange History::rangeForDifferenceRequest() const {
return MsgRange();
}
Data::HistoryMessages &History::messages() {
if (!_messages) {
_messages = std::make_unique<Data::HistoryMessages>();
const auto from = loadedAtTop() ? 0 : minMsgId();
const auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
auto list = std::vector<MsgId>();
list.reserve(std::min(
int(_items.size()),
int(blocks.size()) * kNewBlockEachMessage));
auto sort = false;
for (const auto &block : blocks) {
for (const auto &view : block->messages) {
const auto item = view->data();
if (item->isRegular()) {
const auto id = item->id;
if (!list.empty() && list.back() >= id) {
sort = true;
}
}
}
}
if (sort) {
ranges::sort(list);
}
if (till) {
_messages->addSlice(std::move(list), { from, till }, {});
}
}
return *_messages;
}
const Data::HistoryMessages &History::messages() const {
return const_cast<History*>(this)->messages();
}
Data::HistoryMessages *History::maybeMessages() {
return _messages.get();
}
HistoryItem *History::insertJoinedMessage() {
const auto channel = peer->asChannel();
if (!channel
@ -3194,11 +3245,11 @@ void History::removeJoinedMessage() {
void History::reactionsEnabledChanged(bool enabled) {
if (!enabled) {
for (const auto &item : _messages) {
for (const auto &item : _items) {
item->updateReactions(nullptr);
}
} else {
for (const auto &item : _messages) {
for (const auto &item : _items) {
item->updateReactionsUnknown();
}
}
@ -3372,6 +3423,9 @@ void History::clear(ClearType type) {
}
_loadedAtTop = _loadedAtBottom = _lastMessage.has_value();
clearSharedMedia();
if (const auto messages = _messages.get()) {
messages->removeAll();
}
clearLastKeyboard();
}
@ -3388,8 +3442,8 @@ void History::clear(ClearType type) {
void History::clearUpTill(MsgId availableMinId) {
auto remove = std::vector<not_null<HistoryItem*>>();
remove.reserve(_messages.size());
for (const auto &item : _messages) {
remove.reserve(_items.size());
for (const auto &item : _items) {
const auto itemId = item->id;
if (!item->isRegular()) {
continue;

View file

@ -26,12 +26,14 @@ class HistoryMainElementDelegateMixin;
struct LanguageId;
namespace Data {
struct Draft;
class Session;
class Folder;
class ChatFilter;
struct SponsoredFrom;
class SponsoredMessages;
class HistoryMessages;
enum class ForwardOptions {
PreserveInfo,
@ -79,7 +81,7 @@ public:
History(not_null<Data::Session*> owner, PeerId peerId);
~History();
not_null<History*> owningHistory() override {
[[nodiscard]] not_null<History*> owningHistory() override {
return this;
}
[[nodiscard]] Data::Thread *threadFor(MsgId topicRootId);
@ -93,23 +95,27 @@ public:
void forumChanged(Data::Forum *old);
[[nodiscard]] bool isForum() const;
not_null<History*> migrateToOrMe() const;
History *migrateFrom() const;
MsgRange rangeForDifferenceRequest() const;
[[nodiscard]] not_null<History*> migrateToOrMe() const;
[[nodiscard]] History *migrateFrom() const;
[[nodiscard]] MsgRange rangeForDifferenceRequest() const;
HistoryItem *joinedMessageInstance() const;
[[nodiscard]] Data::HistoryMessages &messages();
[[nodiscard]] const Data::HistoryMessages &messages() const;
[[nodiscard]] Data::HistoryMessages *maybeMessages();
[[nodiscard]] HistoryItem *joinedMessageInstance() const;
void checkLocalMessages();
void removeJoinedMessage();
void reactionsEnabledChanged(bool enabled);
bool isEmpty() const;
bool isDisplayedEmpty() const;
Element *findFirstNonEmpty() const;
Element *findFirstDisplayed() const;
Element *findLastNonEmpty() const;
Element *findLastDisplayed() const;
bool hasOrphanMediaGroupPart() const;
[[nodiscard]] bool isEmpty() const;
[[nodiscard]] bool isDisplayedEmpty() const;
[[nodiscard]] Element *findFirstNonEmpty() const;
[[nodiscard]] Element *findFirstDisplayed() const;
[[nodiscard]] Element *findLastNonEmpty() const;
[[nodiscard]] Element *findLastDisplayed() const;
[[nodiscard]] bool hasOrphanMediaGroupPart() const;
[[nodiscard]] std::vector<MsgId> collectMessagesFromParticipantToDelete(
not_null<PeerData*> participant) const;
@ -590,7 +596,9 @@ private:
std::optional<HistoryItem*> _lastMessage;
std::optional<HistoryItem*> _lastServerMessage;
base::flat_set<not_null<HistoryItem*>> _clientSideMessages;
std::unordered_set<std::unique_ptr<HistoryItem>> _messages;
std::unordered_set<std::unique_ptr<HistoryItem>> _items;
std::unique_ptr<Data::HistoryMessages> _messages;
// This almost always is equal to _lastMessage. The only difference is
// for a group that migrated to a supergroup. Then _lastMessage can

View file

@ -55,6 +55,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_game.h"
#include "data/data_history_messages.h"
#include "data/data_user.h"
#include "data/data_group_call.h" // Data::GroupCall::id().
#include "data/data_poll.h" // PollData::publicVotes.
@ -1791,6 +1792,7 @@ void HistoryItem::applySentMessage(const MTPDmessage &data) {
setIsPinned(data.is_pinned());
contributeToSlowmode(data.vdate().v);
addToSharedMediaIndex();
addToMessagesIndex();
invalidateChatListEntry();
if (const auto period = data.vttl_period(); period && period->v > 0) {
applyTTL(data.vdate().v + period->v);
@ -1815,6 +1817,7 @@ void HistoryItem::applySentMessage(
contributeToSlowmode(data.vdate().v);
if (!wasAlready) {
addToSharedMediaIndex();
addToMessagesIndex();
}
invalidateChatListEntry();
if (const auto period = data.vttl_period(); period && period->v > 0) {
@ -2011,6 +2014,14 @@ void HistoryItem::removeFromSharedMediaIndex() {
}
}
void HistoryItem::addToMessagesIndex() {
if (isRegular()) {
if (const auto messages = _history->maybeMessages()) {
messages->addNew(id);
}
}
}
void HistoryItem::incrementReplyToTopCounter() {
if (isRegular() && _history->peer->isMegagroup()) {
_history->session().changes().messageUpdated(

View file

@ -359,6 +359,7 @@ public:
void indexAsNewItem();
void addToSharedMediaIndex();
void addToMessagesIndex();
void removeFromSharedMediaIndex();
struct NotificationTextOptions {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_chat_preview.h"
#include "data/data_forum_topic.h"
#include "data/data_history_messages.h"
#include "data/data_peer.h"
#include "data/data_replies_list.h"
#include "data/data_thread.h"
@ -15,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_list_widget.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h"
@ -136,6 +138,7 @@ private:
const not_null<QAction*> _dummyAction;
const not_null<Main::Session*> _session;
const not_null<Data::Thread*> _thread;
const std::shared_ptr<Data::RepliesList> _replies;
const not_null<History*> _history;
const not_null<PeerData*> _peer;
const std::shared_ptr<Ui::ChatTheme> _theme;
@ -153,6 +156,7 @@ Item::Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread)
, _dummyAction(new QAction(parent))
, _session(&thread->session())
, _thread(thread)
, _replies(thread->asTopic() ? thread->asTopic()->replies() : nullptr)
, _history(thread->owningHistory())
, _peer(thread->peer())
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
@ -253,52 +257,13 @@ rpl::producer<Data::MessagesSlice> Item::listSource(
Data::MessagePosition aroundId,
int limitBefore,
int limitAfter) {
if (const auto topic = _thread->asTopic()) {
return topic->replies()->source(
return _replies
? _replies->source(aroundId, limitBefore, limitAfter)
: Data::HistoryMessagesViewer(
_thread->asHistory(),
aroundId,
limitBefore,
limitAfter
) | rpl::before_next([=] { // after_next makes a copy of value.
//if (!_loaded) {
// _loaded = true;
// crl::on_main(this, [=] {
// updatePinnedVisibility();
// });
//}
});
}
// #TODO
//const auto messageId = aroundId.fullId.msg
// ? aroundId.fullId.msg
// : (ServerMaxMsgId - 1);
//return SharedMediaMergedViewer(
// &_thread->session(),
// SharedMediaMergedKey(
// SparseIdsMergedSlice::Key(
// _history->peer->id,
// _thread->topicRootId(),
// _migratedPeer ? _migratedPeer->id : 0,
// messageId),
// Storage::SharedMediaType::Pinned),
// limitBefore,
// limitAfter
//) | rpl::map([=](SparseIdsMergedSlice &&slice) {
// auto result = Data::MessagesSlice();
// result.fullCount = slice.fullCount();
// result.skippedAfter = slice.skippedAfter();
// result.skippedBefore = slice.skippedBefore();
// const auto count = slice.size();
// result.ids.reserve(count);
// if (const auto msgId = slice.nearest(messageId)) {
// result.nearestToAround = *msgId;
// }
// for (auto i = 0; i != count; ++i) {
// result.ids.push_back(slice[i]);
// }
// return result;
//});
return rpl::single(Data::MessagesSlice());
limitAfter);
}
bool Item::listAllowsMultiSelect() {
@ -341,7 +306,44 @@ void Item::listMarkContentsRead(
MessagesBarData Item::listMessagesBar(
const std::vector<not_null<Element*>> &elements) {
return {};// #TODO
if (elements.empty()) {
return {};
} else if (!_replies && !_history->unreadCount()) {
return {};
}
const auto repliesTill = _replies
? _replies->computeInboxReadTillFull()
: MsgId();
const auto migrated = _replies ? nullptr : _history->migrateFrom();
const auto migratedTill = migrated ? migrated->inboxReadTillId() : 0;
const auto historyTill = _replies ? 0 : _history->inboxReadTillId();
if (!_replies && !migratedTill && !historyTill) {
return {};
}
const auto hidden = _replies && (repliesTill < 2);
for (auto i = 0, count = int(elements.size()); i != count; ++i) {
const auto item = elements[i]->data();
if (!item->isRegular()
|| item->out()
|| (_replies && !item->replyToId())) {
continue;
}
const auto inHistory = (item->history() == _history);
if ((_replies && item->id > repliesTill)
|| (migratedTill && (inHistory || item->id > migratedTill))
|| (historyTill && inHistory && item->id > historyTill)) {
return {
.bar = {
.element = elements[i],
.hidden = hidden,
.focus = true,
},
.text = tr::lng_unread_bar_some(),
};
}
}
return {};
}
void Item::listContentRefreshed() {

View file

@ -939,7 +939,9 @@ void ListWidget::restoreScrollState() {
}
_scrollInited = true;
_scrollTopState.item = _bar.element->data()->position();
_scrollTopState.shift = st::lineWidth + st::historyUnreadBarMargin;
_scrollTopState.shift = st::lineWidth
+ st::historyUnreadBarMargin
+ _bar.element->displayedDateHeight();
}
const auto index = findNearestItem(_scrollTopState.item);
if (index >= 0) {

View file

@ -187,20 +187,9 @@ void SparseIdsList::invalidateBottom() {
rpl::producer<SparseIdsListResult> SparseIdsList::query(
SparseIdsListQuery &&query) const {
return [this, query = std::move(query)](auto consumer) {
auto slice = query.aroundId
? ranges::lower_bound(
_slices,
query.aroundId,
std::less<>(),
[](const Slice &slice) { return slice.range.till; })
: _slices.end();
if (slice != _slices.end()
&& slice->range.from <= query.aroundId) {
consumer.put_next(queryFromSlice(query, *slice));
} else if (_count) {
auto result = SparseIdsListResult {};
result.count = _count;
consumer.put_next(std::move(result));
auto now = snapshot(query);
if (!now.messageIds.empty() || now.count) {
consumer.put_next(std::move(now));
}
consumer.put_done();
return rpl::lifetime();

View file

@ -1086,4 +1086,7 @@ previewMenu: PopupMenu(defaultPopupMenu) {
maxHeight: 420px;
radius: boxRadius;
shadow: boxRoundShadow;
animation: PanelAnimation(defaultPanelAnimation) {
shadow: boxRoundShadow;
}
}