mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-04-07 01:53:14 +00:00
First version of feed section view.
This commit is contained in:
parent
50b120bc22
commit
794e31505b
Telegram
Resources/langs
SourceFiles
apiwrap.cppapiwrap.happ.cpp
boxes/peers
data
data_feed.cppdata_feed.hdata_feed_messages.cppdata_feed_messages.hdata_messages.cppdata_messages.hdata_search_controller.cppdata_search_controller.hdata_sparse_ids.cppdata_sparse_ids.hdata_types.h
dialogs
history
admin_log
history_admin_log_filter.cpphistory_admin_log_filter.hhistory_admin_log_inner.cpphistory_admin_log_inner.hhistory_admin_log_item.cpphistory_admin_log_item.hhistory_admin_log_section.cpphistory_admin_log_section.h
feed
history_inner_widget.cpphistory_inner_widget.hhistory_item.cpphistory_item.hhistory_item_components.cpphistory_message.cpphistory_service.cpphistory_service.hhistory_widget.cpphistory_widget.hview
info/media
mainwidget.cppmedia/view
profile
storage
storage_facade.cppstorage_facade.hstorage_feed_messages.cppstorage_feed_messages.hstorage_shared_media.cpp
window
gyp
@ -1415,6 +1415,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_admin_pin_messages" = "Pin messages";
|
||||
"lng_admin_log_admin_add_admins" = "Add new admins";
|
||||
|
||||
"lng_feed_show_next" = "Show Next";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "storage/storage_user_photos.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "storage/storage_feed_messages.h"
|
||||
#include "data/data_sparse_ids.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "data/data_channel_admins.h"
|
||||
@ -49,6 +50,7 @@ constexpr auto kUnreadMentionsPreloadIfLess = 5;
|
||||
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
|
||||
constexpr auto kUnreadMentionsNextRequestLimit = 100;
|
||||
constexpr auto kSharedMediaLimit = 100;
|
||||
constexpr auto kFeedMessagesLimit = 50;
|
||||
constexpr auto kReadFeaturedSetsTimeout = TimeMs(1000);
|
||||
constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
|
||||
|
||||
@ -2517,6 +2519,125 @@ void ApiWrap::userPhotosDone(
|
||||
));
|
||||
}
|
||||
|
||||
void ApiWrap::requestFeedMessages(
|
||||
not_null<Data::Feed*> feed,
|
||||
Data::MessagePosition messageId,
|
||||
SliceType slice) {
|
||||
const auto key = std::make_tuple(feed, messageId, slice);
|
||||
if (_feedMessagesRequests.contains(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto addOffset = 0;
|
||||
const auto limit = kFeedMessagesLimit;
|
||||
const auto sourcesHash = int32(0);
|
||||
const auto hash = int32(0);
|
||||
const auto flags = (messageId && messageId.fullId.channel)
|
||||
? MTPchannels_GetFeed::Flag::f_offset_position
|
||||
: MTPchannels_GetFeed::Flag::f_offset_to_max_read;
|
||||
const auto requestId = request(MTPchannels_GetFeed(
|
||||
MTP_flags(flags),
|
||||
MTP_int(feed->id()),
|
||||
MTP_feedPosition(
|
||||
MTP_int(messageId.date),
|
||||
MTP_peerChannel(MTP_int(messageId.fullId.channel)),
|
||||
MTP_int(messageId.fullId.msg)),
|
||||
MTP_int(addOffset),
|
||||
MTP_int(limit),
|
||||
MTPFeedPosition(),
|
||||
MTPFeedPosition(),
|
||||
MTP_int(sourcesHash),
|
||||
MTP_int(hash)
|
||||
)).done([=](const MTPmessages_FeedMessages &result) {
|
||||
const auto key = std::make_tuple(feed, messageId, slice);
|
||||
_feedMessagesRequests.remove(key);
|
||||
feedMessagesDone(feed, messageId, slice, result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_feedMessagesRequests.remove(key);
|
||||
}).send();
|
||||
_feedMessagesRequests.emplace(key, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::feedMessagesDone(
|
||||
not_null<Data::Feed*> feed,
|
||||
Data::MessagePosition messageId,
|
||||
SliceType slice,
|
||||
const MTPmessages_FeedMessages &result) {
|
||||
if (result.type() == mtpc_messages_feedMessagesNotModified) {
|
||||
LOG(("API Error: Unexpected messages.feedMessagesNotModified."));
|
||||
_session->storage().add(Storage::FeedMessagesAddSlice(
|
||||
feed->id(),
|
||||
std::vector<Data::MessagePosition>(),
|
||||
Data::FullMessagesRange));
|
||||
return;
|
||||
}
|
||||
Assert(result.type() == mtpc_messages_feedMessages);
|
||||
const auto &data = result.c_messages_feedMessages();
|
||||
const auto &messages = data.vmessages.v;
|
||||
const auto type = NewMessageExisting;
|
||||
|
||||
auto ids = std::vector<Data::MessagePosition>();
|
||||
auto noSkipRange = Data::MessagesRange(messageId, messageId);
|
||||
auto accumulateFrom = [](auto &from, const auto &candidate) {
|
||||
if (!from || from > candidate) {
|
||||
from = candidate;
|
||||
}
|
||||
};
|
||||
auto accumulateTill = [](auto &till, const auto &candidate) {
|
||||
if (!till || till < candidate) {
|
||||
till = candidate;
|
||||
}
|
||||
};
|
||||
App::feedUsers(data.vusers);
|
||||
App::feedChats(data.vchats);
|
||||
if (!messages.empty()) {
|
||||
ids.reserve(messages.size());
|
||||
for (const auto &msg : messages) {
|
||||
if (const auto item = App::histories().addNewMessage(msg, type)) {
|
||||
const auto position = item->position();
|
||||
ids.push_back(position);
|
||||
accumulateFrom(noSkipRange.from, position);
|
||||
accumulateTill(noSkipRange.till, position);
|
||||
}
|
||||
}
|
||||
ranges::reverse(ids);
|
||||
}
|
||||
if (data.has_min_position()) {
|
||||
accumulateFrom(
|
||||
noSkipRange.from,
|
||||
Data::FeedPositionFromMTP(data.vmin_position));
|
||||
} else {
|
||||
noSkipRange.from = Data::MinMessagePosition;
|
||||
}
|
||||
if (data.has_max_position()) {
|
||||
accumulateTill(
|
||||
noSkipRange.till,
|
||||
Data::FeedPositionFromMTP(data.vmax_position));
|
||||
} else {
|
||||
noSkipRange.till = Data::MaxMessagePosition;
|
||||
}
|
||||
|
||||
const auto unreadPosition = [&] {
|
||||
if (data.has_read_max_position()) {
|
||||
return Data::FeedPositionFromMTP(data.vread_max_position);
|
||||
} else if (!messageId) {
|
||||
return ids.empty()
|
||||
? noSkipRange.till
|
||||
: ids.back();
|
||||
}
|
||||
return Data::MessagePosition();
|
||||
}();
|
||||
|
||||
_session->storage().add(Storage::FeedMessagesAddSlice(
|
||||
feed->id(),
|
||||
std::move(ids),
|
||||
noSkipRange));
|
||||
|
||||
if (unreadPosition) {
|
||||
feed->setUnreadPosition(unreadPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::sendAction(const SendOptions &options) {
|
||||
readServerHistory(options.history);
|
||||
options.history->getReadyFor(ShowAtTheEndMsgId);
|
||||
|
@ -9,15 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <rpl/event_stream.h>
|
||||
#include "base/timer.h"
|
||||
#include "core/single_timer.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "core/single_timer.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
#include "data/data_messages.h"
|
||||
|
||||
class TaskQueue;
|
||||
class AuthSession;
|
||||
enum class SparseIdsLoadDirection;
|
||||
struct MessageGroupId;
|
||||
struct SendingAlbum;
|
||||
enum class SendMediaType;
|
||||
@ -129,7 +129,7 @@ public:
|
||||
bool adminsEnabled,
|
||||
base::flat_set<not_null<UserData*>> &&admins);
|
||||
|
||||
using SliceType = SparseIdsLoadDirection;
|
||||
using SliceType = Data::LoadDirection;
|
||||
void requestSharedMedia(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type,
|
||||
@ -143,6 +143,11 @@ public:
|
||||
not_null<UserData*> user,
|
||||
PhotoId afterId);
|
||||
|
||||
void requestFeedMessages(
|
||||
not_null<Data::Feed*> feed,
|
||||
Data::MessagePosition messageId,
|
||||
SliceType slice);
|
||||
|
||||
void stickerSetInstalled(uint64 setId) {
|
||||
_stickerSetInstalled.fire_copy(setId);
|
||||
}
|
||||
@ -300,6 +305,12 @@ private:
|
||||
PhotoId photoId,
|
||||
const MTPphotos_Photos &result);
|
||||
|
||||
void feedMessagesDone(
|
||||
not_null<Data::Feed*> feed,
|
||||
Data::MessagePosition messageId,
|
||||
SliceType slice,
|
||||
const MTPmessages_FeedMessages &result);
|
||||
|
||||
void sendSharedContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
@ -417,6 +428,11 @@ private:
|
||||
|
||||
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
|
||||
|
||||
base::flat_map<std::tuple<
|
||||
not_null<Data::Feed*>,
|
||||
Data::MessagePosition,
|
||||
SliceType>, mtpRequestId> _feedMessagesRequests;
|
||||
|
||||
rpl::event_stream<SendOptions> _sendActions;
|
||||
|
||||
struct ReadRequest {
|
||||
|
@ -19,10 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "data/data_abstract_structure.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_location_manager.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "media/media_audio.h"
|
||||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "messenger.h"
|
||||
@ -2141,7 +2141,7 @@ namespace {
|
||||
if (App::main()) {
|
||||
App::main()->updateScrollColors();
|
||||
}
|
||||
HistoryLayout::serviceColorsUpdated();
|
||||
HistoryView::serviceColorsUpdated();
|
||||
} else if (update.type == Update::Type::New) {
|
||||
prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg);
|
||||
prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected);
|
||||
@ -2149,7 +2149,7 @@ namespace {
|
||||
if (App::main()) {
|
||||
App::main()->updateScrollColors();
|
||||
}
|
||||
HistoryLayout::serviceColorsUpdated();
|
||||
HistoryView::serviceColorsUpdated();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_peer_info_box.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "profile/profile_channel_controllers.h"
|
||||
#include "info/profile/info_profile_button.h"
|
||||
|
@ -11,16 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Data {
|
||||
|
||||
FeedPosition::FeedPosition(const MTPFeedPosition &position)
|
||||
: date(position.c_feedPosition().vdate.v)
|
||||
, msgId(
|
||||
peerToChannel(peerFromMTP(position.c_feedPosition().vpeer)),
|
||||
position.c_feedPosition().vid.v) {
|
||||
}
|
||||
MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position) {
|
||||
Expects(position.type() == mtpc_feedPosition);
|
||||
|
||||
FeedPosition::FeedPosition(not_null<HistoryItem*> item)
|
||||
: date(toServerTime(item->date.toTime_t()).v)
|
||||
, msgId(item->fullId()) {
|
||||
const auto &data = position.c_feedPosition();
|
||||
return MessagePosition(data.vdate.v, FullMsgId(
|
||||
peerToChannel(peerFromMTP(data.vpeer)),
|
||||
data.vid.v));
|
||||
}
|
||||
|
||||
Feed::Feed(FeedId id)
|
||||
@ -82,7 +79,7 @@ void Feed::paintUserpic(
|
||||
}
|
||||
|
||||
bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
|
||||
if (_lastMessage && FeedPosition(item) <= FeedPosition(_lastMessage)) {
|
||||
if (_lastMessage && item->position() <= _lastMessage->position()) {
|
||||
return false;
|
||||
}
|
||||
_lastMessage = item;
|
||||
@ -118,8 +115,4 @@ void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
|
||||
_unreadMutedCount = unreadMutedCount;
|
||||
}
|
||||
|
||||
void Feed::setUnreadPosition(const FeedPosition &position) {
|
||||
_unreadPosition = position;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
@ -8,51 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
#include "data/data_messages.h"
|
||||
|
||||
class ChannelData;
|
||||
|
||||
namespace Data {
|
||||
|
||||
struct FeedPosition {
|
||||
FeedPosition() = default;
|
||||
explicit FeedPosition(const MTPFeedPosition &position);
|
||||
explicit FeedPosition(not_null<HistoryItem*> item);
|
||||
FeedPosition(TimeId date, FullMsgId msgId) : date(date), msgId(msgId) {
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return (msgId.msg != 0);
|
||||
}
|
||||
|
||||
inline bool operator<(const FeedPosition &other) const {
|
||||
if (date < other.date) {
|
||||
return true;
|
||||
} else if (other.date < date) {
|
||||
return false;
|
||||
}
|
||||
return (msgId < other.msgId);
|
||||
}
|
||||
inline bool operator>(const FeedPosition &other) const {
|
||||
return other < *this;
|
||||
}
|
||||
inline bool operator<=(const FeedPosition &other) const {
|
||||
return !(other < *this);
|
||||
}
|
||||
inline bool operator>=(const FeedPosition &other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
inline bool operator==(const FeedPosition &other) const {
|
||||
return (date == other.date)
|
||||
&& (msgId == other.msgId);
|
||||
}
|
||||
inline bool operator!=(const FeedPosition &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
TimeId date = 0;
|
||||
FullMsgId msgId;
|
||||
|
||||
};
|
||||
MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position);
|
||||
|
||||
class Feed : public Dialogs::Entry {
|
||||
public:
|
||||
@ -69,7 +31,15 @@ public:
|
||||
void historyCleared(not_null<History*> history);
|
||||
|
||||
void setUnreadCounts(int unreadCount, int unreadMutedCount);
|
||||
void setUnreadPosition(const FeedPosition &position);
|
||||
void setUnreadPosition(const MessagePosition &position) {
|
||||
_unreadPosition = position;
|
||||
}
|
||||
MessagePosition unreadPosition() const {
|
||||
return _unreadPosition.current();
|
||||
}
|
||||
rpl::producer<MessagePosition> unreadPositionChanges() const {
|
||||
return _unreadPosition.changes();
|
||||
}
|
||||
|
||||
bool toImportant() const override {
|
||||
return false; // TODO feeds workmode
|
||||
@ -99,7 +69,7 @@ private:
|
||||
|
||||
HistoryItem *_lastMessage = nullptr;
|
||||
|
||||
FeedPosition _unreadPosition;
|
||||
rpl::variable<MessagePosition> _unreadPosition;
|
||||
int _unreadCount = 0;
|
||||
int _unreadMutedCount = 0;
|
||||
bool _complete = false;
|
||||
|
87
Telegram/SourceFiles/data/data_feed_messages.cpp
Normal file
87
Telegram/SourceFiles/data/data_feed_messages.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
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_feed_messages.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "storage/storage_feed_messages.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
rpl::producer<MessagesSlice> FeedMessagesViewer(
|
||||
Storage::FeedMessagesKey key,
|
||||
int limitBefore,
|
||||
int limitAfter) {
|
||||
Expects(IsServerMsgId(key.position.fullId.msg)
|
||||
|| (key.position.fullId.msg == 0));
|
||||
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
const auto builder = lifetime.make_state<MessagesSliceBuilder>(
|
||||
key.position,
|
||||
limitBefore,
|
||||
limitAfter);
|
||||
const auto feed = Auth().data().feed(key.feedId);
|
||||
using AroundData = MessagesSliceBuilder::AroundData;
|
||||
const auto requestMediaAround = [=](const AroundData &data) {
|
||||
if (data.aroundId || !key.position) {
|
||||
Auth().api().requestFeedMessages(
|
||||
feed,
|
||||
data.aroundId,
|
||||
data.direction);
|
||||
}
|
||||
};
|
||||
builder->insufficientAround(
|
||||
) | rpl::start_with_next(requestMediaAround, lifetime);
|
||||
|
||||
const auto pushNextSnapshot = [=] {
|
||||
consumer.put_next(builder->snapshot());
|
||||
};
|
||||
|
||||
using SliceUpdate = Storage::FeedMessagesSliceUpdate;
|
||||
Auth().storage().feedMessagesSliceUpdated(
|
||||
) | rpl::filter([=](const SliceUpdate &update) {
|
||||
return (update.feedId == key.feedId);
|
||||
}) | rpl::filter([=](const SliceUpdate &update) {
|
||||
return builder->applyUpdate(update.data);
|
||||
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
|
||||
|
||||
using OneRemoved = Storage::FeedMessagesRemoveOne;
|
||||
Auth().storage().feedMessagesOneRemoved(
|
||||
) | rpl::filter([=](const OneRemoved &update) {
|
||||
return (update.feedId == key.feedId);
|
||||
}) | rpl::filter([=](const OneRemoved &update) {
|
||||
return builder->removeOne(update.messageId);
|
||||
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
|
||||
|
||||
using AllRemoved = Storage::FeedMessagesRemoveAll;
|
||||
Auth().storage().feedMessagesAllRemoved(
|
||||
) | rpl::filter([=](const AllRemoved &update) {
|
||||
return (update.feedId == key.feedId);
|
||||
}) | rpl::filter([=] {
|
||||
return builder->removeAll();
|
||||
}) | rpl::start_with_next(pushNextSnapshot, lifetime);
|
||||
|
||||
using Result = Storage::FeedMessagesResult;
|
||||
Auth().storage().query(Storage::FeedMessagesQuery(
|
||||
key,
|
||||
limitBefore,
|
||||
limitAfter
|
||||
)) | rpl::filter([=](const Result &result) {
|
||||
return builder->applyInitial(result);
|
||||
}) | rpl::start_with_next_done(
|
||||
pushNextSnapshot,
|
||||
[=] { builder->checkInsufficient(); },
|
||||
lifetime);
|
||||
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Data
|
24
Telegram/SourceFiles/data/data_feed_messages.h
Normal file
24
Telegram/SourceFiles/data/data_feed_messages.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
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 "data/data_feed.h"
|
||||
#include "data/data_messages.h"
|
||||
|
||||
namespace Storage {
|
||||
struct FeedMessagesKey;
|
||||
} // namespace Storage
|
||||
|
||||
namespace Data {
|
||||
|
||||
rpl::producer<MessagesSlice> FeedMessagesViewer(
|
||||
Storage::FeedMessagesKey key,
|
||||
int limitBefore,
|
||||
int limitAfter);
|
||||
|
||||
} // namespace Data
|
444
Telegram/SourceFiles/data/data_messages.cpp
Normal file
444
Telegram/SourceFiles/data/data_messages.cpp
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
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_feed_messages.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
MessagesList::Slice::Slice(
|
||||
base::flat_set<MessagePosition> &&messages,
|
||||
MessagesRange range)
|
||||
: messages(std::move(messages))
|
||||
, range(range) {
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
void MessagesList::Slice::merge(
|
||||
const Range &moreMessages,
|
||||
MessagesRange moreNoSkipRange) {
|
||||
Expects(moreNoSkipRange.from <= range.till);
|
||||
Expects(range.from <= moreNoSkipRange.till);
|
||||
|
||||
messages.merge(std::begin(moreMessages), std::end(moreMessages));
|
||||
range = {
|
||||
qMin(range.from, moreNoSkipRange.from),
|
||||
qMax(range.till, moreNoSkipRange.till)
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
int MessagesList::uniteAndAdd(
|
||||
MessagesSliceUpdate &update,
|
||||
base::flat_set<Slice>::iterator uniteFrom,
|
||||
base::flat_set<Slice>::iterator uniteTill,
|
||||
const Range &messages,
|
||||
MessagesRange noSkipRange) {
|
||||
auto uniteFromIndex = uniteFrom - _slices.begin();
|
||||
auto was = uniteFrom->messages.size();
|
||||
_slices.modify(uniteFrom, [&](Slice &slice) {
|
||||
slice.merge(messages, noSkipRange);
|
||||
});
|
||||
auto firstToErase = uniteFrom + 1;
|
||||
if (firstToErase != uniteTill) {
|
||||
for (auto it = firstToErase; it != uniteTill; ++it) {
|
||||
_slices.modify(uniteFrom, [&](Slice &slice) {
|
||||
slice.merge(it->messages, it->range);
|
||||
});
|
||||
}
|
||||
_slices.erase(firstToErase, uniteTill);
|
||||
uniteFrom = _slices.begin() + uniteFromIndex;
|
||||
}
|
||||
update.messages = &uniteFrom->messages;
|
||||
update.range = uniteFrom->range;
|
||||
return uniteFrom->messages.size() - was;
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
int MessagesList::addRangeItemsAndCountNew(
|
||||
MessagesSliceUpdate &update,
|
||||
const Range &messages,
|
||||
MessagesRange noSkipRange) {
|
||||
Expects(noSkipRange.from <= noSkipRange.till);
|
||||
|
||||
auto uniteFrom = ranges::lower_bound(
|
||||
_slices,
|
||||
noSkipRange.from,
|
||||
std::less<>(),
|
||||
[](const Slice &slice) { return slice.range.till; });
|
||||
auto uniteTill = ranges::upper_bound(
|
||||
_slices,
|
||||
noSkipRange.till,
|
||||
std::less<>(),
|
||||
[](const Slice &slice) { return slice.range.from; });
|
||||
if (uniteFrom < uniteTill) {
|
||||
return uniteAndAdd(update, uniteFrom, uniteTill, messages, noSkipRange);
|
||||
}
|
||||
|
||||
auto sliceMessages = base::flat_set<MessagePosition> {
|
||||
std::begin(messages),
|
||||
std::end(messages) };
|
||||
auto slice = _slices.emplace(
|
||||
std::move(sliceMessages),
|
||||
noSkipRange);
|
||||
update.messages = &slice->messages;
|
||||
update.range = slice->range;
|
||||
return slice->messages.size();
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
void MessagesList::addRange(
|
||||
const Range &messages,
|
||||
MessagesRange noSkipRange,
|
||||
base::optional<int> count,
|
||||
bool incrementCount) {
|
||||
Expects(!count || !incrementCount);
|
||||
|
||||
auto wasCount = _count;
|
||||
auto update = MessagesSliceUpdate();
|
||||
auto result = addRangeItemsAndCountNew(
|
||||
update,
|
||||
messages,
|
||||
noSkipRange);
|
||||
if (count) {
|
||||
_count = count;
|
||||
} else if (incrementCount && _count && result > 0) {
|
||||
*_count += result;
|
||||
}
|
||||
if (_slices.size() == 1) {
|
||||
if (_slices.front().range == FullMessagesRange) {
|
||||
_count = _slices.front().messages.size();
|
||||
}
|
||||
}
|
||||
update.count = _count;
|
||||
_sliceUpdated.fire(std::move(update));
|
||||
}
|
||||
|
||||
void MessagesList::addNew(MessagePosition messageId) {
|
||||
auto range = { messageId };
|
||||
addRange(range, { messageId, MaxMessagePosition }, base::none, true);
|
||||
}
|
||||
|
||||
void MessagesList::addSlice(
|
||||
std::vector<MessagePosition> &&messageIds,
|
||||
MessagesRange noSkipRange,
|
||||
base::optional<int> count) {
|
||||
addRange(messageIds, noSkipRange, count);
|
||||
}
|
||||
|
||||
void MessagesList::removeOne(MessagePosition messageId) {
|
||||
auto slice = ranges::lower_bound(
|
||||
_slices,
|
||||
messageId,
|
||||
std::less<>(),
|
||||
[](const Slice &slice) { return slice.range.till; });
|
||||
if (slice != _slices.end() && slice->range.from <= messageId) {
|
||||
_slices.modify(slice, [messageId](Slice &slice) {
|
||||
return slice.messages.remove(messageId);
|
||||
});
|
||||
}
|
||||
if (_count) {
|
||||
--*_count;
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesList::removeAll(ChannelId channelId) {
|
||||
// #TODO feeds show
|
||||
//_slices.clear();
|
||||
//_slices.emplace(base::flat_set<MessagePosition>{}, FullMessagesRange);
|
||||
}
|
||||
|
||||
rpl::producer<MessagesResult> MessagesList::query(
|
||||
MessagesQuery &&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));
|
||||
}
|
||||
consumer.put_done();
|
||||
return rpl::lifetime();
|
||||
};
|
||||
}
|
||||
|
||||
rpl::producer<MessagesSliceUpdate> MessagesList::sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
|
||||
MessagesResult MessagesList::queryFromSlice(
|
||||
const MessagesQuery &query,
|
||||
const Slice &slice) const {
|
||||
auto result = MessagesResult {};
|
||||
auto position = ranges::lower_bound(slice.messages, query.aroundId);
|
||||
auto haveBefore = int(position - begin(slice.messages));
|
||||
auto haveEqualOrAfter = int(end(slice.messages) - position);
|
||||
auto before = qMin(haveBefore, query.limitBefore);
|
||||
auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1);
|
||||
auto ids = std::vector<MessagePosition>(position - before, position + equalOrAfter);
|
||||
result.messageIds.merge(ids.begin(), ids.end());
|
||||
if (slice.range.from == MinMessagePosition) {
|
||||
result.skippedBefore = haveBefore - before;
|
||||
}
|
||||
if (slice.range.till == MaxMessagePosition) {
|
||||
result.skippedAfter = haveEqualOrAfter - equalOrAfter;
|
||||
}
|
||||
if (_count) {
|
||||
result.count = _count;
|
||||
if (!result.skippedBefore && result.skippedAfter) {
|
||||
result.skippedBefore = *result.count
|
||||
- *result.skippedAfter
|
||||
- int(result.messageIds.size());
|
||||
} else if (!result.skippedAfter && result.skippedBefore) {
|
||||
result.skippedAfter = *result.count
|
||||
- *result.skippedBefore
|
||||
- int(result.messageIds.size());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MessagesSliceBuilder::MessagesSliceBuilder(
|
||||
Key key,
|
||||
int limitBefore,
|
||||
int limitAfter)
|
||||
: _key(key)
|
||||
, _limitBefore(limitBefore)
|
||||
, _limitAfter(limitAfter) {
|
||||
}
|
||||
|
||||
bool MessagesSliceBuilder::applyInitial(const MessagesResult &result) {
|
||||
mergeSliceData(
|
||||
result.count,
|
||||
result.messageIds,
|
||||
result.skippedBefore,
|
||||
result.skippedAfter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagesSliceBuilder::applyUpdate(const MessagesSliceUpdate &update) {
|
||||
auto intersects = [](MessagesRange range1, MessagesRange range2) {
|
||||
return (range1.from <= range2.till)
|
||||
&& (range2.from <= range1.till);
|
||||
};
|
||||
auto needMergeMessages = (update.messages != nullptr)
|
||||
&& intersects(update.range, {
|
||||
_ids.empty() ? _key : _ids.front(),
|
||||
_ids.empty() ? _key : _ids.back()
|
||||
});
|
||||
if (!needMergeMessages && !update.count) {
|
||||
return false;
|
||||
}
|
||||
auto skippedBefore = (update.range.from == MinMessagePosition)
|
||||
? 0
|
||||
: base::optional<int> {};
|
||||
auto skippedAfter = (update.range.till == MaxMessagePosition)
|
||||
? 0
|
||||
: base::optional<int> {};
|
||||
mergeSliceData(
|
||||
update.count,
|
||||
needMergeMessages
|
||||
? *update.messages
|
||||
: base::flat_set<MessagePosition> {},
|
||||
skippedBefore,
|
||||
skippedAfter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagesSliceBuilder::removeOne(MessagePosition messageId) {
|
||||
auto changed = false;
|
||||
if (_fullCount && *_fullCount > 0) {
|
||||
--*_fullCount;
|
||||
changed = true;
|
||||
}
|
||||
if (_ids.contains(messageId)) {
|
||||
_ids.remove(messageId);
|
||||
changed = true;
|
||||
} else if (!_ids.empty()) {
|
||||
if (_ids.front() > messageId
|
||||
&& _skippedBefore
|
||||
&& *_skippedBefore > 0) {
|
||||
--*_skippedBefore;
|
||||
changed = true;
|
||||
} else if (_ids.back() < messageId
|
||||
&& _skippedAfter
|
||||
&& *_skippedAfter > 0) {
|
||||
--*_skippedAfter;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool MessagesSliceBuilder::removeAll() {
|
||||
_ids = {};
|
||||
_range = FullMessagesRange;
|
||||
_fullCount = 0;
|
||||
_skippedBefore = 0;
|
||||
_skippedAfter = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagesSliceBuilder::removeFromChannel(ChannelId channelId) {
|
||||
for (auto i = _ids.begin(); i != _ids.end();) {
|
||||
if ((*i).fullId.channel == channelId) {
|
||||
i = _ids.erase(i);
|
||||
if (_fullCount) {
|
||||
--*_fullCount;
|
||||
}
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
_skippedBefore = _skippedAfter = base::none;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessagesSliceBuilder::checkInsufficient() {
|
||||
sliceToLimits();
|
||||
}
|
||||
|
||||
void MessagesSliceBuilder::mergeSliceData(
|
||||
base::optional<int> count,
|
||||
const base::flat_set<MessagePosition> &messageIds,
|
||||
base::optional<int> skippedBefore,
|
||||
base::optional<int> skippedAfter) {
|
||||
if (messageIds.empty()) {
|
||||
if (count && _fullCount != count) {
|
||||
_fullCount = count;
|
||||
if (*_fullCount <= _ids.size()) {
|
||||
_fullCount = _ids.size();
|
||||
_skippedBefore = _skippedAfter = 0;
|
||||
}
|
||||
}
|
||||
fillSkippedAndSliceToLimits();
|
||||
return;
|
||||
}
|
||||
if (count) {
|
||||
_fullCount = count;
|
||||
}
|
||||
const auto impossible = MessagePosition(-1, FullMsgId());
|
||||
auto wasMinId = _ids.empty() ? impossible : _ids.front();
|
||||
auto wasMaxId = _ids.empty() ? impossible : _ids.back();
|
||||
_ids.merge(messageIds.begin(), messageIds.end());
|
||||
|
||||
auto adjustSkippedBefore = [&](MessagePosition oldId, int oldSkippedBefore) {
|
||||
auto it = _ids.find(oldId);
|
||||
Assert(it != _ids.end());
|
||||
_skippedBefore = oldSkippedBefore - (it - _ids.begin());
|
||||
accumulate_max(*_skippedBefore, 0);
|
||||
};
|
||||
if (skippedBefore) {
|
||||
adjustSkippedBefore(messageIds.front(), *skippedBefore);
|
||||
} else if (wasMinId != impossible && _skippedBefore) {
|
||||
adjustSkippedBefore(wasMinId, *_skippedBefore);
|
||||
} else {
|
||||
_skippedBefore = base::none;
|
||||
}
|
||||
|
||||
auto adjustSkippedAfter = [&](MessagePosition oldId, int oldSkippedAfter) {
|
||||
auto it = _ids.find(oldId);
|
||||
Assert(it != _ids.end());
|
||||
_skippedAfter = oldSkippedAfter - (_ids.end() - it - 1);
|
||||
accumulate_max(*_skippedAfter, 0);
|
||||
};
|
||||
if (skippedAfter) {
|
||||
adjustSkippedAfter(messageIds.back(), *skippedAfter);
|
||||
} else if (wasMaxId != impossible && _skippedAfter) {
|
||||
adjustSkippedAfter(wasMaxId, *_skippedAfter);
|
||||
} else {
|
||||
_skippedAfter = base::none;
|
||||
}
|
||||
fillSkippedAndSliceToLimits();
|
||||
}
|
||||
|
||||
void MessagesSliceBuilder::fillSkippedAndSliceToLimits() {
|
||||
if (_fullCount) {
|
||||
if (_skippedBefore && !_skippedAfter) {
|
||||
_skippedAfter = *_fullCount
|
||||
- *_skippedBefore
|
||||
- int(_ids.size());
|
||||
} else if (_skippedAfter && !_skippedBefore) {
|
||||
_skippedBefore = *_fullCount
|
||||
- *_skippedAfter
|
||||
- int(_ids.size());
|
||||
}
|
||||
}
|
||||
sliceToLimits();
|
||||
}
|
||||
|
||||
void MessagesSliceBuilder::sliceToLimits() {
|
||||
if (!_key) {
|
||||
if (!_fullCount) {
|
||||
requestMessagesCount();
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto requestedSomething = false;
|
||||
auto aroundIt = ranges::lower_bound(_ids, _key);
|
||||
auto removeFromBegin = (aroundIt - _ids.begin() - _limitBefore);
|
||||
auto removeFromEnd = (_ids.end() - aroundIt - _limitAfter - 1);
|
||||
if (removeFromBegin > 0) {
|
||||
_ids.erase(_ids.begin(), _ids.begin() + removeFromBegin);
|
||||
if (_skippedBefore) {
|
||||
*_skippedBefore += removeFromBegin;
|
||||
}
|
||||
} else if (removeFromBegin < 0
|
||||
&& (!_skippedBefore || *_skippedBefore > 0)) {
|
||||
requestedSomething = true;
|
||||
requestMessages(RequestDirection::Before);
|
||||
}
|
||||
if (removeFromEnd > 0) {
|
||||
_ids.erase(_ids.end() - removeFromEnd, _ids.end());
|
||||
if (_skippedAfter) {
|
||||
*_skippedAfter += removeFromEnd;
|
||||
}
|
||||
} else if (removeFromEnd < 0
|
||||
&& (!_skippedAfter || *_skippedAfter > 0)) {
|
||||
requestedSomething = true;
|
||||
requestMessages(RequestDirection::After);
|
||||
}
|
||||
if (!_fullCount && !requestedSomething) {
|
||||
requestMessagesCount();
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesSliceBuilder::requestMessages(RequestDirection direction) {
|
||||
auto requestAroundData = [&]() -> AroundData {
|
||||
if (_ids.empty()) {
|
||||
return { _key, Data::LoadDirection::Around };
|
||||
} else if (direction == RequestDirection::Before) {
|
||||
return { _ids.front(), Data::LoadDirection::Before };
|
||||
}
|
||||
return { _ids.back(), Data::LoadDirection::After };
|
||||
};
|
||||
_insufficientAround.fire(requestAroundData());
|
||||
}
|
||||
|
||||
void MessagesSliceBuilder::requestMessagesCount() {
|
||||
_insufficientAround.fire({
|
||||
MessagePosition(),
|
||||
Data::LoadDirection::Around });
|
||||
}
|
||||
|
||||
MessagesSlice MessagesSliceBuilder::snapshot() const {
|
||||
auto result = MessagesSlice();
|
||||
result.ids.reserve(_ids.size());
|
||||
for (const auto &position : _ids) {
|
||||
result.ids.push_back(position.fullId);
|
||||
}
|
||||
result.skippedBefore = _skippedBefore;
|
||||
result.skippedAfter = _skippedAfter;
|
||||
result.fullCount = _fullCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Data
|
244
Telegram/SourceFiles/data/data_messages.h
Normal file
244
Telegram/SourceFiles/data/data_messages.h
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
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 MaxDate = std::numeric_limits<TimeId>::max();
|
||||
constexpr auto MinMessagePosition = MessagePosition(TimeId(0), FullMsgId());
|
||||
constexpr auto MaxMessagePosition = MessagePosition(MaxDate, FullMsgId());
|
||||
constexpr auto FullMessagesRange = MessagesRange(
|
||||
MinMessagePosition,
|
||||
MaxMessagePosition);
|
||||
constexpr auto UnreadMessagePosition = MinMessagePosition;
|
||||
|
||||
struct MessagesSlice {
|
||||
std::vector<FullMsgId> ids;
|
||||
base::optional<int> skippedBefore;
|
||||
base::optional<int> skippedAfter;
|
||||
base::optional<int> 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 {
|
||||
base::optional<int> count;
|
||||
base::optional<int> skippedBefore;
|
||||
base::optional<int> skippedAfter;
|
||||
base::flat_set<MessagePosition> messageIds;
|
||||
};
|
||||
|
||||
struct MessagesSliceUpdate {
|
||||
const base::flat_set<MessagePosition> *messages = nullptr;
|
||||
MessagesRange range;
|
||||
base::optional<int> count;
|
||||
};
|
||||
|
||||
class MessagesList {
|
||||
public:
|
||||
void addNew(MessagePosition messageId);
|
||||
void addSlice(
|
||||
std::vector<MessagePosition> &&messageIds,
|
||||
MessagesRange noSkipRange,
|
||||
base::optional<int> count);
|
||||
void removeOne(MessagePosition messageId);
|
||||
void removeAll(ChannelId channelId);
|
||||
rpl::producer<MessagesResult> query(MessagesQuery &&query) const;
|
||||
rpl::producer<MessagesSliceUpdate> sliceUpdated() 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,
|
||||
base::optional<int> count,
|
||||
bool incrementCount = false);
|
||||
|
||||
MessagesResult queryFromSlice(
|
||||
const MessagesQuery &query,
|
||||
const Slice &slice) const;
|
||||
|
||||
base::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 removeFromChannel(ChannelId channelId);
|
||||
bool removeAll();
|
||||
|
||||
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(
|
||||
base::optional<int> count,
|
||||
const base::flat_set<MessagePosition> &messageIds,
|
||||
base::optional<int> skippedBefore = base::none,
|
||||
base::optional<int> skippedAfter = base::none);
|
||||
|
||||
MessagePosition _key;
|
||||
base::flat_set<MessagePosition> _ids;
|
||||
MessagesRange _range;
|
||||
base::optional<int> _fullCount;
|
||||
base::optional<int> _skippedBefore;
|
||||
base::optional<int> _skippedAfter;
|
||||
int _limitBefore = 0;
|
||||
int _limitAfter = 0;
|
||||
|
||||
rpl::event_stream<AroundData> _insufficientAround;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "auth_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_messages.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
@ -23,7 +24,7 @@ MTPmessages_Search PrepareSearchRequest(
|
||||
Storage::SharedMediaType type,
|
||||
const QString &query,
|
||||
MsgId messageId,
|
||||
SparseIdsLoadDirection direction) {
|
||||
Data::LoadDirection direction) {
|
||||
const auto filter = [&] {
|
||||
using Type = Storage::SharedMediaType;
|
||||
switch (type) {
|
||||
@ -58,17 +59,17 @@ MTPmessages_Search PrepareSearchRequest(
|
||||
const auto limit = messageId ? kSharedMediaLimit : 0;
|
||||
const auto offsetId = [&] {
|
||||
switch (direction) {
|
||||
case SparseIdsLoadDirection::Before:
|
||||
case SparseIdsLoadDirection::Around: return messageId;
|
||||
case SparseIdsLoadDirection::After: return messageId + 1;
|
||||
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 SparseIdsLoadDirection::Before: return 0;
|
||||
case SparseIdsLoadDirection::Around: return -limit / 2;
|
||||
case SparseIdsLoadDirection::After: return -limit;
|
||||
case Data::LoadDirection::Before: return 0;
|
||||
case Data::LoadDirection::Around: return -limit / 2;
|
||||
case Data::LoadDirection::After: return -limit;
|
||||
}
|
||||
Unexpected("Direction in PrepareSearchRequest");
|
||||
}();
|
||||
@ -92,7 +93,7 @@ SearchResult ParseSearchResult(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type,
|
||||
MsgId messageId,
|
||||
SparseIdsLoadDirection direction,
|
||||
Data::LoadDirection direction,
|
||||
const MTPmessages_Messages &data) {
|
||||
auto result = SearchResult();
|
||||
result.noSkipRange = MsgRange{ messageId, messageId };
|
||||
@ -158,11 +159,11 @@ SearchResult ParseSearchResult(
|
||||
if (messageId && result.messageIds.empty()) {
|
||||
result.noSkipRange = [&]() -> MsgRange {
|
||||
switch (direction) {
|
||||
case SparseIdsLoadDirection::Before: // All old loaded.
|
||||
case Data::LoadDirection::Before: // All old loaded.
|
||||
return { 0, result.noSkipRange.till };
|
||||
case SparseIdsLoadDirection::Around: // All loaded.
|
||||
case Data::LoadDirection::Around: // All loaded.
|
||||
return { 0, ServerMaxMsgId };
|
||||
case SparseIdsLoadDirection::After: // All new loaded.
|
||||
case Data::LoadDirection::After: // All new loaded.
|
||||
return { result.noSkipRange.from, ServerMaxMsgId };
|
||||
}
|
||||
Unexpected("Direction in ParseSearchResult");
|
||||
|
@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "base/value_ordering.h"
|
||||
|
||||
namespace Data {
|
||||
enum class LoadDirection : char;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
struct SearchResult {
|
||||
@ -26,13 +30,13 @@ MTPmessages_Search PrepareSearchRequest(
|
||||
Storage::SharedMediaType type,
|
||||
const QString &query,
|
||||
MsgId messageId,
|
||||
SparseIdsLoadDirection direction);
|
||||
Data::LoadDirection direction);
|
||||
|
||||
SearchResult ParseSearchResult(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type,
|
||||
MsgId messageId,
|
||||
SparseIdsLoadDirection direction,
|
||||
Data::LoadDirection direction,
|
||||
const MTPmessages_Messages &data);
|
||||
|
||||
class SearchController : private MTP::Sender {
|
||||
|
@ -361,17 +361,17 @@ void SparseIdsSliceBuilder::requestMessages(
|
||||
RequestDirection direction) {
|
||||
auto requestAroundData = [&]() -> AroundData {
|
||||
if (_ids.empty()) {
|
||||
return { _key, SparseIdsLoadDirection::Around };
|
||||
return { _key, Data::LoadDirection::Around };
|
||||
} else if (direction == RequestDirection::Before) {
|
||||
return { _ids.front(), SparseIdsLoadDirection::Before };
|
||||
return { _ids.front(), Data::LoadDirection::Before };
|
||||
}
|
||||
return { _ids.back(), SparseIdsLoadDirection::After };
|
||||
return { _ids.back(), Data::LoadDirection::After };
|
||||
};
|
||||
_insufficientAround.fire(requestAroundData());
|
||||
}
|
||||
|
||||
void SparseIdsSliceBuilder::requestMessagesCount() {
|
||||
_insufficientAround.fire({ 0, SparseIdsLoadDirection::Around });
|
||||
_insufficientAround.fire({ 0, Data::LoadDirection::Around });
|
||||
}
|
||||
|
||||
SparseIdsSlice SparseIdsSliceBuilder::snapshot() const {
|
||||
|
@ -7,17 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_messages.h"
|
||||
|
||||
namespace Storage {
|
||||
struct SparseIdsListResult;
|
||||
struct SparseIdsSliceUpdate;
|
||||
} // namespace Storage
|
||||
|
||||
enum class SparseIdsLoadDirection {
|
||||
Around,
|
||||
Before,
|
||||
After,
|
||||
};
|
||||
|
||||
class SparseIdsSlice {
|
||||
public:
|
||||
using Key = MsgId;
|
||||
@ -178,8 +174,7 @@ public:
|
||||
void checkInsufficient();
|
||||
struct AroundData {
|
||||
MsgId aroundId = 0;
|
||||
SparseIdsLoadDirection direction
|
||||
= SparseIdsLoadDirection::Around;
|
||||
Data::LoadDirection direction = Data::LoadDirection::Around;
|
||||
|
||||
inline bool operator<(const AroundData &other) const {
|
||||
return (aroundId < other.aroundId)
|
||||
|
@ -133,29 +133,43 @@ inline bool operator!=(const MsgRange &a, const MsgRange &b) {
|
||||
}
|
||||
|
||||
struct FullMsgId {
|
||||
FullMsgId() = default;
|
||||
FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) {
|
||||
constexpr FullMsgId() = default;
|
||||
constexpr FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) {
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return msg != 0;
|
||||
}
|
||||
|
||||
|
||||
inline constexpr bool operator<(const FullMsgId &other) const {
|
||||
if (channel < other.channel) {
|
||||
return true;
|
||||
} else if (channel > other.channel) {
|
||||
return false;
|
||||
}
|
||||
return msg < other.msg;
|
||||
}
|
||||
inline constexpr bool operator>(const FullMsgId &other) const {
|
||||
return other < *this;
|
||||
}
|
||||
inline constexpr bool operator<=(const FullMsgId &other) const {
|
||||
return !(other < *this);
|
||||
}
|
||||
inline constexpr bool operator>=(const FullMsgId &other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
inline constexpr bool operator==(const FullMsgId &other) const {
|
||||
return (channel == other.channel) && (msg == other.msg);
|
||||
}
|
||||
inline constexpr bool operator!=(const FullMsgId &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
ChannelId channel = NoChannel;
|
||||
MsgId msg = 0;
|
||||
|
||||
};
|
||||
inline bool operator==(const FullMsgId &a, const FullMsgId &b) {
|
||||
return (a.channel == b.channel) && (a.msg == b.msg);
|
||||
}
|
||||
inline bool operator!=(const FullMsgId &a, const FullMsgId &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
inline bool operator<(const FullMsgId &a, const FullMsgId &b) {
|
||||
if (a.channel < b.channel) {
|
||||
return true;
|
||||
} else if (a.channel > b.channel) {
|
||||
return false;
|
||||
}
|
||||
return a.msg < b.msg;
|
||||
}
|
||||
|
||||
using MessageIdsList = std::vector<FullMsgId>;
|
||||
|
||||
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "dialogs/dialogs_layout.h"
|
||||
#include "dialogs/dialogs_search_from_controllers.h"
|
||||
#include "history/feed/history_feed_section.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_window.h"
|
||||
@ -1739,8 +1740,8 @@ void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) {
|
||||
addSavedPeersAfter(feed->chatsListDate());
|
||||
}
|
||||
if (dialog.has_read_max_position()) {
|
||||
const auto position = Data::FeedPosition(dialog.vread_max_position);
|
||||
feed->setUnreadPosition(position);
|
||||
feed->setUnreadPosition(
|
||||
Data::FeedPositionFromMTP(dialog.vread_max_position));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2307,8 +2308,7 @@ bool DialogsInner::chooseRow() {
|
||||
if (const auto history = chosen.key.history()) {
|
||||
App::main()->choosePeer(history->peer->id, chosen.messageId);
|
||||
} else if (const auto feed = chosen.key.feed()) {
|
||||
// #TODO feeds open
|
||||
// _controller->showSection(HistoryFeed::Memento(feed));
|
||||
_controller->showSection(HistoryFeed::Memento(feed));
|
||||
}
|
||||
if (openSearchResult) {
|
||||
emit searchResultChosen();
|
||||
|
@ -5,7 +5,7 @@ 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 "history/history_admin_log_filter.h"
|
||||
#include "history/admin_log/history_admin_log_filter.h"
|
||||
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/widgets/checkbox.h"
|
@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
|
||||
namespace AdminLog {
|
||||
|
@ -5,15 +5,15 @@ 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 "history/history_admin_log_inner.h"
|
||||
#include "history/admin_log/history_admin_log_inner.h"
|
||||
|
||||
#include "styles/style_history.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
#include "history/history_admin_log_filter.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "history/admin_log/history_admin_log_filter.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
@ -691,7 +691,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
if (auto date = item->Get<HistoryMessageDate>()) {
|
||||
date->paint(p, dateY, width);
|
||||
} else {
|
||||
HistoryLayout::ServiceMessagePainter::paintDate(p, item->date, dateY, width);
|
||||
HistoryView::ServiceMessagePainter::paintDate(
|
||||
p,
|
||||
item->date,
|
||||
dateY,
|
||||
width);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -723,7 +727,12 @@ void InnerWidget::paintEmpty(Painter &p) {
|
||||
auto innerWidth = rectWidth - st::historyAdminLogEmptyPadding.left() - st::historyAdminLogEmptyPadding.right();
|
||||
auto rectHeight = st::historyAdminLogEmptyPadding.top() + _emptyText.countHeight(innerWidth) + st::historyAdminLogEmptyPadding.bottom();
|
||||
auto rect = QRect((width() - rectWidth) / 2, (height() - rectHeight) / 3, rectWidth, rectHeight);
|
||||
HistoryLayout::ServiceMessagePainter::paintBubble(p, rect.x(), rect.y(), rect.width(), rect.height());
|
||||
HistoryView::ServiceMessagePainter::paintBubble(
|
||||
p,
|
||||
rect.x(),
|
||||
rect.y(),
|
||||
rect.width(),
|
||||
rect.height());
|
||||
|
||||
p.setPen(st::msgServiceFg);
|
||||
_emptyText.draw(p, rect.x() + st::historyAdminLogEmptyPadding.left(), rect.y() + st::historyAdminLogEmptyPadding.top(), innerWidth, style::al_top);
|
@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "history/history_admin_log_item.h"
|
||||
#include "history/history_admin_log_section.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "mtproto/sender.h"
|
@ -5,11 +5,11 @@ 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 "history/history_admin_log_item.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
|
||||
#include "history/history_service.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_admin_log_inner.h"
|
||||
#include "history/admin_log/history_admin_log_inner.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/sticker_set_box.h"
|
||||
#include "core/tl_help.h"
|
@ -5,10 +5,10 @@ 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 "history/history_admin_log_section.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
|
||||
#include "history/history_admin_log_inner.h"
|
||||
#include "history/history_admin_log_filter.h"
|
||||
#include "history/admin_log/history_admin_log_inner.h"
|
||||
#include "history/admin_log/history_admin_log_filter.h"
|
||||
#include "profile/profile_back_button.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_window.h"
|
||||
@ -379,45 +379,10 @@ void Widget::paintEvent(QPaintEvent *e) {
|
||||
// updateListSize();
|
||||
//}
|
||||
|
||||
Painter p(this);
|
||||
auto clip = e->rect();
|
||||
auto ms = getms();
|
||||
//auto ms = getms();
|
||||
//_historyDownShown.step(ms);
|
||||
|
||||
auto fill = QRect(0, 0, width(), App::main()->height());
|
||||
auto fromy = App::main()->backgroundFromY();
|
||||
auto x = 0, y = 0;
|
||||
auto cached = App::main()->cachedBackground(fill, x, y);
|
||||
if (cached.isNull()) {
|
||||
if (Window::Theme::Background()->tile()) {
|
||||
auto &pix = Window::Theme::Background()->pixmapForTiled();
|
||||
auto left = clip.left();
|
||||
auto top = clip.top();
|
||||
auto right = clip.left() + clip.width();
|
||||
auto bottom = clip.top() + clip.height();
|
||||
auto w = pix.width() / cRetinaFactor();
|
||||
auto h = pix.height() / cRetinaFactor();
|
||||
auto sx = qFloor(left / w);
|
||||
auto sy = qFloor((top - fromy) / h);
|
||||
auto cx = qCeil(right / w);
|
||||
auto cy = qCeil((bottom - fromy) / h);
|
||||
for (auto i = sx; i < cx; ++i) {
|
||||
for (auto j = sy; j < cy; ++j) {
|
||||
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
auto &pix = Window::Theme::Background()->pixmap();
|
||||
QRect to, from;
|
||||
Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from);
|
||||
to.moveTop(to.top() + fromy);
|
||||
p.drawPixmap(to, pix, from);
|
||||
}
|
||||
} else {
|
||||
p.drawPixmap(x, fromy + y, cached);
|
||||
}
|
||||
SectionWidget::PaintBackground(this, e);
|
||||
}
|
||||
|
||||
void Widget::onScroll() {
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "window/section_widget.h"
|
||||
#include "window/section_memento.h"
|
||||
#include "history/history_admin_log_item.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Notify {
|
265
Telegram/SourceFiles/history/feed/history_feed_section.cpp
Normal file
265
Telegram/SourceFiles/history/feed/history_feed_section.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
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 "history/feed/history_feed_section.h"
|
||||
|
||||
#include "history/view/history_view_top_bar_widget.h"
|
||||
#include "history/view/history_view_list_widget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "data/data_feed_messages.h"
|
||||
#include "storage/storage_feed_messages.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
namespace HistoryFeed {
|
||||
|
||||
Memento::Memento(
|
||||
not_null<Data::Feed*> feed,
|
||||
Data::MessagePosition aroundPosition)
|
||||
: _feed(feed)
|
||||
, _list(std::make_unique<HistoryView::ListMemento>(aroundPosition)) {
|
||||
}
|
||||
|
||||
Memento::~Memento() = default;
|
||||
|
||||
object_ptr<Window::SectionWidget> Memento::createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Window::Column column,
|
||||
const QRect &geometry) {
|
||||
if (column == Window::Column::Third) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = object_ptr<Widget>(parent, controller, _feed);
|
||||
result->setInternalState(geometry, this);
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<Data::Feed*> feed)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
, _feed(feed)
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _topBar(this, controller)
|
||||
, _topBarShadow(this)
|
||||
, _showNext(
|
||||
this,
|
||||
lang(lng_feed_show_next).toUpper(),
|
||||
st::historyComposeButton) {
|
||||
_topBar->move(0, 0);
|
||||
_topBar->resizeToWidth(width());
|
||||
_topBar->show();
|
||||
|
||||
_topBarShadow->raise();
|
||||
updateAdaptiveLayout();
|
||||
subscribe(Adaptive::Changed(), [this] { updateAdaptiveLayout(); });
|
||||
|
||||
_inner = _scroll->setOwnedWidget(
|
||||
object_ptr<HistoryView::ListWidget>(this, controller, this));
|
||||
_scroll->move(0, _topBar->height());
|
||||
_scroll->show();
|
||||
|
||||
connect(
|
||||
_scroll,
|
||||
&Ui::ScrollArea::scrolled,
|
||||
this,
|
||||
[this] { onScroll(); });
|
||||
|
||||
_showNext->setClickedCallback([this] {
|
||||
// #TODO feeds show next
|
||||
Ui::show(Box<InformBox>(lang(lng_admin_log_about_text)));
|
||||
});
|
||||
|
||||
_feed->unreadPositionChanges(
|
||||
) | rpl::filter([=](const Data::MessagePosition &position) {
|
||||
return _undefinedAroundPosition && position;
|
||||
}) | rpl::start_with_next([=](const Data::MessagePosition &position) {
|
||||
auto memento = HistoryView::ListMemento(position);
|
||||
_inner->restoreState(&memento);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void Widget::updateAdaptiveLayout() {
|
||||
_topBarShadow->moveToLeft(
|
||||
Adaptive::OneColumn() ? 0 : st::lineWidth,
|
||||
_topBar->height());
|
||||
}
|
||||
|
||||
QPixmap Widget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) {
|
||||
if (params.withTopBarShadow) _topBarShadow->hide();
|
||||
auto result = Ui::GrabWidget(this);
|
||||
if (params.withTopBarShadow) _topBarShadow->show();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Widget::doSetInnerFocus() {
|
||||
_inner->setFocus();
|
||||
}
|
||||
|
||||
bool Widget::showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) {
|
||||
if (const auto feedMemento = dynamic_cast<Memento*>(memento.get())) {
|
||||
if (feedMemento->feed() == _feed) {
|
||||
restoreState(feedMemento);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Widget::setInternalState(
|
||||
const QRect &geometry,
|
||||
not_null<Memento*> memento) {
|
||||
setGeometry(geometry);
|
||||
Ui::SendPendingMoveResizeEvents(this);
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
bool Widget::cmd_search() {
|
||||
if (!inFocusChain()) {
|
||||
return false;
|
||||
}
|
||||
// #TODO feeds search
|
||||
return true;
|
||||
}
|
||||
|
||||
void Widget::listScrollTo(int top) {
|
||||
if (_scroll->scrollTop() != top) {
|
||||
_scroll->scrollToY(top);
|
||||
} else {
|
||||
updateInnerVisibleArea();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::listCloseRequest() {
|
||||
controller()->showBackFromStack();
|
||||
}
|
||||
|
||||
rpl::producer<Data::MessagesSlice> Widget::listSource(
|
||||
Data::MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) {
|
||||
return Data::FeedMessagesViewer(
|
||||
Storage::FeedMessagesKey(_feed->id(), aroundId),
|
||||
limitBefore,
|
||||
limitAfter);
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||
auto result = std::make_unique<Memento>(_feed);
|
||||
saveState(result.get());
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
void Widget::saveState(not_null<Memento*> memento) {
|
||||
_inner->saveState(memento->list());
|
||||
}
|
||||
|
||||
void Widget::restoreState(not_null<Memento*> memento) {
|
||||
const auto list = memento->list();
|
||||
if (!list->aroundPosition()) {
|
||||
if (const auto position = _feed->unreadPosition()) {
|
||||
list->setAroundPosition(position);
|
||||
}
|
||||
}
|
||||
_undefinedAroundPosition = !list->aroundPosition();
|
||||
_inner->restoreState(memento->list());
|
||||
}
|
||||
|
||||
void Widget::resizeEvent(QResizeEvent *e) {
|
||||
if (!width() || !height()) {
|
||||
return;
|
||||
}
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
void Widget::updateControlsGeometry() {
|
||||
const auto contentWidth = width();
|
||||
|
||||
const auto newScrollTop = _scroll->scrollTop() + topDelta();
|
||||
_topBar->resizeToWidth(contentWidth);
|
||||
_topBarShadow->resize(contentWidth, st::lineWidth);
|
||||
|
||||
const auto bottom = height();
|
||||
const auto scrollHeight = bottom
|
||||
- _topBar->height()
|
||||
- _showNext->height();
|
||||
const auto scrollSize = QSize(contentWidth, scrollHeight);
|
||||
if (_scroll->size() != scrollSize) {
|
||||
_scroll->resize(scrollSize);
|
||||
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
|
||||
//_inner->restoreScrollPosition();
|
||||
}
|
||||
|
||||
if (!_scroll->isHidden()) {
|
||||
if (topDelta()) {
|
||||
_scroll->scrollToY(newScrollTop);
|
||||
}
|
||||
updateInnerVisibleArea();
|
||||
}
|
||||
const auto fullWidthButtonRect = myrtlrect(
|
||||
0,
|
||||
bottom - _showNext->height(),
|
||||
contentWidth,
|
||||
_showNext->height());
|
||||
_showNext->setGeometry(fullWidthButtonRect);
|
||||
}
|
||||
|
||||
void Widget::paintEvent(QPaintEvent *e) {
|
||||
if (animating()) {
|
||||
SectionWidget::paintEvent(e);
|
||||
return;
|
||||
}
|
||||
if (Ui::skipPaintEvent(this, e)) {
|
||||
return;
|
||||
}
|
||||
//if (hasPendingResizedItems()) {
|
||||
// updateListSize();
|
||||
//}
|
||||
|
||||
//auto ms = getms();
|
||||
//_historyDownShown.step(ms);
|
||||
|
||||
SectionWidget::PaintBackground(this, e);
|
||||
}
|
||||
|
||||
void Widget::onScroll() {
|
||||
updateInnerVisibleArea();
|
||||
}
|
||||
|
||||
void Widget::updateInnerVisibleArea() {
|
||||
const auto scrollTop = _scroll->scrollTop();
|
||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||
}
|
||||
|
||||
void Widget::showAnimatedHook(
|
||||
const Window::SectionSlideParams ¶ms) {
|
||||
_topBar->setAnimatingMode(true);
|
||||
if (params.withTopBarShadow) _topBarShadow->show();
|
||||
}
|
||||
|
||||
void Widget::showFinishedHook() {
|
||||
_topBar->setAnimatingMode(false);
|
||||
}
|
||||
|
||||
bool Widget::wheelEventFromFloatPlayer(QEvent *e) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
|
||||
QRect Widget::rectForFloatPlayer() const {
|
||||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
} // namespace HistoryFeed
|
122
Telegram/SourceFiles/history/feed/history_feed_section.h
Normal file
122
Telegram/SourceFiles/history/feed/history_feed_section.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
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 "history/view/history_view_list_widget.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "window/section_memento.h"
|
||||
#include "data/data_feed.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScrollArea;
|
||||
class PlainShadow;
|
||||
class FlatButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
class ListWidget;
|
||||
class TopBarWidget;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace HistoryFeed {
|
||||
|
||||
class Memento;
|
||||
|
||||
class Widget final
|
||||
: public Window::SectionWidget
|
||||
, public HistoryView::ListDelegate {
|
||||
public:
|
||||
Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<Data::Feed*> feed);
|
||||
|
||||
bool hasTopBarShadow() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
QPixmap grabForShowAnimation(
|
||||
const Window::SectionSlideParams ¶ms) override;
|
||||
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
std::unique_ptr<Window::SectionMemento> createMemento() override;
|
||||
|
||||
void setInternalState(
|
||||
const QRect &geometry,
|
||||
not_null<Memento*> memento);
|
||||
|
||||
// Float player interface.
|
||||
bool wheelEventFromFloatPlayer(QEvent *e) override;
|
||||
QRect rectForFloatPlayer() const override;
|
||||
|
||||
bool cmd_search() override;
|
||||
|
||||
// HistoryView::ListDelegate interface.
|
||||
void listScrollTo(int top) override;
|
||||
void listCloseRequest() override;
|
||||
rpl::producer<Data::MessagesSlice> listSource(
|
||||
Data::MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void showAnimatedHook(
|
||||
const Window::SectionSlideParams ¶ms) override;
|
||||
void showFinishedHook() override;
|
||||
void doSetInnerFocus() override;
|
||||
|
||||
private:
|
||||
void onScroll();
|
||||
void updateInnerVisibleArea();
|
||||
void updateControlsGeometry();
|
||||
void updateAdaptiveLayout();
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
not_null<Data::Feed*> _feed;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<HistoryView::ListWidget> _inner;
|
||||
object_ptr<HistoryView::TopBarWidget> _topBar;
|
||||
object_ptr<Ui::PlainShadow> _topBarShadow;
|
||||
object_ptr<Ui::FlatButton> _showNext;
|
||||
bool _undefinedAroundPosition = false;
|
||||
|
||||
};
|
||||
|
||||
class Memento : public Window::SectionMemento {
|
||||
public:
|
||||
explicit Memento(
|
||||
not_null<Data::Feed*> feed,
|
||||
Data::MessagePosition aroundPosition = Data::UnreadMessagePosition);
|
||||
~Memento();
|
||||
|
||||
object_ptr<Window::SectionWidget> createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
Window::Column column,
|
||||
const QRect &geometry) override;
|
||||
|
||||
not_null<Data::Feed*> feed() const {
|
||||
return _feed;
|
||||
}
|
||||
not_null<HistoryView::ListMemento*> list() const {
|
||||
return _list.get();
|
||||
}
|
||||
|
||||
private:
|
||||
not_null<Data::Feed*> _feed;
|
||||
std::unique_ptr<HistoryView::ListMemento> _list;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryFeed
|
@ -11,9 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_history.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "window/window_controller.h"
|
||||
@ -475,7 +475,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
p.restoreTextPalette();
|
||||
}
|
||||
} else if (noHistoryDisplayed) {
|
||||
HistoryLayout::paintEmpty(p, width(), height());
|
||||
HistoryView::paintEmpty(p, width(), height());
|
||||
}
|
||||
if (!noHistoryDisplayed) {
|
||||
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
|
||||
@ -637,7 +637,11 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
if (auto date = item->Get<HistoryMessageDate>()) {
|
||||
date->paint(p, dateY, width);
|
||||
} else {
|
||||
HistoryLayout::ServiceMessagePainter::paintDate(p, item->date, dateY, width);
|
||||
HistoryView::ServiceMessagePainter::paintDate(
|
||||
p,
|
||||
item->date,
|
||||
dateY,
|
||||
width);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2117,8 +2121,9 @@ bool HistoryInner::canDeleteSelected() const {
|
||||
return (selectedState.count > 0) && (selectedState.count == selectedState.canDeleteCount);
|
||||
}
|
||||
|
||||
HistoryTopBarWidget::SelectedState HistoryInner::getSelectionState() const {
|
||||
auto result = HistoryTopBarWidget::SelectedState {};
|
||||
auto HistoryInner::getSelectionState() const
|
||||
-> HistoryView::TopBarWidget::SelectedState {
|
||||
auto result = HistoryView::TopBarWidget::SelectedState {};
|
||||
for (auto &selected : _selected) {
|
||||
if (selected.second == FullSelection) {
|
||||
++result.count;
|
||||
|
@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "history/history_top_bar_widget.h"
|
||||
#include "history/view/history_view_top_bar_widget.h"
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
@ -50,7 +50,7 @@ public:
|
||||
bool canCopySelected() const;
|
||||
bool canDeleteSelected() const;
|
||||
|
||||
HistoryTopBarWidget::SelectedState getSelectionState() const;
|
||||
HistoryView::TopBarWidget::SelectedState getSelectionState() const;
|
||||
void clearSelectedItems(bool onlyTextSelection = false);
|
||||
MessageIdsList getSelectedItems() const;
|
||||
void selectItem(not_null<HistoryItem*> item);
|
||||
|
@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_media_grouped.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "media/media_clip_reader.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_history.h"
|
||||
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_controller.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_messages.h"
|
||||
#include "data/data_feed.h"
|
||||
|
||||
namespace {
|
||||
@ -635,6 +636,10 @@ QString HistoryItem::directLink() const {
|
||||
return QString();
|
||||
}
|
||||
|
||||
Data::MessagePosition HistoryItem::position() const {
|
||||
return Data::MessagePosition(toServerTime(date.toTime_t()).v, fullId());
|
||||
}
|
||||
|
||||
MsgId HistoryItem::replyToId() const {
|
||||
if (auto reply = Get<HistoryMessageReply>()) {
|
||||
return reply->replyToId();
|
||||
|
@ -37,6 +37,10 @@ struct BotKeyboardButton;
|
||||
struct RippleAnimation;
|
||||
} // namespace style
|
||||
|
||||
namespace Data {
|
||||
struct MessagePosition;
|
||||
} // namespace Data
|
||||
|
||||
class HistoryElement {
|
||||
public:
|
||||
HistoryElement() = default;
|
||||
@ -434,6 +438,7 @@ public:
|
||||
FullMsgId fullId() const {
|
||||
return FullMsgId(channelId(), id);
|
||||
}
|
||||
Data::MessagePosition position() const;
|
||||
|
||||
HistoryMedia *getMedia() const {
|
||||
return _media.get();
|
||||
|
@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_media.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "media/media_audio.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "styles/style_widgets.h"
|
||||
@ -825,7 +825,7 @@ int HistoryMessageDate::height() const {
|
||||
}
|
||||
|
||||
void HistoryMessageDate::paint(Painter &p, int y, int w) const {
|
||||
HistoryLayout::ServiceMessagePainter::paintDate(p, _text, _width, y, w);
|
||||
HistoryView::ServiceMessagePainter::paintDate(p, _text, _width, y, w);
|
||||
}
|
||||
|
||||
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default;
|
||||
|
@ -13,9 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_location_manager.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_service.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "auth_session.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
@ -1797,7 +1797,7 @@ void HistoryMessage::draw(Painter &p, QRect clip, TextSelection selection, TimeM
|
||||
|| (_media && _media->skipBubbleTail())
|
||||
|| (keyboard != nullptr);
|
||||
auto displayTail = skipTail ? RectPart::None : (outbg && !Adaptive::ChatWide()) ? RectPart::Right : RectPart::Left;
|
||||
HistoryLayout::paintBubble(p, g, width(), selected, outbg, displayTail);
|
||||
HistoryView::paintBubble(p, g, width(), selected, outbg, displayTail);
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && _media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/);
|
||||
|
@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "data/data_feed.h"
|
||||
#include "auth_session.h"
|
||||
#include "window/notifications_manager.h"
|
||||
@ -512,8 +512,8 @@ void HistoryService::draw(Painter &p, QRect clip, TextSelection selection, TimeM
|
||||
height -= unreadbarh;
|
||||
}
|
||||
|
||||
HistoryLayout::PaintContext context(ms, clip, selection);
|
||||
HistoryLayout::ServiceMessagePainter::paint(p, this, context, height);
|
||||
HistoryView::PaintContext context(ms, clip, selection);
|
||||
HistoryView::ServiceMessagePainter::paint(p, this, context, height);
|
||||
|
||||
if (auto skiph = dateh + unreadbarh) {
|
||||
p.translate(0, -skiph);
|
||||
|
@ -34,9 +34,9 @@ struct HistoryServiceSelfDestruct : public RuntimeComponent<HistoryServiceSelfDe
|
||||
TimeMs destructAt = 0;
|
||||
};
|
||||
|
||||
namespace HistoryLayout {
|
||||
namespace HistoryView {
|
||||
class ServiceMessagePainter;
|
||||
} // namespace HistoryLayout
|
||||
} // namespace HistoryView
|
||||
|
||||
class HistoryService : public HistoryItem, private HistoryItemInstantiated<HistoryService> {
|
||||
public:
|
||||
@ -102,7 +102,7 @@ public:
|
||||
~HistoryService();
|
||||
|
||||
protected:
|
||||
friend class HistoryLayout::ServiceMessagePainter;
|
||||
friend class HistoryView::ServiceMessagePainter;
|
||||
|
||||
HistoryService(not_null<History*> history, const MTPDmessage &message);
|
||||
HistoryService(not_null<History*> history, const MTPDmessageService &message);
|
||||
|
@ -24,11 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "history/history_drag_area.h"
|
||||
#include "history/history_inner_widget.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "profile/profile_block_group_members.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "core/click_handler_types.h"
|
||||
@ -51,7 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "media/media_audio_capture.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "apiwrap.h"
|
||||
#include "history/history_top_bar_widget.h"
|
||||
#include "history/view/history_view_top_bar_widget.h"
|
||||
#include "observer_peer.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
@ -1704,7 +1704,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||
|
||||
noSelectingScroll();
|
||||
_nonEmptySelection = false;
|
||||
_topBar->showSelected(HistoryTopBarWidget::SelectedState {});
|
||||
_topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
|
||||
|
||||
App::hoveredItem(nullptr);
|
||||
App::pressedItem(nullptr);
|
||||
@ -3039,7 +3039,7 @@ void HistoryWidget::showAnimated(
|
||||
_a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition());
|
||||
if (_history) {
|
||||
_topBar->show();
|
||||
_topBar->setAnimationMode(true);
|
||||
_topBar->setAnimatingMode(true);
|
||||
}
|
||||
|
||||
activate();
|
||||
@ -3056,7 +3056,7 @@ void HistoryWidget::animationCallback() {
|
||||
}
|
||||
|
||||
void HistoryWidget::doneShow() {
|
||||
_topBar->setAnimationMode(false);
|
||||
_topBar->setAnimatingMode(false);
|
||||
updateReportSpamStatus();
|
||||
updateBotKeyboard();
|
||||
updateControlsVisibility();
|
||||
@ -6142,7 +6142,7 @@ MessageIdsList HistoryWidget::getSelectedItems() const {
|
||||
|
||||
void HistoryWidget::updateTopBarSelection() {
|
||||
if (!_list) {
|
||||
_topBar->showSelected(HistoryTopBarWidget::SelectedState {});
|
||||
_topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6489,81 +6489,52 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryWidget::paintShowAnimationFrame(TimeMs ms) {
|
||||
auto progress = _a_show.current(ms, 1.);
|
||||
if (!_a_show.animating()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Painter p(this);
|
||||
auto animationWidth = width();
|
||||
auto retina = cIntRetinaFactor();
|
||||
auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft);
|
||||
auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress);
|
||||
auto coordOver = fromLeft ? anim::interpolate(0, animationWidth, progress) : anim::interpolate(animationWidth, 0, progress);
|
||||
auto shadow = fromLeft ? (1. - progress) : progress;
|
||||
if (coordOver > 0) {
|
||||
p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, height() * retina));
|
||||
p.setOpacity(shadow);
|
||||
p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg);
|
||||
p.setOpacity(1);
|
||||
}
|
||||
p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, 0, _cacheOver.width(), height() * retina));
|
||||
p.setOpacity(shadow);
|
||||
st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||
if (!App::main() || (App::wnd() && App::wnd()->contentOverlapped(this, e))) {
|
||||
auto ms = getms();
|
||||
_historyDownShown.step(ms);
|
||||
_unreadMentionsShown.step(ms);
|
||||
if (paintShowAnimationFrame(ms)) {
|
||||
return;
|
||||
}
|
||||
if (Ui::skipPaintEvent(this, e)) {
|
||||
return;
|
||||
}
|
||||
if (hasPendingResizedItems()) {
|
||||
updateListSize();
|
||||
}
|
||||
|
||||
Window::SectionWidget::PaintBackground(this, e);
|
||||
|
||||
Painter p(this);
|
||||
QRect r(e->rect());
|
||||
if (r != rect()) {
|
||||
p.setClipRect(r);
|
||||
}
|
||||
|
||||
auto ms = getms();
|
||||
_historyDownShown.step(ms);
|
||||
_unreadMentionsShown.step(ms);
|
||||
auto progress = _a_show.current(ms, 1.);
|
||||
if (_a_show.animating()) {
|
||||
auto animationWidth = width();
|
||||
auto retina = cIntRetinaFactor();
|
||||
auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft);
|
||||
auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress);
|
||||
auto coordOver = fromLeft ? anim::interpolate(0, animationWidth, progress) : anim::interpolate(animationWidth, 0, progress);
|
||||
auto shadow = fromLeft ? (1. - progress) : progress;
|
||||
if (coordOver > 0) {
|
||||
p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, height() * retina));
|
||||
p.setOpacity(shadow);
|
||||
p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg);
|
||||
p.setOpacity(1);
|
||||
}
|
||||
p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, 0, _cacheOver.width(), height() * retina));
|
||||
p.setOpacity(shadow);
|
||||
st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
|
||||
return;
|
||||
}
|
||||
|
||||
QRect fill(0, 0, width(), App::main()->height());
|
||||
auto fromy = App::main()->backgroundFromY();
|
||||
auto x = 0, y = 0;
|
||||
QPixmap cached = App::main()->cachedBackground(fill, x, y);
|
||||
if (cached.isNull()) {
|
||||
if (Window::Theme::Background()->tile()) {
|
||||
auto &pix = Window::Theme::Background()->pixmapForTiled();
|
||||
auto left = r.left();
|
||||
auto top = r.top();
|
||||
auto right = r.left() + r.width();
|
||||
auto bottom = r.top() + r.height();
|
||||
auto w = pix.width() / cRetinaFactor();
|
||||
auto h = pix.height() / cRetinaFactor();
|
||||
auto sx = qFloor(left / w);
|
||||
auto sy = qFloor((top - fromy) / h);
|
||||
auto cx = qCeil(right / w);
|
||||
auto cy = qCeil((bottom - fromy) / h);
|
||||
for (auto i = sx; i < cx; ++i) {
|
||||
for (auto j = sy; j < cy; ++j) {
|
||||
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
auto &pix = Window::Theme::Background()->pixmap();
|
||||
QRect to, from;
|
||||
Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from);
|
||||
to.moveTop(to.top() + fromy);
|
||||
p.drawPixmap(to, pix, from);
|
||||
}
|
||||
} else {
|
||||
p.drawPixmap(x, fromy + y, cached);
|
||||
}
|
||||
|
||||
const auto clip = e->rect();
|
||||
if (_list) {
|
||||
if (!_field->isHidden() || _recording) {
|
||||
drawField(p, r);
|
||||
drawField(p, clip);
|
||||
if (!_send->isHidden() && _recording) {
|
||||
drawRecording(p, _send->recordActiveRatio());
|
||||
}
|
||||
@ -6575,13 +6546,13 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
if (_scroll->isHidden()) {
|
||||
p.setClipRect(_scroll->geometry());
|
||||
HistoryLayout::paintEmpty(p, width(), height() - _field->height() - 2 * st::historySendPadding);
|
||||
HistoryView::paintEmpty(p, width(), height() - _field->height() - 2 * st::historySendPadding);
|
||||
}
|
||||
} else {
|
||||
style::font font(st::msgServiceFont);
|
||||
int32 w = font->width(lang(lng_willbe_history)) + st::msgPadding.left() + st::msgPadding.right(), h = font->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + 2;
|
||||
QRect tr((width() - w) / 2, (height() - _field->height() - 2 * st::historySendPadding - h) / 2, w, h);
|
||||
HistoryLayout::ServiceMessagePainter::paintBubble(p, tr.x(), tr.y(), tr.width(), tr.height());
|
||||
HistoryView::ServiceMessagePainter::paintBubble(p, tr.x(), tr.y(), tr.width(), tr.height());
|
||||
|
||||
p.setPen(st::msgServiceFg);
|
||||
p.setFont(font->f);
|
||||
|
@ -65,12 +65,15 @@ enum class MimeDataState;
|
||||
struct PreparedList;
|
||||
} // namespace Storage
|
||||
|
||||
namespace HistoryView {
|
||||
class TopBarWidget;
|
||||
} // namespace HistoryView
|
||||
|
||||
class DragArea;
|
||||
class SendFilesBox;
|
||||
class BotKeyboard;
|
||||
class MessageField;
|
||||
class HistoryInner;
|
||||
class HistoryTopBarWidget;
|
||||
struct HistoryMessageMarkupButton;
|
||||
|
||||
class ReportSpamPanel : public TWidget {
|
||||
@ -592,6 +595,7 @@ private:
|
||||
void drawRecording(Painter &p, float64 recordActive);
|
||||
void drawPinnedBar(Painter &p);
|
||||
void drawRestrictedWrite(Painter &p);
|
||||
bool paintShowAnimationFrame(TimeMs ms);
|
||||
|
||||
void updateMouseTracking();
|
||||
|
||||
@ -740,7 +744,7 @@ private:
|
||||
MsgId _delayedShowAtMsgId = -1; // wtf?
|
||||
mtpRequestId _delayedShowAtRequest = 0;
|
||||
|
||||
object_ptr<HistoryTopBarWidget> _topBar;
|
||||
object_ptr<HistoryView::TopBarWidget> _topBar;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<HistoryInner> _list;
|
||||
History *_migrated = nullptr;
|
||||
|
1364
Telegram/SourceFiles/history/view/history_view_list_widget.cpp
Normal file
1364
Telegram/SourceFiles/history/view/history_view_list_widget.cpp
Normal file
File diff suppressed because it is too large
Load Diff
261
Telegram/SourceFiles/history/view/history_view_list_widget.h
Normal file
261
Telegram/SourceFiles/history/view/history_view_list_widget.h
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
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 "ui/widgets/tooltip.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/timer.h"
|
||||
#include "data/data_messages.h"
|
||||
|
||||
namespace Ui {
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class ListDelegate {
|
||||
public:
|
||||
virtual void listScrollTo(int top) = 0;
|
||||
virtual void listCloseRequest() = 0;
|
||||
virtual rpl::producer<Data::MessagesSlice> listSource(
|
||||
Data::MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) = 0;
|
||||
};
|
||||
|
||||
class ListMemento {
|
||||
public:
|
||||
struct ScrollTopState {
|
||||
Data::MessagePosition item;
|
||||
int shift = 0;
|
||||
};
|
||||
|
||||
explicit ListMemento(Data::MessagePosition position)
|
||||
: _aroundPosition(position) {
|
||||
}
|
||||
void setAroundPosition(Data::MessagePosition position) {
|
||||
_aroundPosition = position;
|
||||
}
|
||||
Data::MessagePosition aroundPosition() const {
|
||||
return _aroundPosition;
|
||||
}
|
||||
void setIdsLimit(int limit) {
|
||||
_idsLimit = limit;
|
||||
}
|
||||
int idsLimit() const {
|
||||
return _idsLimit;
|
||||
}
|
||||
void setScrollTopState(ScrollTopState state) {
|
||||
_scrollTopState = state;
|
||||
}
|
||||
ScrollTopState scrollTopState() const {
|
||||
return _scrollTopState;
|
||||
}
|
||||
|
||||
private:
|
||||
Data::MessagePosition _aroundPosition;
|
||||
ScrollTopState _scrollTopState;
|
||||
int _idsLimit = 0;
|
||||
|
||||
};
|
||||
|
||||
class ListWidget final
|
||||
: public Ui::RpWidget
|
||||
, public Ui::AbstractTooltipShower
|
||||
, private base::Subscriber {
|
||||
public:
|
||||
ListWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<ListDelegate*> delegate);
|
||||
|
||||
// Set the correct scroll position after being resized.
|
||||
void restoreScrollPosition();
|
||||
|
||||
void resizeToWidth(int newWidth, int minHeight) {
|
||||
_minHeight = minHeight;
|
||||
return TWidget::resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
void saveState(not_null<ListMemento*> memento);
|
||||
void restoreState(not_null<ListMemento*> memento);
|
||||
|
||||
// AbstractTooltipShower interface
|
||||
QString tooltipText() const override;
|
||||
QPoint tooltipPos() const override;
|
||||
|
||||
~ListWidget();
|
||||
|
||||
protected:
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||
void enterEventHook(QEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
|
||||
// Resize content and count natural widget height for the desired width.
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private:
|
||||
enum class Direction {
|
||||
Up,
|
||||
Down,
|
||||
};
|
||||
enum class MouseAction {
|
||||
None,
|
||||
PrepareDrag,
|
||||
Dragging,
|
||||
Selecting,
|
||||
};
|
||||
enum class EnumItemsDirection {
|
||||
TopToBottom,
|
||||
BottomToTop,
|
||||
};
|
||||
using ScrollTopState = ListMemento::ScrollTopState;
|
||||
|
||||
void refreshViewer();
|
||||
void updateAroundPositionFromRows();
|
||||
void refreshRows();
|
||||
ScrollTopState countScrollState() const;
|
||||
void saveScrollState();
|
||||
void restoreScrollState();
|
||||
|
||||
void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button);
|
||||
void mouseActionUpdate(const QPoint &screenPos);
|
||||
void mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button);
|
||||
void mouseActionCancel();
|
||||
void updateSelected();
|
||||
void performDrag();
|
||||
int itemTop(not_null<const HistoryItem*> item) const;
|
||||
void repaintItem(const HistoryItem *item);
|
||||
QPoint mapPointToItem(QPoint point, const HistoryItem *item) const;
|
||||
void handlePendingHistoryResize();
|
||||
|
||||
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
||||
void savePhotoToFile(PhotoData *photo);
|
||||
void saveDocumentToFile(DocumentData *document);
|
||||
void copyContextImage(PhotoData *photo);
|
||||
void showStickerPackInfo();
|
||||
void copyContextUrl();
|
||||
void cancelContextDownload();
|
||||
void showContextInFolder();
|
||||
void openContextGif();
|
||||
void copyContextText();
|
||||
void copySelectedText();
|
||||
TextWithEntities getSelectedText() const;
|
||||
void setToClipboard(
|
||||
const TextWithEntities &forClipboard,
|
||||
QClipboard::Mode mode = QClipboard::Clipboard);
|
||||
|
||||
not_null<HistoryItem*> findItemByY(int y) const;
|
||||
HistoryItem *strictFindItemByY(int y) const;
|
||||
int findNearestItem(Data::MessagePosition position) const;
|
||||
|
||||
void checkMoveToOtherViewer();
|
||||
void updateVisibleTopItem();
|
||||
void itemsAdded(Direction direction, int addedCount);
|
||||
void updateSize();
|
||||
|
||||
void toggleScrollDateShown();
|
||||
void repaintScrollDateCallback();
|
||||
bool displayScrollDate() const;
|
||||
void scrollDateHide();
|
||||
void scrollDateCheck();
|
||||
void scrollDateHideByTimer();
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Method has "bool (*Method)(HistoryItem *item, int itemtop, int itembottom)" signature
|
||||
// if it returns false the enumeration stops immediately.
|
||||
template <EnumItemsDirection direction, typename Method>
|
||||
void enumerateItems(Method method);
|
||||
|
||||
// This function finds all userpics on the left that are displayed and calls template method
|
||||
// for each found userpic (from the top to the bottom) using enumerateItems() method.
|
||||
//
|
||||
// Method has "bool (*Method)(not_null<HistoryMessage*> message, int userpicTop)" signature
|
||||
// if it returns false the enumeration stops immediately.
|
||||
template <typename Method>
|
||||
void enumerateUserpics(Method method);
|
||||
|
||||
// This function finds all date elements that are displayed and calls template method
|
||||
// for each found date element (from the bottom to the top) using enumerateItems() method.
|
||||
//
|
||||
// Method has "bool (*Method)(not_null<HistoryItem*> item, int itemtop, int dateTop)" signature
|
||||
// if it returns false the enumeration stops immediately.
|
||||
template <typename Method>
|
||||
void enumerateDates(Method method);
|
||||
|
||||
static constexpr auto kMinimalIdsLimit = 24;
|
||||
|
||||
not_null<ListDelegate*> _delegate;
|
||||
not_null<Window::Controller*> _controller;
|
||||
Data::MessagePosition _aroundPosition;
|
||||
int _aroundIndex = -1;
|
||||
int _idsLimit = kMinimalIdsLimit;
|
||||
Data::MessagesSlice _slice;
|
||||
std::vector<not_null<HistoryItem*>> _items;
|
||||
std::map<uint64, HistoryItem*> _itemsByIds;
|
||||
int _itemsTop = 0;
|
||||
int _itemsHeight = 0;
|
||||
|
||||
int _minHeight = 0;
|
||||
int _visibleTop = 0;
|
||||
int _visibleBottom = 0;
|
||||
HistoryItem *_visibleTopItem = nullptr;
|
||||
int _visibleTopFromItem = 0;
|
||||
ScrollTopState _scrollTopState;
|
||||
|
||||
bool _scrollDateShown = false;
|
||||
Animation _scrollDateOpacity;
|
||||
SingleQueuedInvokation _scrollDateCheck;
|
||||
base::Timer _scrollDateHideTimer;
|
||||
HistoryItem *_scrollDateLastItem = nullptr;
|
||||
int _scrollDateLastItemTop = 0;
|
||||
|
||||
MouseAction _mouseAction = MouseAction::None;
|
||||
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
||||
QPoint _dragStartPosition;
|
||||
QPoint _mousePosition;
|
||||
HistoryItem *_mouseActionItem = nullptr;
|
||||
HistoryCursorState _mouseCursorState = HistoryDefaultCursorState;
|
||||
uint16 _mouseTextSymbol = 0;
|
||||
bool _pressWasInactive = false;
|
||||
|
||||
HistoryItem *_selectedItem = nullptr;
|
||||
TextSelection _selectedText;
|
||||
bool _wasSelectedText = false; // was some text selected in current drag action
|
||||
Qt::CursorShape _cursor = style::cur_default;
|
||||
|
||||
// context menu
|
||||
Ui::PopupMenu *_menu = nullptr;
|
||||
|
||||
QPoint _trippleClickPoint;
|
||||
base::Timer _trippleClickTimer;
|
||||
|
||||
ClickHandlerPtr _contextMenuLink;
|
||||
|
||||
rpl::lifetime _viewerLifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
@ -5,7 +5,7 @@ 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 "history/history_service_layout.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
|
||||
#include "history/history_service.h"
|
||||
#include "history/history_media.h"
|
||||
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwidget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace HistoryLayout {
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
enum CircleMask {
|
||||
@ -359,4 +359,4 @@ void paintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool out
|
||||
App::roundRect(p, rect, bg, cors, &sh, parts);
|
||||
}
|
||||
|
||||
} // namespace HistoryLayout
|
||||
} // namespace HistoryView
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class HistoryService;
|
||||
|
||||
namespace HistoryLayout {
|
||||
namespace HistoryView {
|
||||
|
||||
int WideChatWidth();
|
||||
|
||||
@ -49,4 +49,4 @@ void serviceColorsUpdated();
|
||||
|
||||
void paintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool outbg, RectPart tailSide);
|
||||
|
||||
} // namespace HistoryLayout
|
||||
} // namespace HistoryView
|
@ -5,7 +5,7 @@ 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 "history/history_top_bar_widget.h"
|
||||
#include "history/view/history_view_top_bar_widget.h"
|
||||
|
||||
#include <rpl/combine.h>
|
||||
#include <rpl/combine_previous.h>
|
||||
@ -34,7 +34,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "observer_peer.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
HistoryTopBarWidget::HistoryTopBarWidget(
|
||||
namespace HistoryView {
|
||||
|
||||
TopBarWidget::TopBarWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller)
|
||||
: RpWidget(parent)
|
||||
@ -122,35 +124,35 @@ HistoryTopBarWidget::HistoryTopBarWidget(
|
||||
updateControlsVisibility();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::refreshLang() {
|
||||
void TopBarWidget::refreshLang() {
|
||||
InvokeQueued(this, [this] { updateControlsGeometry(); });
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::onForwardSelection() {
|
||||
void TopBarWidget::onForwardSelection() {
|
||||
if (App::main()) App::main()->forwardSelectedItems();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::onDeleteSelection() {
|
||||
void TopBarWidget::onDeleteSelection() {
|
||||
if (App::main()) App::main()->confirmDeleteSelectedItems();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::onClearSelection() {
|
||||
void TopBarWidget::onClearSelection() {
|
||||
if (App::main()) App::main()->clearSelectedItems();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::onSearch() {
|
||||
void TopBarWidget::onSearch() {
|
||||
if (_historyPeer) {
|
||||
App::main()->searchInPeer(_historyPeer);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::onCall() {
|
||||
void TopBarWidget::onCall() {
|
||||
if (auto user = _historyPeer->asUser()) {
|
||||
Calls::Current().startOutgoingCall(user);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::showMenu() {
|
||||
void TopBarWidget::showMenu() {
|
||||
if (!_historyPeer || _menu) {
|
||||
return;
|
||||
}
|
||||
@ -184,7 +186,7 @@ void HistoryTopBarWidget::showMenu() {
|
||||
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::toggleInfoSection() {
|
||||
void TopBarWidget::toggleInfoSection() {
|
||||
if (Adaptive::ThreeColumn()
|
||||
&& (Auth().settings().thirdSectionInfoEnabled()
|
||||
|| Auth().settings().tabbedReplacedWithInfo())) {
|
||||
@ -209,7 +211,7 @@ void HistoryTopBarWidget::toggleInfoSection() {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryTopBarWidget::eventFilter(QObject *obj, QEvent *e) {
|
||||
bool TopBarWidget::eventFilter(QObject *obj, QEvent *e) {
|
||||
if (obj == _membersShowArea) {
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseButtonPress:
|
||||
@ -228,8 +230,8 @@ bool HistoryTopBarWidget::eventFilter(QObject *obj, QEvent *e) {
|
||||
return TWidget::eventFilter(obj, e);
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::paintEvent(QPaintEvent *e) {
|
||||
if (_animationMode) {
|
||||
void TopBarWidget::paintEvent(QPaintEvent *e) {
|
||||
if (_animatingMode) {
|
||||
return;
|
||||
}
|
||||
Painter p(this);
|
||||
@ -247,7 +249,7 @@ void HistoryTopBarWidget::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::paintTopBar(Painter &p, TimeMs ms) {
|
||||
void TopBarWidget::paintTopBar(Painter &p, TimeMs ms) {
|
||||
auto history = App::historyLoaded(_historyPeer);
|
||||
if (!history) return;
|
||||
|
||||
@ -291,7 +293,7 @@ void HistoryTopBarWidget::paintTopBar(Painter &p, TimeMs ms) {
|
||||
}
|
||||
}
|
||||
|
||||
QRect HistoryTopBarWidget::getMembersShowAreaGeometry() const {
|
||||
QRect TopBarWidget::getMembersShowAreaGeometry() const {
|
||||
int membersTextLeft = _leftTaken;
|
||||
int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
|
||||
int membersTextWidth = _titlePeerTextWidth;
|
||||
@ -300,12 +302,12 @@ QRect HistoryTopBarWidget::getMembersShowAreaGeometry() const {
|
||||
return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight);
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::mousePressEvent(QMouseEvent *e) {
|
||||
void TopBarWidget::mousePressEvent(QMouseEvent *e) {
|
||||
auto handleClick = (e->button() == Qt::LeftButton)
|
||||
&& (e->pos().y() < st::topBarHeight)
|
||||
&& (!_selectedCount);
|
||||
if (handleClick) {
|
||||
if (_animationMode && _back->rect().contains(e->pos())) {
|
||||
if (_animatingMode && _back->rect().contains(e->pos())) {
|
||||
backClicked();
|
||||
} else if (_historyPeer) {
|
||||
infoClicked();
|
||||
@ -313,7 +315,7 @@ void HistoryTopBarWidget::mousePressEvent(QMouseEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::infoClicked() {
|
||||
void TopBarWidget::infoClicked() {
|
||||
if (_historyPeer && _historyPeer->isSelf()) {
|
||||
_controller->showSection(Info::Memento(
|
||||
_historyPeer->id,
|
||||
@ -323,11 +325,11 @@ void HistoryTopBarWidget::infoClicked() {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::backClicked() {
|
||||
void TopBarWidget::backClicked() {
|
||||
_controller->showBackFromStack();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::setHistoryPeer(PeerData *historyPeer) {
|
||||
void TopBarWidget::setHistoryPeer(PeerData *historyPeer) {
|
||||
if (_historyPeer != historyPeer) {
|
||||
_historyPeer = historyPeer;
|
||||
_back->clearState();
|
||||
@ -353,15 +355,15 @@ void HistoryTopBarWidget::setHistoryPeer(PeerData *historyPeer) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::resizeEvent(QResizeEvent *e) {
|
||||
void TopBarWidget::resizeEvent(QResizeEvent *e) {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
int HistoryTopBarWidget::countSelectedButtonsTop(float64 selectedShown) {
|
||||
int TopBarWidget::countSelectedButtonsTop(float64 selectedShown) {
|
||||
return (1. - selectedShown) * (-st::topBarHeight);
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateControlsGeometry() {
|
||||
void TopBarWidget::updateControlsGeometry() {
|
||||
auto hasSelected = (_selectedCount > 0);
|
||||
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.current(hasSelected ? 1. : 0.));
|
||||
auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
|
||||
@ -417,22 +419,22 @@ void HistoryTopBarWidget::updateControlsGeometry() {
|
||||
updateMembersShowArea();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::finishAnimating() {
|
||||
void TopBarWidget::finishAnimating() {
|
||||
_selectedShown.finish();
|
||||
updateControlsVisibility();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::setAnimationMode(bool enabled) {
|
||||
if (_animationMode != enabled) {
|
||||
_animationMode = enabled;
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, !_animationMode);
|
||||
void TopBarWidget::setAnimatingMode(bool enabled) {
|
||||
if (_animatingMode != enabled) {
|
||||
_animatingMode = enabled;
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, !_animatingMode);
|
||||
finishAnimating();
|
||||
updateControlsVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateControlsVisibility() {
|
||||
if (_animationMode) {
|
||||
void TopBarWidget::updateControlsVisibility() {
|
||||
if (_animatingMode) {
|
||||
hideChildren();
|
||||
return;
|
||||
}
|
||||
@ -465,7 +467,7 @@ void HistoryTopBarWidget::updateControlsVisibility() {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateMembersShowArea() {
|
||||
void TopBarWidget::updateMembersShowArea() {
|
||||
if (!App::main()) {
|
||||
return;
|
||||
}
|
||||
@ -496,7 +498,7 @@ void HistoryTopBarWidget::updateMembersShowArea() {
|
||||
_membersShowArea->setGeometry(getMembersShowAreaGeometry());
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::showSelected(SelectedState state) {
|
||||
void TopBarWidget::showSelected(SelectedState state) {
|
||||
auto canDelete = (state.count > 0 && state.count == state.canDeleteCount);
|
||||
auto canForward = (state.count > 0 && state.count == state.canForwardCount);
|
||||
if (_selectedCount == state.count && _canDelete == canDelete && _canForward == canForward) {
|
||||
@ -539,12 +541,12 @@ void HistoryTopBarWidget::showSelected(SelectedState state) {
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::selectedShowCallback() {
|
||||
void TopBarWidget::selectedShowCallback() {
|
||||
updateControlsGeometry();
|
||||
update();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateAdaptiveLayout() {
|
||||
void TopBarWidget::updateAdaptiveLayout() {
|
||||
updateControlsVisibility();
|
||||
if (Adaptive::OneColumn()) {
|
||||
createUnreadBadge();
|
||||
@ -555,7 +557,7 @@ void HistoryTopBarWidget::updateAdaptiveLayout() {
|
||||
updateInfoToggleActive();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::createUnreadBadge() {
|
||||
void TopBarWidget::createUnreadBadge() {
|
||||
if (_unreadBadge) {
|
||||
return;
|
||||
}
|
||||
@ -569,7 +571,7 @@ void HistoryTopBarWidget::createUnreadBadge() {
|
||||
updateUnreadBadge();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateUnreadBadge() {
|
||||
void TopBarWidget::updateUnreadBadge() {
|
||||
if (!_unreadBadge) return;
|
||||
|
||||
auto mutedCount = App::histories().unreadMutedCount();
|
||||
@ -596,7 +598,7 @@ void HistoryTopBarWidget::updateUnreadBadge() {
|
||||
}(), active);
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateInfoToggleActive() {
|
||||
void TopBarWidget::updateInfoToggleActive() {
|
||||
auto infoThirdActive = Adaptive::ThreeColumn()
|
||||
&& (Auth().settings().thirdSectionInfoEnabled()
|
||||
|| Auth().settings().tabbedReplacedWithInfo());
|
||||
@ -610,7 +612,7 @@ void HistoryTopBarWidget::updateInfoToggleActive() {
|
||||
_infoToggle->setRippleColorOverride(rippleOverride);
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateOnlineDisplay() {
|
||||
void TopBarWidget::updateOnlineDisplay() {
|
||||
if (!_historyPeer) return;
|
||||
|
||||
QString text;
|
||||
@ -689,7 +691,7 @@ void HistoryTopBarWidget::updateOnlineDisplay() {
|
||||
updateOnlineDisplayTimer();
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateOnlineDisplayTimer() {
|
||||
void TopBarWidget::updateOnlineDisplayTimer() {
|
||||
if (!_historyPeer) return;
|
||||
|
||||
const auto now = unixtime();
|
||||
@ -709,6 +711,8 @@ void HistoryTopBarWidget::updateOnlineDisplayTimer() {
|
||||
updateOnlineDisplayIn(minTimeout);
|
||||
}
|
||||
|
||||
void HistoryTopBarWidget::updateOnlineDisplayIn(TimeMs timeout) {
|
||||
void TopBarWidget::updateOnlineDisplayIn(TimeMs timeout) {
|
||||
_onlineUpdater.callOnce(timeout);
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
@ -22,9 +22,11 @@ namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
class HistoryTopBarWidget : public Ui::RpWidget, private base::Subscriber {
|
||||
namespace HistoryView {
|
||||
|
||||
class TopBarWidget : public Ui::RpWidget, private base::Subscriber {
|
||||
public:
|
||||
HistoryTopBarWidget(
|
||||
TopBarWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller);
|
||||
|
||||
@ -41,15 +43,10 @@ public:
|
||||
rpl::producer<bool> membersShowAreaActive() const {
|
||||
return _membersShowAreaActive.events();
|
||||
}
|
||||
void setAnimationMode(bool enabled);
|
||||
void setAnimatingMode(bool enabled);
|
||||
|
||||
void setHistoryPeer(PeerData *historyPeer);
|
||||
|
||||
static void paintUnreadCounter(
|
||||
Painter &p,
|
||||
int outerWidth,
|
||||
PeerData *substractPeer = nullptr);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
@ -116,9 +113,11 @@ private:
|
||||
int _titlePeerTextWidth = 0;
|
||||
int _leftTaken = 0;
|
||||
int _rightTaken = 0;
|
||||
bool _animationMode = false;
|
||||
bool _animatingMode = false;
|
||||
|
||||
int _unreadCounterSubscription = 0;
|
||||
base::Timer _onlineUpdater;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
@ -791,8 +791,7 @@ void ListWidget::refreshViewer() {
|
||||
idForViewer,
|
||||
_idsLimit,
|
||||
_idsLimit
|
||||
) | rpl::start_with_next([=](
|
||||
SparseIdsMergedSlice &&slice) {
|
||||
) | rpl::start_with_next([=](SparseIdsMergedSlice &&slice) {
|
||||
if (!slice.fullCount()) {
|
||||
// Don't display anything while full count is unknown.
|
||||
return;
|
||||
|
@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_widget.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_media.h"
|
||||
#include "history/history_service_layout.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
@ -3530,7 +3530,7 @@ void MainWidget::updateWindowAdaptiveLayout() {
|
||||
// auto thirdColumnWidth = _history->tabbedSelectorSectionWidth();
|
||||
// auto twoColumnsWidth = (layout.bodyWidth - thirdColumnWidth);
|
||||
// auto sameRatioChatWidth = twoColumnsWidth - qRound(dialogsWidthRatio * twoColumnsWidth);
|
||||
// auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryLayout::WideChatWidth());
|
||||
// auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryView::WideChatWidth());
|
||||
// chatWidth -= thirdColumnWidth;
|
||||
// auto extendChatBy = desiredChatWidth - chatWidth;
|
||||
// accumulate_min(extendChatBy, layout.dialogsWidth - st::columnMinimalWidthLeft);
|
||||
|
@ -503,9 +503,7 @@ auto GroupThumbs::validateCacheEntry(Key key) -> not_null<Thumb*> {
|
||||
}
|
||||
|
||||
void GroupThumbs::markCacheStale() {
|
||||
while (!_dying.empty()) {
|
||||
_dying.pop_back();
|
||||
}
|
||||
_dying.clear();
|
||||
for (const auto &cacheItem : _cache) {
|
||||
const auto &thumb = cacheItem.second;
|
||||
thumb->setState(Thumb::State::Unknown);
|
||||
|
@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "profile/profile_back_button.h"
|
||||
|
||||
//#include "history/history_top_bar_widget.h"
|
||||
//#include "history/view/history_view_top_bar_widget.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_profile.h"
|
||||
@ -41,8 +41,6 @@ void BackButton::paintEvent(QPaintEvent *e) {
|
||||
p.setFont(st::topBarButton.font);
|
||||
p.setPen(st::topBarButton.textFg);
|
||||
p.drawTextLeft(st::topBarArrowPadding.left(), st::topBarButton.padding.top() + st::topBarButton.textTop, width(), _text);
|
||||
|
||||
// HistoryTopBarWidget::paintUnreadCounter(p, width());
|
||||
}
|
||||
|
||||
void BackButton::onStateChanged(State was, StateChangeSource source) {
|
||||
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "storage/storage_user_photos.h"
|
||||
#include "storage/storage_feed_messages.h"
|
||||
|
||||
namespace Storage {
|
||||
|
||||
@ -31,9 +32,20 @@ public:
|
||||
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
|
||||
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() const;
|
||||
|
||||
void add(FeedMessagesAddNew &&query);
|
||||
void add(FeedMessagesAddSlice &&query);
|
||||
void remove(FeedMessagesRemoveOne &&query);
|
||||
void remove(FeedMessagesRemoveAll &&query);
|
||||
rpl::producer<FeedMessagesResult> query(
|
||||
FeedMessagesQuery &&query) const;
|
||||
rpl::producer<FeedMessagesSliceUpdate> feedMessagesSliceUpdated() const;
|
||||
rpl::producer<FeedMessagesRemoveOne> feedMessagesOneRemoved() const;
|
||||
rpl::producer<FeedMessagesRemoveAll> feedMessagesAllRemoved() const;
|
||||
|
||||
private:
|
||||
SharedMedia _sharedMedia;
|
||||
UserPhotos _userPhotos;
|
||||
FeedMessages _feedMessages;
|
||||
|
||||
};
|
||||
|
||||
@ -97,6 +109,39 @@ rpl::producer<UserPhotosSliceUpdate> Facade::Impl::userPhotosSliceUpdated() cons
|
||||
return _userPhotos.sliceUpdated();
|
||||
}
|
||||
|
||||
void Facade::Impl::add(FeedMessagesAddNew &&query) {
|
||||
return _feedMessages.add(std::move(query));
|
||||
}
|
||||
|
||||
void Facade::Impl::add(FeedMessagesAddSlice &&query) {
|
||||
return _feedMessages.add(std::move(query));
|
||||
}
|
||||
|
||||
void Facade::Impl::remove(FeedMessagesRemoveOne &&query) {
|
||||
return _feedMessages.remove(std::move(query));
|
||||
}
|
||||
|
||||
void Facade::Impl::remove(FeedMessagesRemoveAll &&query) {
|
||||
return _feedMessages.remove(std::move(query));
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesResult> Facade::Impl::query(
|
||||
FeedMessagesQuery &&query) const {
|
||||
return _feedMessages.query(std::move(query));
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesSliceUpdate> Facade::Impl::feedMessagesSliceUpdated() const {
|
||||
return _feedMessages.sliceUpdated();
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesRemoveOne> Facade::Impl::feedMessagesOneRemoved() const {
|
||||
return _feedMessages.oneRemoved();
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesRemoveAll> Facade::Impl::feedMessagesAllRemoved() const {
|
||||
return _feedMessages.allRemoved();
|
||||
}
|
||||
|
||||
Facade::Facade() : _impl(std::make_unique<Impl>()) {
|
||||
}
|
||||
|
||||
@ -160,6 +205,39 @@ rpl::producer<UserPhotosSliceUpdate> Facade::userPhotosSliceUpdated() const {
|
||||
return _impl->userPhotosSliceUpdated();
|
||||
}
|
||||
|
||||
void Facade::add(FeedMessagesAddNew &&query) {
|
||||
return _impl->add(std::move(query));
|
||||
}
|
||||
|
||||
void Facade::add(FeedMessagesAddSlice &&query) {
|
||||
return _impl->add(std::move(query));
|
||||
}
|
||||
|
||||
void Facade::remove(FeedMessagesRemoveOne &&query) {
|
||||
return _impl->remove(std::move(query));
|
||||
}
|
||||
|
||||
void Facade::remove(FeedMessagesRemoveAll &&query) {
|
||||
return _impl->remove(std::move(query));
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesResult> Facade::query(
|
||||
FeedMessagesQuery &&query) const {
|
||||
return _impl->query(std::move(query));
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesSliceUpdate> Facade::feedMessagesSliceUpdated() const {
|
||||
return _impl->feedMessagesSliceUpdated();
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesRemoveOne> Facade::feedMessagesOneRemoved() const {
|
||||
return _impl->feedMessagesOneRemoved();
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesRemoveAll> Facade::feedMessagesAllRemoved() const {
|
||||
return _impl->feedMessagesAllRemoved();
|
||||
}
|
||||
|
||||
Facade::~Facade() = default;
|
||||
|
||||
} // namespace Storage
|
||||
|
@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <rpl/producer.h>
|
||||
#include "base/enum_mask.h"
|
||||
|
||||
namespace Data {
|
||||
struct MessagesResult;
|
||||
} // namespace Data
|
||||
|
||||
namespace Storage {
|
||||
|
||||
struct SparseIdsListResult;
|
||||
@ -31,6 +35,14 @@ struct UserPhotosQuery;
|
||||
struct UserPhotosResult;
|
||||
struct UserPhotosSliceUpdate;
|
||||
|
||||
struct FeedMessagesAddNew;
|
||||
struct FeedMessagesAddSlice;
|
||||
struct FeedMessagesRemoveOne;
|
||||
struct FeedMessagesRemoveAll;
|
||||
struct FeedMessagesQuery;
|
||||
using FeedMessagesResult = Data::MessagesResult;
|
||||
struct FeedMessagesSliceUpdate;
|
||||
|
||||
class Facade {
|
||||
public:
|
||||
Facade();
|
||||
@ -54,6 +66,17 @@ public:
|
||||
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
|
||||
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() const;
|
||||
|
||||
void add(FeedMessagesAddNew &&query);
|
||||
void add(FeedMessagesAddSlice &&query);
|
||||
void remove(FeedMessagesRemoveOne &&query);
|
||||
void remove(FeedMessagesRemoveAll &&query);
|
||||
|
||||
rpl::producer<FeedMessagesResult> query(
|
||||
FeedMessagesQuery &&query) const;
|
||||
rpl::producer<FeedMessagesSliceUpdate> feedMessagesSliceUpdated() const;
|
||||
rpl::producer<FeedMessagesRemoveOne> feedMessagesOneRemoved() const;
|
||||
rpl::producer<FeedMessagesRemoveAll> feedMessagesAllRemoved() const;
|
||||
|
||||
~Facade();
|
||||
|
||||
private:
|
||||
|
85
Telegram/SourceFiles/storage/storage_feed_messages.cpp
Normal file
85
Telegram/SourceFiles/storage/storage_feed_messages.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
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 "storage/storage_feed_messages.h"
|
||||
|
||||
namespace Storage {
|
||||
|
||||
auto FeedMessages::enforceLists(FeedId feedId)
|
||||
-> std::map<FeedId, List>::iterator {
|
||||
auto result = _lists.find(feedId);
|
||||
if (result != _lists.end()) {
|
||||
return result;
|
||||
}
|
||||
result = _lists.emplace(feedId, List {}).first;
|
||||
result->second.sliceUpdated(
|
||||
) | rpl::map([=](Data::MessagesSliceUpdate &&update) {
|
||||
return FeedMessagesSliceUpdate(
|
||||
feedId,
|
||||
std::move(update));
|
||||
}) | rpl::start_to_stream(_sliceUpdated, _lifetime);
|
||||
return result;
|
||||
}
|
||||
|
||||
void FeedMessages::add(FeedMessagesAddNew &&query) {
|
||||
auto feedId = query.feedId;
|
||||
auto feedIt = enforceLists(feedId);
|
||||
feedIt->second.addNew(query.messageId);
|
||||
}
|
||||
|
||||
void FeedMessages::add(FeedMessagesAddSlice &&query) {
|
||||
auto feedIt = enforceLists(query.feedId);
|
||||
feedIt->second.addSlice(
|
||||
std::move(query.messageIds),
|
||||
query.noSkipRange,
|
||||
base::none);
|
||||
}
|
||||
|
||||
void FeedMessages::remove(FeedMessagesRemoveOne &&query) {
|
||||
auto feedIt = _lists.find(query.feedId);
|
||||
if (feedIt != _lists.end()) {
|
||||
feedIt->second.removeOne(query.messageId);
|
||||
_oneRemoved.fire(std::move(query));
|
||||
}
|
||||
}
|
||||
|
||||
void FeedMessages::remove(FeedMessagesRemoveAll &&query) {
|
||||
auto feedIt = _lists.find(query.feedId);
|
||||
if (feedIt != _lists.end()) {
|
||||
feedIt->second.removeAll(query.channelId);
|
||||
_allRemoved.fire(std::move(query));
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesResult> FeedMessages::query(
|
||||
FeedMessagesQuery &&query) const {
|
||||
auto feedIt = _lists.find(query.key.feedId);
|
||||
if (feedIt != _lists.end()) {
|
||||
return feedIt->second.query(Data::MessagesQuery(
|
||||
query.key.position,
|
||||
query.limitBefore,
|
||||
query.limitAfter));
|
||||
}
|
||||
return [](auto consumer) {
|
||||
consumer.put_done();
|
||||
return rpl::lifetime();
|
||||
};
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesSliceUpdate> FeedMessages::sliceUpdated() const {
|
||||
return _sliceUpdated.events();
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesRemoveOne> FeedMessages::oneRemoved() const {
|
||||
return _oneRemoved.events();
|
||||
}
|
||||
|
||||
rpl::producer<FeedMessagesRemoveAll> FeedMessages::allRemoved() const {
|
||||
return _allRemoved.events();
|
||||
}
|
||||
|
||||
} // namespace Storage
|
147
Telegram/SourceFiles/storage/storage_feed_messages.h
Normal file
147
Telegram/SourceFiles/storage/storage_feed_messages.h
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
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 <rpl/event_stream.h>
|
||||
#include "storage/storage_facade.h"
|
||||
#include "data/data_feed_messages.h"
|
||||
|
||||
namespace Storage {
|
||||
|
||||
struct FeedMessagesAddNew {
|
||||
FeedMessagesAddNew(FeedId feedId, Data::MessagePosition messageId)
|
||||
: feedId(feedId)
|
||||
, messageId(messageId) {
|
||||
}
|
||||
|
||||
FeedId feedId = 0;
|
||||
Data::MessagePosition messageId;
|
||||
|
||||
};
|
||||
|
||||
struct FeedMessagesAddSlice {
|
||||
FeedMessagesAddSlice(
|
||||
FeedId feedId,
|
||||
std::vector<Data::MessagePosition> &&messageIds,
|
||||
Data::MessagesRange noSkipRange)
|
||||
: feedId(feedId)
|
||||
, messageIds(std::move(messageIds))
|
||||
, noSkipRange(noSkipRange) {
|
||||
}
|
||||
|
||||
FeedId feedId = 0;
|
||||
std::vector<Data::MessagePosition> messageIds;
|
||||
Data::MessagesRange noSkipRange;
|
||||
|
||||
};
|
||||
|
||||
struct FeedMessagesRemoveOne {
|
||||
FeedMessagesRemoveOne(
|
||||
FeedId feedId,
|
||||
Data::MessagePosition messageId)
|
||||
: feedId(feedId)
|
||||
, messageId(messageId) {
|
||||
}
|
||||
|
||||
FeedId feedId = 0;
|
||||
Data::MessagePosition messageId;
|
||||
|
||||
};
|
||||
|
||||
struct FeedMessagesRemoveAll {
|
||||
FeedMessagesRemoveAll(FeedId feedId, ChannelId channelId)
|
||||
: feedId(feedId)
|
||||
, channelId(channelId) {
|
||||
}
|
||||
|
||||
FeedId feedId = 0;
|
||||
ChannelId channelId = 0;
|
||||
|
||||
};
|
||||
|
||||
struct FeedMessagesKey {
|
||||
FeedMessagesKey(
|
||||
FeedId feedId,
|
||||
Data::MessagePosition position)
|
||||
: feedId(feedId)
|
||||
, position(position) {
|
||||
}
|
||||
|
||||
bool operator==(const FeedMessagesKey &other) const {
|
||||
return (feedId == other.feedId)
|
||||
&& (position == other.position);
|
||||
}
|
||||
bool operator!=(const FeedMessagesKey &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
FeedId feedId = 0;
|
||||
Data::MessagePosition position;
|
||||
|
||||
};
|
||||
|
||||
struct FeedMessagesQuery {
|
||||
FeedMessagesQuery(
|
||||
FeedMessagesKey key,
|
||||
int limitBefore,
|
||||
int limitAfter)
|
||||
: key(key)
|
||||
, limitBefore(limitBefore)
|
||||
, limitAfter(limitAfter) {
|
||||
}
|
||||
|
||||
FeedMessagesKey key;
|
||||
int limitBefore = 0;
|
||||
int limitAfter = 0;
|
||||
|
||||
};
|
||||
|
||||
using FeedMessagesResult = Data::MessagesResult;
|
||||
|
||||
struct FeedMessagesSliceUpdate {
|
||||
FeedMessagesSliceUpdate(
|
||||
FeedId feedId,
|
||||
Data::MessagesSliceUpdate &&data)
|
||||
: feedId(feedId)
|
||||
, data(std::move(data)) {
|
||||
}
|
||||
|
||||
FeedId feedId = 0;
|
||||
Data::MessagesSliceUpdate data;
|
||||
|
||||
};
|
||||
|
||||
class FeedMessages {
|
||||
public:
|
||||
void add(FeedMessagesAddNew &&query);
|
||||
void add(FeedMessagesAddSlice &&query);
|
||||
void remove(FeedMessagesRemoveOne &&query);
|
||||
void remove(FeedMessagesRemoveAll &&query);
|
||||
|
||||
rpl::producer<FeedMessagesResult> query(
|
||||
FeedMessagesQuery &&query) const;
|
||||
rpl::producer<FeedMessagesSliceUpdate> sliceUpdated() const;
|
||||
rpl::producer<FeedMessagesRemoveOne> oneRemoved() const;
|
||||
rpl::producer<FeedMessagesRemoveAll> allRemoved() const;
|
||||
|
||||
private:
|
||||
using List = Data::MessagesList;
|
||||
|
||||
std::map<FeedId, List>::iterator enforceLists(FeedId feedId);
|
||||
|
||||
std::map<FeedId, List> _lists;
|
||||
|
||||
rpl::event_stream<FeedMessagesSliceUpdate> _sliceUpdated;
|
||||
rpl::event_stream<FeedMessagesRemoveOne> _oneRemoved;
|
||||
rpl::event_stream<FeedMessagesRemoveAll> _allRemoved;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Storage
|
@ -56,6 +56,7 @@ void SharedMedia::add(SharedMediaAddExisting &&query) {
|
||||
|
||||
void SharedMedia::add(SharedMediaAddSlice &&query) {
|
||||
Expects(IsValidSharedMediaType(query.type));
|
||||
|
||||
auto peerIt = enforceLists(query.peerId);
|
||||
auto index = static_cast<int>(query.type);
|
||||
peerIt->second[index].addSlice(
|
||||
|
@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <rpl/range.h>
|
||||
#include "application.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window/section_memento.h"
|
||||
#include "window/window_slide_animation.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
|
||||
namespace Window {
|
||||
|
||||
@ -71,6 +73,46 @@ void SectionWidget::showFast() {
|
||||
showFinished();
|
||||
}
|
||||
|
||||
void SectionWidget::PaintBackground(QWidget *widget, QPaintEvent *event) {
|
||||
Painter p(widget);
|
||||
|
||||
auto clip = event->rect();
|
||||
auto fill = QRect(0, 0, widget->width(), App::main()->height());
|
||||
auto fromy = App::main()->backgroundFromY();
|
||||
auto x = 0, y = 0;
|
||||
auto cached = App::main()->cachedBackground(fill, x, y);
|
||||
if (cached.isNull()) {
|
||||
if (Window::Theme::Background()->tile()) {
|
||||
auto &pix = Window::Theme::Background()->pixmapForTiled();
|
||||
auto left = clip.left();
|
||||
auto top = clip.top();
|
||||
auto right = clip.left() + clip.width();
|
||||
auto bottom = clip.top() + clip.height();
|
||||
auto w = pix.width() / cRetinaFactor();
|
||||
auto h = pix.height() / cRetinaFactor();
|
||||
auto sx = qFloor(left / w);
|
||||
auto sy = qFloor((top - fromy) / h);
|
||||
auto cx = qCeil(right / w);
|
||||
auto cy = qCeil((bottom - fromy) / h);
|
||||
for (auto i = sx; i < cx; ++i) {
|
||||
for (auto j = sy; j < cy; ++j) {
|
||||
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PainterHighQualityEnabler hq(p);
|
||||
|
||||
auto &pix = Window::Theme::Background()->pixmap();
|
||||
QRect to, from;
|
||||
Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from);
|
||||
to.moveTop(to.top() + fromy);
|
||||
p.drawPixmap(to, pix, from);
|
||||
}
|
||||
} else {
|
||||
p.drawPixmap(x, fromy + y, cached);
|
||||
}
|
||||
}
|
||||
|
||||
void SectionWidget::paintEvent(QPaintEvent *e) {
|
||||
if (_showAnimation) {
|
||||
Painter p(this);
|
||||
|
@ -126,6 +126,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void PaintBackground(QWidget *widget, QPaintEvent *event);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
|
@ -169,8 +169,12 @@
|
||||
<(src_loc)/data/data_drafts.h
|
||||
<(src_loc)/data/data_feed.cpp
|
||||
<(src_loc)/data/data_feed.h
|
||||
<(src_loc)/data/data_feed_messages.cpp
|
||||
<(src_loc)/data/data_feed_messages.h
|
||||
<(src_loc)/data/data_flags.h
|
||||
<(src_loc)/data/data_game.h
|
||||
<(src_loc)/data/data_messages.cpp
|
||||
<(src_loc)/data/data_messages.h
|
||||
<(src_loc)/data/data_notify_settings.cpp
|
||||
<(src_loc)/data/data_notify_settings.h
|
||||
<(src_loc)/data/data_peer.cpp
|
||||
@ -211,16 +215,24 @@
|
||||
<(src_loc)/dialogs/dialogs_search_from_controllers.h
|
||||
<(src_loc)/dialogs/dialogs_widget.cpp
|
||||
<(src_loc)/dialogs/dialogs_widget.h
|
||||
<(src_loc)/history/admin_log/history_admin_log_filter.cpp
|
||||
<(src_loc)/history/admin_log/history_admin_log_filter.h
|
||||
<(src_loc)/history/admin_log/history_admin_log_inner.cpp
|
||||
<(src_loc)/history/admin_log/history_admin_log_inner.h
|
||||
<(src_loc)/history/admin_log/history_admin_log_item.cpp
|
||||
<(src_loc)/history/admin_log/history_admin_log_item.h
|
||||
<(src_loc)/history/admin_log/history_admin_log_section.cpp
|
||||
<(src_loc)/history/admin_log/history_admin_log_section.h
|
||||
<(src_loc)/history/feed/history_feed_section.cpp
|
||||
<(src_loc)/history/feed/history_feed_section.h
|
||||
<(src_loc)/history/view/history_view_list_widget.cpp
|
||||
<(src_loc)/history/view/history_view_list_widget.h
|
||||
<(src_loc)/history/view/history_view_service_message.cpp
|
||||
<(src_loc)/history/view/history_view_service_message.h
|
||||
<(src_loc)/history/view/history_view_top_bar_widget.cpp
|
||||
<(src_loc)/history/view/history_view_top_bar_widget.h
|
||||
<(src_loc)/history/history.cpp
|
||||
<(src_loc)/history/history.h
|
||||
<(src_loc)/history/history_admin_log_filter.cpp
|
||||
<(src_loc)/history/history_admin_log_filter.h
|
||||
<(src_loc)/history/history_admin_log_inner.cpp
|
||||
<(src_loc)/history/history_admin_log_inner.h
|
||||
<(src_loc)/history/history_admin_log_item.cpp
|
||||
<(src_loc)/history/history_admin_log_item.h
|
||||
<(src_loc)/history/history_admin_log_section.cpp
|
||||
<(src_loc)/history/history_admin_log_section.h
|
||||
<(src_loc)/history/history_drag_area.cpp
|
||||
<(src_loc)/history/history_drag_area.h
|
||||
<(src_loc)/history/history_item.cpp
|
||||
@ -241,10 +253,6 @@
|
||||
<(src_loc)/history/history_message.h
|
||||
<(src_loc)/history/history_service.cpp
|
||||
<(src_loc)/history/history_service.h
|
||||
<(src_loc)/history/history_service_layout.cpp
|
||||
<(src_loc)/history/history_service_layout.h
|
||||
<(src_loc)/history/history_top_bar_widget.cpp
|
||||
<(src_loc)/history/history_top_bar_widget.h
|
||||
<(src_loc)/history/history_widget.cpp
|
||||
<(src_loc)/history/history_widget.h
|
||||
<(src_loc)/info/info_content_widget.cpp
|
||||
@ -531,6 +539,8 @@
|
||||
<(src_loc)/storage/serialize_document.h
|
||||
<(src_loc)/storage/storage_facade.cpp
|
||||
<(src_loc)/storage/storage_facade.h
|
||||
<(src_loc)/storage/storage_feed_messages.cpp
|
||||
<(src_loc)/storage/storage_feed_messages.h
|
||||
<(src_loc)/storage/storage_media_prepare.cpp
|
||||
<(src_loc)/storage/storage_media_prepare.h
|
||||
<(src_loc)/storage/storage_shared_media.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user