mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-08 16:13:56 +02:00
First version of feed section view.
This commit is contained in:
parent
50b120bc22
commit
794e31505b
55 changed files with 3608 additions and 331 deletions
|
@ -1415,6 +1415,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_admin_log_admin_pin_messages" = "Pin messages";
|
"lng_admin_log_admin_pin_messages" = "Pin messages";
|
||||||
"lng_admin_log_admin_add_admins" = "Add new admins";
|
"lng_admin_log_admin_add_admins" = "Add new admins";
|
||||||
|
|
||||||
|
"lng_feed_show_next" = "Show Next";
|
||||||
|
|
||||||
// Wnd specific
|
// Wnd specific
|
||||||
|
|
||||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
"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_shared_media.h"
|
||||||
#include "storage/storage_user_photos.h"
|
#include "storage/storage_user_photos.h"
|
||||||
#include "storage/storage_media_prepare.h"
|
#include "storage/storage_media_prepare.h"
|
||||||
|
#include "storage/storage_feed_messages.h"
|
||||||
#include "data/data_sparse_ids.h"
|
#include "data/data_sparse_ids.h"
|
||||||
#include "data/data_search_controller.h"
|
#include "data/data_search_controller.h"
|
||||||
#include "data/data_channel_admins.h"
|
#include "data/data_channel_admins.h"
|
||||||
|
@ -49,6 +50,7 @@ constexpr auto kUnreadMentionsPreloadIfLess = 5;
|
||||||
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
|
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
|
||||||
constexpr auto kUnreadMentionsNextRequestLimit = 100;
|
constexpr auto kUnreadMentionsNextRequestLimit = 100;
|
||||||
constexpr auto kSharedMediaLimit = 100;
|
constexpr auto kSharedMediaLimit = 100;
|
||||||
|
constexpr auto kFeedMessagesLimit = 50;
|
||||||
constexpr auto kReadFeaturedSetsTimeout = TimeMs(1000);
|
constexpr auto kReadFeaturedSetsTimeout = TimeMs(1000);
|
||||||
constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
|
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) {
|
void ApiWrap::sendAction(const SendOptions &options) {
|
||||||
readServerHistory(options.history);
|
readServerHistory(options.history);
|
||||||
options.history->getReadyFor(ShowAtTheEndMsgId);
|
options.history->getReadyFor(ShowAtTheEndMsgId);
|
||||||
|
|
|
@ -9,15 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include <rpl/event_stream.h>
|
#include <rpl/event_stream.h>
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
#include "core/single_timer.h"
|
|
||||||
#include "mtproto/sender.h"
|
|
||||||
#include "base/flat_map.h"
|
#include "base/flat_map.h"
|
||||||
#include "base/flat_set.h"
|
#include "base/flat_set.h"
|
||||||
|
#include "core/single_timer.h"
|
||||||
|
#include "mtproto/sender.h"
|
||||||
#include "chat_helpers/stickers.h"
|
#include "chat_helpers/stickers.h"
|
||||||
|
#include "data/data_messages.h"
|
||||||
|
|
||||||
class TaskQueue;
|
class TaskQueue;
|
||||||
class AuthSession;
|
class AuthSession;
|
||||||
enum class SparseIdsLoadDirection;
|
|
||||||
struct MessageGroupId;
|
struct MessageGroupId;
|
||||||
struct SendingAlbum;
|
struct SendingAlbum;
|
||||||
enum class SendMediaType;
|
enum class SendMediaType;
|
||||||
|
@ -129,7 +129,7 @@ public:
|
||||||
bool adminsEnabled,
|
bool adminsEnabled,
|
||||||
base::flat_set<not_null<UserData*>> &&admins);
|
base::flat_set<not_null<UserData*>> &&admins);
|
||||||
|
|
||||||
using SliceType = SparseIdsLoadDirection;
|
using SliceType = Data::LoadDirection;
|
||||||
void requestSharedMedia(
|
void requestSharedMedia(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Storage::SharedMediaType type,
|
Storage::SharedMediaType type,
|
||||||
|
@ -143,6 +143,11 @@ public:
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
PhotoId afterId);
|
PhotoId afterId);
|
||||||
|
|
||||||
|
void requestFeedMessages(
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
Data::MessagePosition messageId,
|
||||||
|
SliceType slice);
|
||||||
|
|
||||||
void stickerSetInstalled(uint64 setId) {
|
void stickerSetInstalled(uint64 setId) {
|
||||||
_stickerSetInstalled.fire_copy(setId);
|
_stickerSetInstalled.fire_copy(setId);
|
||||||
}
|
}
|
||||||
|
@ -300,6 +305,12 @@ private:
|
||||||
PhotoId photoId,
|
PhotoId photoId,
|
||||||
const MTPphotos_Photos &result);
|
const MTPphotos_Photos &result);
|
||||||
|
|
||||||
|
void feedMessagesDone(
|
||||||
|
not_null<Data::Feed*> feed,
|
||||||
|
Data::MessagePosition messageId,
|
||||||
|
SliceType slice,
|
||||||
|
const MTPmessages_FeedMessages &result);
|
||||||
|
|
||||||
void sendSharedContact(
|
void sendSharedContact(
|
||||||
const QString &phone,
|
const QString &phone,
|
||||||
const QString &firstName,
|
const QString &firstName,
|
||||||
|
@ -417,6 +428,11 @@ private:
|
||||||
|
|
||||||
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
|
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;
|
rpl::event_stream<SendOptions> _sendActions;
|
||||||
|
|
||||||
struct ReadRequest {
|
struct ReadRequest {
|
||||||
|
|
|
@ -19,10 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "data/data_abstract_structure.h"
|
#include "data/data_abstract_structure.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "history/history_service_layout.h"
|
|
||||||
#include "history/history_location_manager.h"
|
#include "history/history_location_manager.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "media/media_audio.h"
|
#include "media/media_audio.h"
|
||||||
#include "inline_bots/inline_bot_layout_item.h"
|
#include "inline_bots/inline_bot_layout_item.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
@ -2141,7 +2141,7 @@ namespace {
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
App::main()->updateScrollColors();
|
App::main()->updateScrollColors();
|
||||||
}
|
}
|
||||||
HistoryLayout::serviceColorsUpdated();
|
HistoryView::serviceColorsUpdated();
|
||||||
} else if (update.type == Update::Type::New) {
|
} else if (update.type == Update::Type::New) {
|
||||||
prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg);
|
prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg);
|
||||||
prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected);
|
prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected);
|
||||||
|
@ -2149,7 +2149,7 @@ namespace {
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
App::main()->updateScrollColors();
|
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 "boxes/peers/edit_peer_info_box.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/widgets/labels.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 "window/window_controller.h"
|
||||||
#include "profile/profile_channel_controllers.h"
|
#include "profile/profile_channel_controllers.h"
|
||||||
#include "info/profile/info_profile_button.h"
|
#include "info/profile/info_profile_button.h"
|
||||||
|
|
|
@ -11,16 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
FeedPosition::FeedPosition(const MTPFeedPosition &position)
|
MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position) {
|
||||||
: date(position.c_feedPosition().vdate.v)
|
Expects(position.type() == mtpc_feedPosition);
|
||||||
, msgId(
|
|
||||||
peerToChannel(peerFromMTP(position.c_feedPosition().vpeer)),
|
|
||||||
position.c_feedPosition().vid.v) {
|
|
||||||
}
|
|
||||||
|
|
||||||
FeedPosition::FeedPosition(not_null<HistoryItem*> item)
|
const auto &data = position.c_feedPosition();
|
||||||
: date(toServerTime(item->date.toTime_t()).v)
|
return MessagePosition(data.vdate.v, FullMsgId(
|
||||||
, msgId(item->fullId()) {
|
peerToChannel(peerFromMTP(data.vpeer)),
|
||||||
|
data.vid.v));
|
||||||
}
|
}
|
||||||
|
|
||||||
Feed::Feed(FeedId id)
|
Feed::Feed(FeedId id)
|
||||||
|
@ -82,7 +79,7 @@ void Feed::paintUserpic(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
|
bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
|
||||||
if (_lastMessage && FeedPosition(item) <= FeedPosition(_lastMessage)) {
|
if (_lastMessage && item->position() <= _lastMessage->position()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_lastMessage = item;
|
_lastMessage = item;
|
||||||
|
@ -118,8 +115,4 @@ void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
|
||||||
_unreadMutedCount = unreadMutedCount;
|
_unreadMutedCount = unreadMutedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Feed::setUnreadPosition(const FeedPosition &position) {
|
|
||||||
_unreadPosition = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -8,51 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "dialogs/dialogs_entry.h"
|
#include "dialogs/dialogs_entry.h"
|
||||||
|
#include "data/data_messages.h"
|
||||||
|
|
||||||
class ChannelData;
|
class ChannelData;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
struct FeedPosition {
|
MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position);
|
||||||
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;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class Feed : public Dialogs::Entry {
|
class Feed : public Dialogs::Entry {
|
||||||
public:
|
public:
|
||||||
|
@ -69,7 +31,15 @@ public:
|
||||||
void historyCleared(not_null<History*> history);
|
void historyCleared(not_null<History*> history);
|
||||||
|
|
||||||
void setUnreadCounts(int unreadCount, int unreadMutedCount);
|
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 {
|
bool toImportant() const override {
|
||||||
return false; // TODO feeds workmode
|
return false; // TODO feeds workmode
|
||||||
|
@ -99,7 +69,7 @@ private:
|
||||||
|
|
||||||
HistoryItem *_lastMessage = nullptr;
|
HistoryItem *_lastMessage = nullptr;
|
||||||
|
|
||||||
FeedPosition _unreadPosition;
|
rpl::variable<MessagePosition> _unreadPosition;
|
||||||
int _unreadCount = 0;
|
int _unreadCount = 0;
|
||||||
int _unreadMutedCount = 0;
|
int _unreadMutedCount = 0;
|
||||||
bool _complete = false;
|
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 "auth_session.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_messages.h"
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -23,7 +24,7 @@ MTPmessages_Search PrepareSearchRequest(
|
||||||
Storage::SharedMediaType type,
|
Storage::SharedMediaType type,
|
||||||
const QString &query,
|
const QString &query,
|
||||||
MsgId messageId,
|
MsgId messageId,
|
||||||
SparseIdsLoadDirection direction) {
|
Data::LoadDirection direction) {
|
||||||
const auto filter = [&] {
|
const auto filter = [&] {
|
||||||
using Type = Storage::SharedMediaType;
|
using Type = Storage::SharedMediaType;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -58,17 +59,17 @@ MTPmessages_Search PrepareSearchRequest(
|
||||||
const auto limit = messageId ? kSharedMediaLimit : 0;
|
const auto limit = messageId ? kSharedMediaLimit : 0;
|
||||||
const auto offsetId = [&] {
|
const auto offsetId = [&] {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case SparseIdsLoadDirection::Before:
|
case Data::LoadDirection::Before:
|
||||||
case SparseIdsLoadDirection::Around: return messageId;
|
case Data::LoadDirection::Around: return messageId;
|
||||||
case SparseIdsLoadDirection::After: return messageId + 1;
|
case Data::LoadDirection::After: return messageId + 1;
|
||||||
}
|
}
|
||||||
Unexpected("Direction in PrepareSearchRequest");
|
Unexpected("Direction in PrepareSearchRequest");
|
||||||
}();
|
}();
|
||||||
const auto addOffset = [&] {
|
const auto addOffset = [&] {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case SparseIdsLoadDirection::Before: return 0;
|
case Data::LoadDirection::Before: return 0;
|
||||||
case SparseIdsLoadDirection::Around: return -limit / 2;
|
case Data::LoadDirection::Around: return -limit / 2;
|
||||||
case SparseIdsLoadDirection::After: return -limit;
|
case Data::LoadDirection::After: return -limit;
|
||||||
}
|
}
|
||||||
Unexpected("Direction in PrepareSearchRequest");
|
Unexpected("Direction in PrepareSearchRequest");
|
||||||
}();
|
}();
|
||||||
|
@ -92,7 +93,7 @@ SearchResult ParseSearchResult(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Storage::SharedMediaType type,
|
Storage::SharedMediaType type,
|
||||||
MsgId messageId,
|
MsgId messageId,
|
||||||
SparseIdsLoadDirection direction,
|
Data::LoadDirection direction,
|
||||||
const MTPmessages_Messages &data) {
|
const MTPmessages_Messages &data) {
|
||||||
auto result = SearchResult();
|
auto result = SearchResult();
|
||||||
result.noSkipRange = MsgRange{ messageId, messageId };
|
result.noSkipRange = MsgRange{ messageId, messageId };
|
||||||
|
@ -158,11 +159,11 @@ SearchResult ParseSearchResult(
|
||||||
if (messageId && result.messageIds.empty()) {
|
if (messageId && result.messageIds.empty()) {
|
||||||
result.noSkipRange = [&]() -> MsgRange {
|
result.noSkipRange = [&]() -> MsgRange {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case SparseIdsLoadDirection::Before: // All old loaded.
|
case Data::LoadDirection::Before: // All old loaded.
|
||||||
return { 0, result.noSkipRange.till };
|
return { 0, result.noSkipRange.till };
|
||||||
case SparseIdsLoadDirection::Around: // All loaded.
|
case Data::LoadDirection::Around: // All loaded.
|
||||||
return { 0, ServerMaxMsgId };
|
return { 0, ServerMaxMsgId };
|
||||||
case SparseIdsLoadDirection::After: // All new loaded.
|
case Data::LoadDirection::After: // All new loaded.
|
||||||
return { result.noSkipRange.from, ServerMaxMsgId };
|
return { result.noSkipRange.from, ServerMaxMsgId };
|
||||||
}
|
}
|
||||||
Unexpected("Direction in ParseSearchResult");
|
Unexpected("Direction in ParseSearchResult");
|
||||||
|
|
|
@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
#include "base/value_ordering.h"
|
#include "base/value_ordering.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
enum class LoadDirection : char;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
|
|
||||||
struct SearchResult {
|
struct SearchResult {
|
||||||
|
@ -26,13 +30,13 @@ MTPmessages_Search PrepareSearchRequest(
|
||||||
Storage::SharedMediaType type,
|
Storage::SharedMediaType type,
|
||||||
const QString &query,
|
const QString &query,
|
||||||
MsgId messageId,
|
MsgId messageId,
|
||||||
SparseIdsLoadDirection direction);
|
Data::LoadDirection direction);
|
||||||
|
|
||||||
SearchResult ParseSearchResult(
|
SearchResult ParseSearchResult(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Storage::SharedMediaType type,
|
Storage::SharedMediaType type,
|
||||||
MsgId messageId,
|
MsgId messageId,
|
||||||
SparseIdsLoadDirection direction,
|
Data::LoadDirection direction,
|
||||||
const MTPmessages_Messages &data);
|
const MTPmessages_Messages &data);
|
||||||
|
|
||||||
class SearchController : private MTP::Sender {
|
class SearchController : private MTP::Sender {
|
||||||
|
|
|
@ -361,17 +361,17 @@ void SparseIdsSliceBuilder::requestMessages(
|
||||||
RequestDirection direction) {
|
RequestDirection direction) {
|
||||||
auto requestAroundData = [&]() -> AroundData {
|
auto requestAroundData = [&]() -> AroundData {
|
||||||
if (_ids.empty()) {
|
if (_ids.empty()) {
|
||||||
return { _key, SparseIdsLoadDirection::Around };
|
return { _key, Data::LoadDirection::Around };
|
||||||
} else if (direction == RequestDirection::Before) {
|
} 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());
|
_insufficientAround.fire(requestAroundData());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SparseIdsSliceBuilder::requestMessagesCount() {
|
void SparseIdsSliceBuilder::requestMessagesCount() {
|
||||||
_insufficientAround.fire({ 0, SparseIdsLoadDirection::Around });
|
_insufficientAround.fire({ 0, Data::LoadDirection::Around });
|
||||||
}
|
}
|
||||||
|
|
||||||
SparseIdsSlice SparseIdsSliceBuilder::snapshot() const {
|
SparseIdsSlice SparseIdsSliceBuilder::snapshot() const {
|
||||||
|
|
|
@ -7,17 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "data/data_messages.h"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
struct SparseIdsListResult;
|
struct SparseIdsListResult;
|
||||||
struct SparseIdsSliceUpdate;
|
struct SparseIdsSliceUpdate;
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
||||||
enum class SparseIdsLoadDirection {
|
|
||||||
Around,
|
|
||||||
Before,
|
|
||||||
After,
|
|
||||||
};
|
|
||||||
|
|
||||||
class SparseIdsSlice {
|
class SparseIdsSlice {
|
||||||
public:
|
public:
|
||||||
using Key = MsgId;
|
using Key = MsgId;
|
||||||
|
@ -178,8 +174,7 @@ public:
|
||||||
void checkInsufficient();
|
void checkInsufficient();
|
||||||
struct AroundData {
|
struct AroundData {
|
||||||
MsgId aroundId = 0;
|
MsgId aroundId = 0;
|
||||||
SparseIdsLoadDirection direction
|
Data::LoadDirection direction = Data::LoadDirection::Around;
|
||||||
= SparseIdsLoadDirection::Around;
|
|
||||||
|
|
||||||
inline bool operator<(const AroundData &other) const {
|
inline bool operator<(const AroundData &other) const {
|
||||||
return (aroundId < other.aroundId)
|
return (aroundId < other.aroundId)
|
||||||
|
|
|
@ -133,29 +133,43 @@ inline bool operator!=(const MsgRange &a, const MsgRange &b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FullMsgId {
|
struct FullMsgId {
|
||||||
FullMsgId() = default;
|
constexpr FullMsgId() = default;
|
||||||
FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) {
|
constexpr FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
return msg != 0;
|
return msg != 0;
|
||||||
}
|
}
|
||||||
ChannelId channel = NoChannel;
|
|
||||||
MsgId msg = 0;
|
|
||||||
};
|
inline constexpr bool operator<(const FullMsgId &other) const {
|
||||||
inline bool operator==(const FullMsgId &a, const FullMsgId &b) {
|
if (channel < other.channel) {
|
||||||
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;
|
return true;
|
||||||
} else if (a.channel > b.channel) {
|
} else if (channel > other.channel) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return a.msg < b.msg;
|
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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
using MessageIdsList = std::vector<FullMsgId>;
|
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_indexed_list.h"
|
||||||
#include "dialogs/dialogs_layout.h"
|
#include "dialogs/dialogs_layout.h"
|
||||||
#include "dialogs/dialogs_search_from_controllers.h"
|
#include "dialogs/dialogs_search_from_controllers.h"
|
||||||
|
#include "history/feed/history_feed_section.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
|
@ -1739,8 +1740,8 @@ void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) {
|
||||||
addSavedPeersAfter(feed->chatsListDate());
|
addSavedPeersAfter(feed->chatsListDate());
|
||||||
}
|
}
|
||||||
if (dialog.has_read_max_position()) {
|
if (dialog.has_read_max_position()) {
|
||||||
const auto position = Data::FeedPosition(dialog.vread_max_position);
|
feed->setUnreadPosition(
|
||||||
feed->setUnreadPosition(position);
|
Data::FeedPositionFromMTP(dialog.vread_max_position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2307,8 +2308,7 @@ bool DialogsInner::chooseRow() {
|
||||||
if (const auto history = chosen.key.history()) {
|
if (const auto history = chosen.key.history()) {
|
||||||
App::main()->choosePeer(history->peer->id, chosen.messageId);
|
App::main()->choosePeer(history->peer->id, chosen.messageId);
|
||||||
} else if (const auto feed = chosen.key.feed()) {
|
} else if (const auto feed = chosen.key.feed()) {
|
||||||
// #TODO feeds open
|
_controller->showSection(HistoryFeed::Memento(feed));
|
||||||
// _controller->showSection(HistoryFeed::Memento(feed));
|
|
||||||
}
|
}
|
||||||
if (openSearchResult) {
|
if (openSearchResult) {
|
||||||
emit searchResultChosen();
|
emit searchResultChosen();
|
||||||
|
|
|
@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
|
||||||
For license and copyright information please follow this link:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
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 "styles/style_boxes.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
|
@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
#include "history/history_admin_log_section.h"
|
#include "history/admin_log/history_admin_log_section.h"
|
||||||
|
|
||||||
namespace AdminLog {
|
namespace AdminLog {
|
||||||
|
|
|
@ -5,15 +5,15 @@ the official desktop application for the Telegram messaging service.
|
||||||
For license and copyright information please follow this link:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
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 "styles/style_history.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_message.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/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 "chat_helpers/message_field.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
@ -691,7 +691,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
if (auto date = item->Get<HistoryMessageDate>()) {
|
if (auto date = item->Get<HistoryMessageDate>()) {
|
||||||
date->paint(p, dateY, width);
|
date->paint(p, dateY, width);
|
||||||
} else {
|
} 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 innerWidth = rectWidth - st::historyAdminLogEmptyPadding.left() - st::historyAdminLogEmptyPadding.right();
|
||||||
auto rectHeight = st::historyAdminLogEmptyPadding.top() + _emptyText.countHeight(innerWidth) + st::historyAdminLogEmptyPadding.bottom();
|
auto rectHeight = st::historyAdminLogEmptyPadding.top() + _emptyText.countHeight(innerWidth) + st::historyAdminLogEmptyPadding.bottom();
|
||||||
auto rect = QRect((width() - rectWidth) / 2, (height() - rectHeight) / 3, rectWidth, rectHeight);
|
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);
|
p.setPen(st::msgServiceFg);
|
||||||
_emptyText.draw(p, rect.x() + st::historyAdminLogEmptyPadding.left(), rect.y() + st::historyAdminLogEmptyPadding.top(), innerWidth, style::al_top);
|
_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
|
#pragma once
|
||||||
|
|
||||||
#include "history/history_admin_log_item.h"
|
#include "history/admin_log/history_admin_log_item.h"
|
||||||
#include "history/history_admin_log_section.h"
|
#include "history/admin_log/history_admin_log_section.h"
|
||||||
#include "ui/widgets/tooltip.h"
|
#include "ui/widgets/tooltip.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "mtproto/sender.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:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
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_service.h"
|
||||||
#include "history/history_message.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 "lang/lang_keys.h"
|
||||||
#include "boxes/sticker_set_box.h"
|
#include "boxes/sticker_set_box.h"
|
||||||
#include "core/tl_help.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:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
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/admin_log/history_admin_log_inner.h"
|
||||||
#include "history/history_admin_log_filter.h"
|
#include "history/admin_log/history_admin_log_filter.h"
|
||||||
#include "profile/profile_back_button.h"
|
#include "profile/profile_back_button.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
|
@ -379,45 +379,10 @@ void Widget::paintEvent(QPaintEvent *e) {
|
||||||
// updateListSize();
|
// updateListSize();
|
||||||
//}
|
//}
|
||||||
|
|
||||||
Painter p(this);
|
//auto ms = getms();
|
||||||
auto clip = e->rect();
|
|
||||||
auto ms = getms();
|
|
||||||
//_historyDownShown.step(ms);
|
//_historyDownShown.step(ms);
|
||||||
|
|
||||||
auto fill = QRect(0, 0, width(), App::main()->height());
|
SectionWidget::PaintBackground(this, e);
|
||||||
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 Widget::onScroll() {
|
void Widget::onScroll() {
|
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "window/section_widget.h"
|
#include "window/section_widget.h"
|
||||||
#include "window/section_memento.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"
|
#include "mtproto/sender.h"
|
||||||
|
|
||||||
namespace Notify {
|
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 "styles/style_history.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_service_layout.h"
|
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "ui/text_options.h"
|
#include "ui/text_options.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
@ -475,7 +475,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
p.restoreTextPalette();
|
p.restoreTextPalette();
|
||||||
}
|
}
|
||||||
} else if (noHistoryDisplayed) {
|
} else if (noHistoryDisplayed) {
|
||||||
HistoryLayout::paintEmpty(p, width(), height());
|
HistoryView::paintEmpty(p, width(), height());
|
||||||
}
|
}
|
||||||
if (!noHistoryDisplayed) {
|
if (!noHistoryDisplayed) {
|
||||||
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
|
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
|
||||||
|
@ -637,7 +637,11 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
if (auto date = item->Get<HistoryMessageDate>()) {
|
if (auto date = item->Get<HistoryMessageDate>()) {
|
||||||
date->paint(p, dateY, width);
|
date->paint(p, dateY, width);
|
||||||
} else {
|
} 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);
|
return (selectedState.count > 0) && (selectedState.count == selectedState.canDeleteCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryTopBarWidget::SelectedState HistoryInner::getSelectionState() const {
|
auto HistoryInner::getSelectionState() const
|
||||||
auto result = HistoryTopBarWidget::SelectedState {};
|
-> HistoryView::TopBarWidget::SelectedState {
|
||||||
|
auto result = HistoryView::TopBarWidget::SelectedState {};
|
||||||
for (auto &selected : _selected) {
|
for (auto &selected : _selected) {
|
||||||
if (selected.second == FullSelection) {
|
if (selected.second == FullSelection) {
|
||||||
++result.count;
|
++result.count;
|
||||||
|
|
|
@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "ui/widgets/tooltip.h"
|
#include "ui/widgets/tooltip.h"
|
||||||
#include "ui/widgets/scroll_area.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 {
|
namespace Window {
|
||||||
class Controller;
|
class Controller;
|
||||||
|
@ -50,7 +50,7 @@ public:
|
||||||
bool canCopySelected() const;
|
bool canCopySelected() const;
|
||||||
bool canDeleteSelected() const;
|
bool canDeleteSelected() const;
|
||||||
|
|
||||||
HistoryTopBarWidget::SelectedState getSelectionState() const;
|
HistoryView::TopBarWidget::SelectedState getSelectionState() const;
|
||||||
void clearSelectedItems(bool onlyTextSelection = false);
|
void clearSelectedItems(bool onlyTextSelection = false);
|
||||||
MessageIdsList getSelectedItems() const;
|
MessageIdsList getSelectedItems() const;
|
||||||
void selectItem(not_null<HistoryItem*> item);
|
void selectItem(not_null<HistoryItem*> item);
|
||||||
|
|
|
@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "history/history_service_layout.h"
|
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_media_grouped.h"
|
#include "history/history_media_grouped.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "media/media_clip_reader.h"
|
#include "media/media_clip_reader.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "core/crash_reports.h"
|
#include "core/crash_reports.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_messages.h"
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -635,6 +636,10 @@ QString HistoryItem::directLink() const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::MessagePosition HistoryItem::position() const {
|
||||||
|
return Data::MessagePosition(toServerTime(date.toTime_t()).v, fullId());
|
||||||
|
}
|
||||||
|
|
||||||
MsgId HistoryItem::replyToId() const {
|
MsgId HistoryItem::replyToId() const {
|
||||||
if (auto reply = Get<HistoryMessageReply>()) {
|
if (auto reply = Get<HistoryMessageReply>()) {
|
||||||
return reply->replyToId();
|
return reply->replyToId();
|
||||||
|
|
|
@ -37,6 +37,10 @@ struct BotKeyboardButton;
|
||||||
struct RippleAnimation;
|
struct RippleAnimation;
|
||||||
} // namespace style
|
} // namespace style
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct MessagePosition;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
class HistoryElement {
|
class HistoryElement {
|
||||||
public:
|
public:
|
||||||
HistoryElement() = default;
|
HistoryElement() = default;
|
||||||
|
@ -434,6 +438,7 @@ public:
|
||||||
FullMsgId fullId() const {
|
FullMsgId fullId() const {
|
||||||
return FullMsgId(channelId(), id);
|
return FullMsgId(channelId(), id);
|
||||||
}
|
}
|
||||||
|
Data::MessagePosition position() const;
|
||||||
|
|
||||||
HistoryMedia *getMedia() const {
|
HistoryMedia *getMedia() const {
|
||||||
return _media.get();
|
return _media.get();
|
||||||
|
|
|
@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/text_options.h"
|
#include "ui/text_options.h"
|
||||||
#include "history/history_service_layout.h"
|
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_media.h"
|
#include "history/history_media.h"
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "media/media_audio.h"
|
#include "media/media_audio.h"
|
||||||
#include "media/player/media_player_instance.h"
|
#include "media/player/media_player_instance.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
|
@ -825,7 +825,7 @@ int HistoryMessageDate::height() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessageDate::paint(Painter &p, int y, int w) 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;
|
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default;
|
||||||
|
|
|
@ -13,9 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "history/history_location_manager.h"
|
#include "history/history_location_manager.h"
|
||||||
#include "history/history_service_layout.h"
|
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_service.h"
|
#include "history/history_service.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
|
@ -1797,7 +1797,7 @@ void HistoryMessage::draw(Painter &p, QRect clip, TextSelection selection, TimeM
|
||||||
|| (_media && _media->skipBubbleTail())
|
|| (_media && _media->skipBubbleTail())
|
||||||
|| (keyboard != nullptr);
|
|| (keyboard != nullptr);
|
||||||
auto displayTail = skipTail ? RectPart::None : (outbg && !Adaptive::ChatWide()) ? RectPart::Right : RectPart::Left;
|
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.
|
// Entry page is always a bubble bottom.
|
||||||
auto mediaOnBottom = (mediaDisplayed && _media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/);
|
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 "lang/lang_keys.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "history/history_service_layout.h"
|
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "data/data_feed.h"
|
#include "data/data_feed.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
|
@ -512,8 +512,8 @@ void HistoryService::draw(Painter &p, QRect clip, TextSelection selection, TimeM
|
||||||
height -= unreadbarh;
|
height -= unreadbarh;
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryLayout::PaintContext context(ms, clip, selection);
|
HistoryView::PaintContext context(ms, clip, selection);
|
||||||
HistoryLayout::ServiceMessagePainter::paint(p, this, context, height);
|
HistoryView::ServiceMessagePainter::paint(p, this, context, height);
|
||||||
|
|
||||||
if (auto skiph = dateh + unreadbarh) {
|
if (auto skiph = dateh + unreadbarh) {
|
||||||
p.translate(0, -skiph);
|
p.translate(0, -skiph);
|
||||||
|
|
|
@ -34,9 +34,9 @@ struct HistoryServiceSelfDestruct : public RuntimeComponent<HistoryServiceSelfDe
|
||||||
TimeMs destructAt = 0;
|
TimeMs destructAt = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace HistoryLayout {
|
namespace HistoryView {
|
||||||
class ServiceMessagePainter;
|
class ServiceMessagePainter;
|
||||||
} // namespace HistoryLayout
|
} // namespace HistoryView
|
||||||
|
|
||||||
class HistoryService : public HistoryItem, private HistoryItemInstantiated<HistoryService> {
|
class HistoryService : public HistoryItem, private HistoryItemInstantiated<HistoryService> {
|
||||||
public:
|
public:
|
||||||
|
@ -102,7 +102,7 @@ public:
|
||||||
~HistoryService();
|
~HistoryService();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class HistoryLayout::ServiceMessagePainter;
|
friend class HistoryView::ServiceMessagePainter;
|
||||||
|
|
||||||
HistoryService(not_null<History*> history, const MTPDmessage &message);
|
HistoryService(not_null<History*> history, const MTPDmessage &message);
|
||||||
HistoryService(not_null<History*> history, const MTPDmessageService &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_drafts.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_service_layout.h"
|
|
||||||
#include "history/history_media_types.h"
|
#include "history/history_media_types.h"
|
||||||
#include "history/history_drag_area.h"
|
#include "history/history_drag_area.h"
|
||||||
#include "history/history_inner_widget.h"
|
#include "history/history_inner_widget.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "profile/profile_block_group_members.h"
|
#include "profile/profile_block_group_members.h"
|
||||||
#include "info/info_memento.h"
|
#include "info/info_memento.h"
|
||||||
#include "core/click_handler_types.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/media_audio_capture.h"
|
||||||
#include "media/player/media_player_instance.h"
|
#include "media/player/media_player_instance.h"
|
||||||
#include "apiwrap.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 "observer_peer.h"
|
||||||
#include "base/qthelp_regex.h"
|
#include "base/qthelp_regex.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
@ -1704,7 +1704,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||||
|
|
||||||
noSelectingScroll();
|
noSelectingScroll();
|
||||||
_nonEmptySelection = false;
|
_nonEmptySelection = false;
|
||||||
_topBar->showSelected(HistoryTopBarWidget::SelectedState {});
|
_topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
|
||||||
|
|
||||||
App::hoveredItem(nullptr);
|
App::hoveredItem(nullptr);
|
||||||
App::pressedItem(nullptr);
|
App::pressedItem(nullptr);
|
||||||
|
@ -3039,7 +3039,7 @@ void HistoryWidget::showAnimated(
|
||||||
_a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition());
|
_a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition());
|
||||||
if (_history) {
|
if (_history) {
|
||||||
_topBar->show();
|
_topBar->show();
|
||||||
_topBar->setAnimationMode(true);
|
_topBar->setAnimatingMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
activate();
|
activate();
|
||||||
|
@ -3056,7 +3056,7 @@ void HistoryWidget::animationCallback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::doneShow() {
|
void HistoryWidget::doneShow() {
|
||||||
_topBar->setAnimationMode(false);
|
_topBar->setAnimatingMode(false);
|
||||||
updateReportSpamStatus();
|
updateReportSpamStatus();
|
||||||
updateBotKeyboard();
|
updateBotKeyboard();
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
|
@ -6142,7 +6142,7 @@ MessageIdsList HistoryWidget::getSelectedItems() const {
|
||||||
|
|
||||||
void HistoryWidget::updateTopBarSelection() {
|
void HistoryWidget::updateTopBarSelection() {
|
||||||
if (!_list) {
|
if (!_list) {
|
||||||
_topBar->showSelected(HistoryTopBarWidget::SelectedState {});
|
_topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6489,25 +6489,13 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::paintEvent(QPaintEvent *e) {
|
bool HistoryWidget::paintShowAnimationFrame(TimeMs ms) {
|
||||||
if (!App::main() || (App::wnd() && App::wnd()->contentOverlapped(this, e))) {
|
auto progress = _a_show.current(ms, 1.);
|
||||||
return;
|
if (!_a_show.animating()) {
|
||||||
}
|
return false;
|
||||||
if (hasPendingResizedItems()) {
|
|
||||||
updateListSize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Painter p(this);
|
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 animationWidth = width();
|
||||||
auto retina = cIntRetinaFactor();
|
auto retina = cIntRetinaFactor();
|
||||||
auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft);
|
auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft);
|
||||||
|
@ -6523,47 +6511,30 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||||
p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, 0, _cacheOver.width(), height() * retina));
|
p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, 0, _cacheOver.width(), height() * retina));
|
||||||
p.setOpacity(shadow);
|
p.setOpacity(shadow);
|
||||||
st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
|
st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
auto ms = getms();
|
||||||
|
_historyDownShown.step(ms);
|
||||||
|
_unreadMentionsShown.step(ms);
|
||||||
|
if (paintShowAnimationFrame(ms)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (Ui::skipPaintEvent(this, e)) {
|
||||||
QRect fill(0, 0, width(), App::main()->height());
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
if (hasPendingResizedItems()) {
|
||||||
} else {
|
updateListSize();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Window::SectionWidget::PaintBackground(this, e);
|
||||||
|
|
||||||
|
Painter p(this);
|
||||||
|
const auto clip = e->rect();
|
||||||
if (_list) {
|
if (_list) {
|
||||||
if (!_field->isHidden() || _recording) {
|
if (!_field->isHidden() || _recording) {
|
||||||
drawField(p, r);
|
drawField(p, clip);
|
||||||
if (!_send->isHidden() && _recording) {
|
if (!_send->isHidden() && _recording) {
|
||||||
drawRecording(p, _send->recordActiveRatio());
|
drawRecording(p, _send->recordActiveRatio());
|
||||||
}
|
}
|
||||||
|
@ -6575,13 +6546,13 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
if (_scroll->isHidden()) {
|
if (_scroll->isHidden()) {
|
||||||
p.setClipRect(_scroll->geometry());
|
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 {
|
} else {
|
||||||
style::font font(st::msgServiceFont);
|
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;
|
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);
|
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.setPen(st::msgServiceFg);
|
||||||
p.setFont(font->f);
|
p.setFont(font->f);
|
||||||
|
|
|
@ -65,12 +65,15 @@ enum class MimeDataState;
|
||||||
struct PreparedList;
|
struct PreparedList;
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
class TopBarWidget;
|
||||||
|
} // namespace HistoryView
|
||||||
|
|
||||||
class DragArea;
|
class DragArea;
|
||||||
class SendFilesBox;
|
class SendFilesBox;
|
||||||
class BotKeyboard;
|
class BotKeyboard;
|
||||||
class MessageField;
|
class MessageField;
|
||||||
class HistoryInner;
|
class HistoryInner;
|
||||||
class HistoryTopBarWidget;
|
|
||||||
struct HistoryMessageMarkupButton;
|
struct HistoryMessageMarkupButton;
|
||||||
|
|
||||||
class ReportSpamPanel : public TWidget {
|
class ReportSpamPanel : public TWidget {
|
||||||
|
@ -592,6 +595,7 @@ private:
|
||||||
void drawRecording(Painter &p, float64 recordActive);
|
void drawRecording(Painter &p, float64 recordActive);
|
||||||
void drawPinnedBar(Painter &p);
|
void drawPinnedBar(Painter &p);
|
||||||
void drawRestrictedWrite(Painter &p);
|
void drawRestrictedWrite(Painter &p);
|
||||||
|
bool paintShowAnimationFrame(TimeMs ms);
|
||||||
|
|
||||||
void updateMouseTracking();
|
void updateMouseTracking();
|
||||||
|
|
||||||
|
@ -740,7 +744,7 @@ private:
|
||||||
MsgId _delayedShowAtMsgId = -1; // wtf?
|
MsgId _delayedShowAtMsgId = -1; // wtf?
|
||||||
mtpRequestId _delayedShowAtRequest = 0;
|
mtpRequestId _delayedShowAtRequest = 0;
|
||||||
|
|
||||||
object_ptr<HistoryTopBarWidget> _topBar;
|
object_ptr<HistoryView::TopBarWidget> _topBar;
|
||||||
object_ptr<Ui::ScrollArea> _scroll;
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
QPointer<HistoryInner> _list;
|
QPointer<HistoryInner> _list;
|
||||||
History *_migrated = nullptr;
|
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:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
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_service.h"
|
||||||
#include "history/history_media.h"
|
#include "history/history_media.h"
|
||||||
|
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
|
||||||
namespace HistoryLayout {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum CircleMask {
|
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);
|
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;
|
class HistoryService;
|
||||||
|
|
||||||
namespace HistoryLayout {
|
namespace HistoryView {
|
||||||
|
|
||||||
int WideChatWidth();
|
int WideChatWidth();
|
||||||
|
|
||||||
|
@ -49,4 +49,4 @@ void serviceColorsUpdated();
|
||||||
|
|
||||||
void paintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool outbg, RectPart tailSide);
|
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:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
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.h>
|
||||||
#include <rpl/combine_previous.h>
|
#include <rpl/combine_previous.h>
|
||||||
|
@ -34,7 +34,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
HistoryTopBarWidget::HistoryTopBarWidget(
|
namespace HistoryView {
|
||||||
|
|
||||||
|
TopBarWidget::TopBarWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::Controller*> controller)
|
not_null<Window::Controller*> controller)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
|
@ -122,35 +124,35 @@ HistoryTopBarWidget::HistoryTopBarWidget(
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::refreshLang() {
|
void TopBarWidget::refreshLang() {
|
||||||
InvokeQueued(this, [this] { updateControlsGeometry(); });
|
InvokeQueued(this, [this] { updateControlsGeometry(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::onForwardSelection() {
|
void TopBarWidget::onForwardSelection() {
|
||||||
if (App::main()) App::main()->forwardSelectedItems();
|
if (App::main()) App::main()->forwardSelectedItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::onDeleteSelection() {
|
void TopBarWidget::onDeleteSelection() {
|
||||||
if (App::main()) App::main()->confirmDeleteSelectedItems();
|
if (App::main()) App::main()->confirmDeleteSelectedItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::onClearSelection() {
|
void TopBarWidget::onClearSelection() {
|
||||||
if (App::main()) App::main()->clearSelectedItems();
|
if (App::main()) App::main()->clearSelectedItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::onSearch() {
|
void TopBarWidget::onSearch() {
|
||||||
if (_historyPeer) {
|
if (_historyPeer) {
|
||||||
App::main()->searchInPeer(_historyPeer);
|
App::main()->searchInPeer(_historyPeer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::onCall() {
|
void TopBarWidget::onCall() {
|
||||||
if (auto user = _historyPeer->asUser()) {
|
if (auto user = _historyPeer->asUser()) {
|
||||||
Calls::Current().startOutgoingCall(user);
|
Calls::Current().startOutgoingCall(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::showMenu() {
|
void TopBarWidget::showMenu() {
|
||||||
if (!_historyPeer || _menu) {
|
if (!_historyPeer || _menu) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -184,7 +186,7 @@ void HistoryTopBarWidget::showMenu() {
|
||||||
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::toggleInfoSection() {
|
void TopBarWidget::toggleInfoSection() {
|
||||||
if (Adaptive::ThreeColumn()
|
if (Adaptive::ThreeColumn()
|
||||||
&& (Auth().settings().thirdSectionInfoEnabled()
|
&& (Auth().settings().thirdSectionInfoEnabled()
|
||||||
|| Auth().settings().tabbedReplacedWithInfo())) {
|
|| 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) {
|
if (obj == _membersShowArea) {
|
||||||
switch (e->type()) {
|
switch (e->type()) {
|
||||||
case QEvent::MouseButtonPress:
|
case QEvent::MouseButtonPress:
|
||||||
|
@ -228,8 +230,8 @@ bool HistoryTopBarWidget::eventFilter(QObject *obj, QEvent *e) {
|
||||||
return TWidget::eventFilter(obj, e);
|
return TWidget::eventFilter(obj, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::paintEvent(QPaintEvent *e) {
|
void TopBarWidget::paintEvent(QPaintEvent *e) {
|
||||||
if (_animationMode) {
|
if (_animatingMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Painter p(this);
|
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);
|
auto history = App::historyLoaded(_historyPeer);
|
||||||
if (!history) return;
|
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 membersTextLeft = _leftTaken;
|
||||||
int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
|
int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
|
||||||
int membersTextWidth = _titlePeerTextWidth;
|
int membersTextWidth = _titlePeerTextWidth;
|
||||||
|
@ -300,12 +302,12 @@ QRect HistoryTopBarWidget::getMembersShowAreaGeometry() const {
|
||||||
return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight);
|
return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::mousePressEvent(QMouseEvent *e) {
|
void TopBarWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
auto handleClick = (e->button() == Qt::LeftButton)
|
auto handleClick = (e->button() == Qt::LeftButton)
|
||||||
&& (e->pos().y() < st::topBarHeight)
|
&& (e->pos().y() < st::topBarHeight)
|
||||||
&& (!_selectedCount);
|
&& (!_selectedCount);
|
||||||
if (handleClick) {
|
if (handleClick) {
|
||||||
if (_animationMode && _back->rect().contains(e->pos())) {
|
if (_animatingMode && _back->rect().contains(e->pos())) {
|
||||||
backClicked();
|
backClicked();
|
||||||
} else if (_historyPeer) {
|
} else if (_historyPeer) {
|
||||||
infoClicked();
|
infoClicked();
|
||||||
|
@ -313,7 +315,7 @@ void HistoryTopBarWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::infoClicked() {
|
void TopBarWidget::infoClicked() {
|
||||||
if (_historyPeer && _historyPeer->isSelf()) {
|
if (_historyPeer && _historyPeer->isSelf()) {
|
||||||
_controller->showSection(Info::Memento(
|
_controller->showSection(Info::Memento(
|
||||||
_historyPeer->id,
|
_historyPeer->id,
|
||||||
|
@ -323,11 +325,11 @@ void HistoryTopBarWidget::infoClicked() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::backClicked() {
|
void TopBarWidget::backClicked() {
|
||||||
_controller->showBackFromStack();
|
_controller->showBackFromStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::setHistoryPeer(PeerData *historyPeer) {
|
void TopBarWidget::setHistoryPeer(PeerData *historyPeer) {
|
||||||
if (_historyPeer != historyPeer) {
|
if (_historyPeer != historyPeer) {
|
||||||
_historyPeer = historyPeer;
|
_historyPeer = historyPeer;
|
||||||
_back->clearState();
|
_back->clearState();
|
||||||
|
@ -353,15 +355,15 @@ void HistoryTopBarWidget::setHistoryPeer(PeerData *historyPeer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::resizeEvent(QResizeEvent *e) {
|
void TopBarWidget::resizeEvent(QResizeEvent *e) {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
int HistoryTopBarWidget::countSelectedButtonsTop(float64 selectedShown) {
|
int TopBarWidget::countSelectedButtonsTop(float64 selectedShown) {
|
||||||
return (1. - selectedShown) * (-st::topBarHeight);
|
return (1. - selectedShown) * (-st::topBarHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateControlsGeometry() {
|
void TopBarWidget::updateControlsGeometry() {
|
||||||
auto hasSelected = (_selectedCount > 0);
|
auto hasSelected = (_selectedCount > 0);
|
||||||
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.current(hasSelected ? 1. : 0.));
|
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.current(hasSelected ? 1. : 0.));
|
||||||
auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
|
auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
|
||||||
|
@ -417,22 +419,22 @@ void HistoryTopBarWidget::updateControlsGeometry() {
|
||||||
updateMembersShowArea();
|
updateMembersShowArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::finishAnimating() {
|
void TopBarWidget::finishAnimating() {
|
||||||
_selectedShown.finish();
|
_selectedShown.finish();
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::setAnimationMode(bool enabled) {
|
void TopBarWidget::setAnimatingMode(bool enabled) {
|
||||||
if (_animationMode != enabled) {
|
if (_animatingMode != enabled) {
|
||||||
_animationMode = enabled;
|
_animatingMode = enabled;
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent, !_animationMode);
|
setAttribute(Qt::WA_OpaquePaintEvent, !_animatingMode);
|
||||||
finishAnimating();
|
finishAnimating();
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateControlsVisibility() {
|
void TopBarWidget::updateControlsVisibility() {
|
||||||
if (_animationMode) {
|
if (_animatingMode) {
|
||||||
hideChildren();
|
hideChildren();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -465,7 +467,7 @@ void HistoryTopBarWidget::updateControlsVisibility() {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateMembersShowArea() {
|
void TopBarWidget::updateMembersShowArea() {
|
||||||
if (!App::main()) {
|
if (!App::main()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -496,7 +498,7 @@ void HistoryTopBarWidget::updateMembersShowArea() {
|
||||||
_membersShowArea->setGeometry(getMembersShowAreaGeometry());
|
_membersShowArea->setGeometry(getMembersShowAreaGeometry());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::showSelected(SelectedState state) {
|
void TopBarWidget::showSelected(SelectedState state) {
|
||||||
auto canDelete = (state.count > 0 && state.count == state.canDeleteCount);
|
auto canDelete = (state.count > 0 && state.count == state.canDeleteCount);
|
||||||
auto canForward = (state.count > 0 && state.count == state.canForwardCount);
|
auto canForward = (state.count > 0 && state.count == state.canForwardCount);
|
||||||
if (_selectedCount == state.count && _canDelete == canDelete && _canForward == canForward) {
|
if (_selectedCount == state.count && _canDelete == canDelete && _canForward == canForward) {
|
||||||
|
@ -539,12 +541,12 @@ void HistoryTopBarWidget::showSelected(SelectedState state) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::selectedShowCallback() {
|
void TopBarWidget::selectedShowCallback() {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateAdaptiveLayout() {
|
void TopBarWidget::updateAdaptiveLayout() {
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
if (Adaptive::OneColumn()) {
|
if (Adaptive::OneColumn()) {
|
||||||
createUnreadBadge();
|
createUnreadBadge();
|
||||||
|
@ -555,7 +557,7 @@ void HistoryTopBarWidget::updateAdaptiveLayout() {
|
||||||
updateInfoToggleActive();
|
updateInfoToggleActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::createUnreadBadge() {
|
void TopBarWidget::createUnreadBadge() {
|
||||||
if (_unreadBadge) {
|
if (_unreadBadge) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -569,7 +571,7 @@ void HistoryTopBarWidget::createUnreadBadge() {
|
||||||
updateUnreadBadge();
|
updateUnreadBadge();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateUnreadBadge() {
|
void TopBarWidget::updateUnreadBadge() {
|
||||||
if (!_unreadBadge) return;
|
if (!_unreadBadge) return;
|
||||||
|
|
||||||
auto mutedCount = App::histories().unreadMutedCount();
|
auto mutedCount = App::histories().unreadMutedCount();
|
||||||
|
@ -596,7 +598,7 @@ void HistoryTopBarWidget::updateUnreadBadge() {
|
||||||
}(), active);
|
}(), active);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateInfoToggleActive() {
|
void TopBarWidget::updateInfoToggleActive() {
|
||||||
auto infoThirdActive = Adaptive::ThreeColumn()
|
auto infoThirdActive = Adaptive::ThreeColumn()
|
||||||
&& (Auth().settings().thirdSectionInfoEnabled()
|
&& (Auth().settings().thirdSectionInfoEnabled()
|
||||||
|| Auth().settings().tabbedReplacedWithInfo());
|
|| Auth().settings().tabbedReplacedWithInfo());
|
||||||
|
@ -610,7 +612,7 @@ void HistoryTopBarWidget::updateInfoToggleActive() {
|
||||||
_infoToggle->setRippleColorOverride(rippleOverride);
|
_infoToggle->setRippleColorOverride(rippleOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateOnlineDisplay() {
|
void TopBarWidget::updateOnlineDisplay() {
|
||||||
if (!_historyPeer) return;
|
if (!_historyPeer) return;
|
||||||
|
|
||||||
QString text;
|
QString text;
|
||||||
|
@ -689,7 +691,7 @@ void HistoryTopBarWidget::updateOnlineDisplay() {
|
||||||
updateOnlineDisplayTimer();
|
updateOnlineDisplayTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateOnlineDisplayTimer() {
|
void TopBarWidget::updateOnlineDisplayTimer() {
|
||||||
if (!_historyPeer) return;
|
if (!_historyPeer) return;
|
||||||
|
|
||||||
const auto now = unixtime();
|
const auto now = unixtime();
|
||||||
|
@ -709,6 +711,8 @@ void HistoryTopBarWidget::updateOnlineDisplayTimer() {
|
||||||
updateOnlineDisplayIn(minTimeout);
|
updateOnlineDisplayIn(minTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryTopBarWidget::updateOnlineDisplayIn(TimeMs timeout) {
|
void TopBarWidget::updateOnlineDisplayIn(TimeMs timeout) {
|
||||||
_onlineUpdater.callOnce(timeout);
|
_onlineUpdater.callOnce(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -22,9 +22,11 @@ namespace Window {
|
||||||
class Controller;
|
class Controller;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
||||||
class HistoryTopBarWidget : public Ui::RpWidget, private base::Subscriber {
|
namespace HistoryView {
|
||||||
|
|
||||||
|
class TopBarWidget : public Ui::RpWidget, private base::Subscriber {
|
||||||
public:
|
public:
|
||||||
HistoryTopBarWidget(
|
TopBarWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::Controller*> controller);
|
not_null<Window::Controller*> controller);
|
||||||
|
|
||||||
|
@ -41,15 +43,10 @@ public:
|
||||||
rpl::producer<bool> membersShowAreaActive() const {
|
rpl::producer<bool> membersShowAreaActive() const {
|
||||||
return _membersShowAreaActive.events();
|
return _membersShowAreaActive.events();
|
||||||
}
|
}
|
||||||
void setAnimationMode(bool enabled);
|
void setAnimatingMode(bool enabled);
|
||||||
|
|
||||||
void setHistoryPeer(PeerData *historyPeer);
|
void setHistoryPeer(PeerData *historyPeer);
|
||||||
|
|
||||||
static void paintUnreadCounter(
|
|
||||||
Painter &p,
|
|
||||||
int outerWidth,
|
|
||||||
PeerData *substractPeer = nullptr);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void mousePressEvent(QMouseEvent *e) override;
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
@ -116,9 +113,11 @@ private:
|
||||||
int _titlePeerTextWidth = 0;
|
int _titlePeerTextWidth = 0;
|
||||||
int _leftTaken = 0;
|
int _leftTaken = 0;
|
||||||
int _rightTaken = 0;
|
int _rightTaken = 0;
|
||||||
bool _animationMode = false;
|
bool _animatingMode = false;
|
||||||
|
|
||||||
int _unreadCounterSubscription = 0;
|
int _unreadCounterSubscription = 0;
|
||||||
base::Timer _onlineUpdater;
|
base::Timer _onlineUpdater;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -791,8 +791,7 @@ void ListWidget::refreshViewer() {
|
||||||
idForViewer,
|
idForViewer,
|
||||||
_idsLimit,
|
_idsLimit,
|
||||||
_idsLimit
|
_idsLimit
|
||||||
) | rpl::start_with_next([=](
|
) | rpl::start_with_next([=](SparseIdsMergedSlice &&slice) {
|
||||||
SparseIdsMergedSlice &&slice) {
|
|
||||||
if (!slice.fullCount()) {
|
if (!slice.fullCount()) {
|
||||||
// Don't display anything while full count is unknown.
|
// Don't display anything while full count is unknown.
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_widget.h"
|
#include "history/history_widget.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history_media.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_keys.h"
|
||||||
#include "lang/lang_cloud_manager.h"
|
#include "lang/lang_cloud_manager.h"
|
||||||
#include "boxes/add_contact_box.h"
|
#include "boxes/add_contact_box.h"
|
||||||
|
@ -3530,7 +3530,7 @@ void MainWidget::updateWindowAdaptiveLayout() {
|
||||||
// auto thirdColumnWidth = _history->tabbedSelectorSectionWidth();
|
// auto thirdColumnWidth = _history->tabbedSelectorSectionWidth();
|
||||||
// auto twoColumnsWidth = (layout.bodyWidth - thirdColumnWidth);
|
// auto twoColumnsWidth = (layout.bodyWidth - thirdColumnWidth);
|
||||||
// auto sameRatioChatWidth = twoColumnsWidth - qRound(dialogsWidthRatio * twoColumnsWidth);
|
// auto sameRatioChatWidth = twoColumnsWidth - qRound(dialogsWidthRatio * twoColumnsWidth);
|
||||||
// auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryLayout::WideChatWidth());
|
// auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryView::WideChatWidth());
|
||||||
// chatWidth -= thirdColumnWidth;
|
// chatWidth -= thirdColumnWidth;
|
||||||
// auto extendChatBy = desiredChatWidth - chatWidth;
|
// auto extendChatBy = desiredChatWidth - chatWidth;
|
||||||
// accumulate_min(extendChatBy, layout.dialogsWidth - st::columnMinimalWidthLeft);
|
// accumulate_min(extendChatBy, layout.dialogsWidth - st::columnMinimalWidthLeft);
|
||||||
|
|
|
@ -503,9 +503,7 @@ auto GroupThumbs::validateCacheEntry(Key key) -> not_null<Thumb*> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupThumbs::markCacheStale() {
|
void GroupThumbs::markCacheStale() {
|
||||||
while (!_dying.empty()) {
|
_dying.clear();
|
||||||
_dying.pop_back();
|
|
||||||
}
|
|
||||||
for (const auto &cacheItem : _cache) {
|
for (const auto &cacheItem : _cache) {
|
||||||
const auto &thumb = cacheItem.second;
|
const auto &thumb = cacheItem.second;
|
||||||
thumb->setState(Thumb::State::Unknown);
|
thumb->setState(Thumb::State::Unknown);
|
||||||
|
|
|
@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "profile/profile_back_button.h"
|
#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_widgets.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
#include "styles/style_profile.h"
|
#include "styles/style_profile.h"
|
||||||
|
@ -41,8 +41,6 @@ void BackButton::paintEvent(QPaintEvent *e) {
|
||||||
p.setFont(st::topBarButton.font);
|
p.setFont(st::topBarButton.font);
|
||||||
p.setPen(st::topBarButton.textFg);
|
p.setPen(st::topBarButton.textFg);
|
||||||
p.drawTextLeft(st::topBarArrowPadding.left(), st::topBarButton.padding.top() + st::topBarButton.textTop, width(), _text);
|
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) {
|
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_shared_media.h"
|
||||||
#include "storage/storage_user_photos.h"
|
#include "storage/storage_user_photos.h"
|
||||||
|
#include "storage/storage_feed_messages.h"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
|
|
||||||
|
@ -31,9 +32,20 @@ public:
|
||||||
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
|
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
|
||||||
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() 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:
|
private:
|
||||||
SharedMedia _sharedMedia;
|
SharedMedia _sharedMedia;
|
||||||
UserPhotos _userPhotos;
|
UserPhotos _userPhotos;
|
||||||
|
FeedMessages _feedMessages;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,6 +109,39 @@ rpl::producer<UserPhotosSliceUpdate> Facade::Impl::userPhotosSliceUpdated() cons
|
||||||
return _userPhotos.sliceUpdated();
|
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>()) {
|
Facade::Facade() : _impl(std::make_unique<Impl>()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +205,39 @@ rpl::producer<UserPhotosSliceUpdate> Facade::userPhotosSliceUpdated() const {
|
||||||
return _impl->userPhotosSliceUpdated();
|
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;
|
Facade::~Facade() = default;
|
||||||
|
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
|
@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <rpl/producer.h>
|
#include <rpl/producer.h>
|
||||||
#include "base/enum_mask.h"
|
#include "base/enum_mask.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct MessagesResult;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
|
|
||||||
struct SparseIdsListResult;
|
struct SparseIdsListResult;
|
||||||
|
@ -31,6 +35,14 @@ struct UserPhotosQuery;
|
||||||
struct UserPhotosResult;
|
struct UserPhotosResult;
|
||||||
struct UserPhotosSliceUpdate;
|
struct UserPhotosSliceUpdate;
|
||||||
|
|
||||||
|
struct FeedMessagesAddNew;
|
||||||
|
struct FeedMessagesAddSlice;
|
||||||
|
struct FeedMessagesRemoveOne;
|
||||||
|
struct FeedMessagesRemoveAll;
|
||||||
|
struct FeedMessagesQuery;
|
||||||
|
using FeedMessagesResult = Data::MessagesResult;
|
||||||
|
struct FeedMessagesSliceUpdate;
|
||||||
|
|
||||||
class Facade {
|
class Facade {
|
||||||
public:
|
public:
|
||||||
Facade();
|
Facade();
|
||||||
|
@ -54,6 +66,17 @@ public:
|
||||||
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
|
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
|
||||||
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() 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();
|
~Facade();
|
||||||
|
|
||||||
private:
|
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) {
|
void SharedMedia::add(SharedMediaAddSlice &&query) {
|
||||||
Expects(IsValidSharedMediaType(query.type));
|
Expects(IsValidSharedMediaType(query.type));
|
||||||
|
|
||||||
auto peerIt = enforceLists(query.peerId);
|
auto peerIt = enforceLists(query.peerId);
|
||||||
auto index = static_cast<int>(query.type);
|
auto index = static_cast<int>(query.type);
|
||||||
peerIt->second[index].addSlice(
|
peerIt->second[index].addSlice(
|
||||||
|
|
|
@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include <rpl/range.h>
|
#include <rpl/range.h>
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
#include "window/section_memento.h"
|
#include "window/section_memento.h"
|
||||||
#include "window/window_slide_animation.h"
|
#include "window/window_slide_animation.h"
|
||||||
|
#include "window/themes/window_theme.h"
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
|
||||||
|
@ -71,6 +73,46 @@ void SectionWidget::showFast() {
|
||||||
showFinished();
|
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) {
|
void SectionWidget::paintEvent(QPaintEvent *e) {
|
||||||
if (_showAnimation) {
|
if (_showAnimation) {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
|
|
@ -126,6 +126,8 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PaintBackground(QWidget *widget, QPaintEvent *event);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
|
|
@ -169,8 +169,12 @@
|
||||||
<(src_loc)/data/data_drafts.h
|
<(src_loc)/data/data_drafts.h
|
||||||
<(src_loc)/data/data_feed.cpp
|
<(src_loc)/data/data_feed.cpp
|
||||||
<(src_loc)/data/data_feed.h
|
<(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_flags.h
|
||||||
<(src_loc)/data/data_game.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.cpp
|
||||||
<(src_loc)/data/data_notify_settings.h
|
<(src_loc)/data/data_notify_settings.h
|
||||||
<(src_loc)/data/data_peer.cpp
|
<(src_loc)/data/data_peer.cpp
|
||||||
|
@ -211,16 +215,24 @@
|
||||||
<(src_loc)/dialogs/dialogs_search_from_controllers.h
|
<(src_loc)/dialogs/dialogs_search_from_controllers.h
|
||||||
<(src_loc)/dialogs/dialogs_widget.cpp
|
<(src_loc)/dialogs/dialogs_widget.cpp
|
||||||
<(src_loc)/dialogs/dialogs_widget.h
|
<(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.cpp
|
||||||
<(src_loc)/history/history.h
|
<(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.cpp
|
||||||
<(src_loc)/history/history_drag_area.h
|
<(src_loc)/history/history_drag_area.h
|
||||||
<(src_loc)/history/history_item.cpp
|
<(src_loc)/history/history_item.cpp
|
||||||
|
@ -241,10 +253,6 @@
|
||||||
<(src_loc)/history/history_message.h
|
<(src_loc)/history/history_message.h
|
||||||
<(src_loc)/history/history_service.cpp
|
<(src_loc)/history/history_service.cpp
|
||||||
<(src_loc)/history/history_service.h
|
<(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.cpp
|
||||||
<(src_loc)/history/history_widget.h
|
<(src_loc)/history/history_widget.h
|
||||||
<(src_loc)/info/info_content_widget.cpp
|
<(src_loc)/info/info_content_widget.cpp
|
||||||
|
@ -531,6 +539,8 @@
|
||||||
<(src_loc)/storage/serialize_document.h
|
<(src_loc)/storage/serialize_document.h
|
||||||
<(src_loc)/storage/storage_facade.cpp
|
<(src_loc)/storage/storage_facade.cpp
|
||||||
<(src_loc)/storage/storage_facade.h
|
<(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.cpp
|
||||||
<(src_loc)/storage/storage_media_prepare.h
|
<(src_loc)/storage/storage_media_prepare.h
|
||||||
<(src_loc)/storage/storage_shared_media.cpp
|
<(src_loc)/storage/storage_shared_media.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue