mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Implement chats preview, show from unread.
This commit is contained in:
parent
68df8448a2
commit
f223ae7eee
16 changed files with 570 additions and 96 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
212
Telegram/SourceFiles/data/data_history_messages.cpp
Normal file
212
Telegram/SourceFiles/data/data_history_messages.cpp
Normal 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
|
67
Telegram/SourceFiles/data/data_history_messages.h
Normal file
67
Telegram/SourceFiles/data/data_history_messages.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -359,6 +359,7 @@ public:
|
|||
|
||||
void indexAsNewItem();
|
||||
void addToSharedMediaIndex();
|
||||
void addToMessagesIndex();
|
||||
void removeFromSharedMediaIndex();
|
||||
|
||||
struct NotificationTextOptions {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1086,4 +1086,7 @@ previewMenu: PopupMenu(defaultPopupMenu) {
|
|||
maxHeight: 420px;
|
||||
radius: boxRadius;
|
||||
shadow: boxRoundShadow;
|
||||
animation: PanelAnimation(defaultPanelAnimation) {
|
||||
shadow: boxRoundShadow;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue