1
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-04-07 01:53:14 +00:00

First version of feed section view.

This commit is contained in:
John Preston 2018-01-09 20:08:31 +03:00
parent 50b120bc22
commit 794e31505b
55 changed files with 3608 additions and 331 deletions

View File

@ -1415,6 +1415,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_admin_pin_messages" = "Pin messages";
"lng_admin_log_admin_add_admins" = "Add new admins";
"lng_feed_show_next" = "Show Next";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h"
#include "storage/storage_user_photos.h"
#include "storage/storage_media_prepare.h"
#include "storage/storage_feed_messages.h"
#include "data/data_sparse_ids.h"
#include "data/data_search_controller.h"
#include "data/data_channel_admins.h"
@ -49,6 +50,7 @@ constexpr auto kUnreadMentionsPreloadIfLess = 5;
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
constexpr auto kUnreadMentionsNextRequestLimit = 100;
constexpr auto kSharedMediaLimit = 100;
constexpr auto kFeedMessagesLimit = 50;
constexpr auto kReadFeaturedSetsTimeout = TimeMs(1000);
constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
@ -2517,6 +2519,125 @@ void ApiWrap::userPhotosDone(
));
}
void ApiWrap::requestFeedMessages(
not_null<Data::Feed*> feed,
Data::MessagePosition messageId,
SliceType slice) {
const auto key = std::make_tuple(feed, messageId, slice);
if (_feedMessagesRequests.contains(key)) {
return;
}
const auto addOffset = 0;
const auto limit = kFeedMessagesLimit;
const auto sourcesHash = int32(0);
const auto hash = int32(0);
const auto flags = (messageId && messageId.fullId.channel)
? MTPchannels_GetFeed::Flag::f_offset_position
: MTPchannels_GetFeed::Flag::f_offset_to_max_read;
const auto requestId = request(MTPchannels_GetFeed(
MTP_flags(flags),
MTP_int(feed->id()),
MTP_feedPosition(
MTP_int(messageId.date),
MTP_peerChannel(MTP_int(messageId.fullId.channel)),
MTP_int(messageId.fullId.msg)),
MTP_int(addOffset),
MTP_int(limit),
MTPFeedPosition(),
MTPFeedPosition(),
MTP_int(sourcesHash),
MTP_int(hash)
)).done([=](const MTPmessages_FeedMessages &result) {
const auto key = std::make_tuple(feed, messageId, slice);
_feedMessagesRequests.remove(key);
feedMessagesDone(feed, messageId, slice, result);
}).fail([=](const RPCError &error) {
_feedMessagesRequests.remove(key);
}).send();
_feedMessagesRequests.emplace(key, requestId);
}
void ApiWrap::feedMessagesDone(
not_null<Data::Feed*> feed,
Data::MessagePosition messageId,
SliceType slice,
const MTPmessages_FeedMessages &result) {
if (result.type() == mtpc_messages_feedMessagesNotModified) {
LOG(("API Error: Unexpected messages.feedMessagesNotModified."));
_session->storage().add(Storage::FeedMessagesAddSlice(
feed->id(),
std::vector<Data::MessagePosition>(),
Data::FullMessagesRange));
return;
}
Assert(result.type() == mtpc_messages_feedMessages);
const auto &data = result.c_messages_feedMessages();
const auto &messages = data.vmessages.v;
const auto type = NewMessageExisting;
auto ids = std::vector<Data::MessagePosition>();
auto noSkipRange = Data::MessagesRange(messageId, messageId);
auto accumulateFrom = [](auto &from, const auto &candidate) {
if (!from || from > candidate) {
from = candidate;
}
};
auto accumulateTill = [](auto &till, const auto &candidate) {
if (!till || till < candidate) {
till = candidate;
}
};
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
if (!messages.empty()) {
ids.reserve(messages.size());
for (const auto &msg : messages) {
if (const auto item = App::histories().addNewMessage(msg, type)) {
const auto position = item->position();
ids.push_back(position);
accumulateFrom(noSkipRange.from, position);
accumulateTill(noSkipRange.till, position);
}
}
ranges::reverse(ids);
}
if (data.has_min_position()) {
accumulateFrom(
noSkipRange.from,
Data::FeedPositionFromMTP(data.vmin_position));
} else {
noSkipRange.from = Data::MinMessagePosition;
}
if (data.has_max_position()) {
accumulateTill(
noSkipRange.till,
Data::FeedPositionFromMTP(data.vmax_position));
} else {
noSkipRange.till = Data::MaxMessagePosition;
}
const auto unreadPosition = [&] {
if (data.has_read_max_position()) {
return Data::FeedPositionFromMTP(data.vread_max_position);
} else if (!messageId) {
return ids.empty()
? noSkipRange.till
: ids.back();
}
return Data::MessagePosition();
}();
_session->storage().add(Storage::FeedMessagesAddSlice(
feed->id(),
std::move(ids),
noSkipRange));
if (unreadPosition) {
feed->setUnreadPosition(unreadPosition);
}
}
void ApiWrap::sendAction(const SendOptions &options) {
readServerHistory(options.history);
options.history->getReadyFor(ShowAtTheEndMsgId);

View File

@ -9,15 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/event_stream.h>
#include "base/timer.h"
#include "core/single_timer.h"
#include "mtproto/sender.h"
#include "base/flat_map.h"
#include "base/flat_set.h"
#include "core/single_timer.h"
#include "mtproto/sender.h"
#include "chat_helpers/stickers.h"
#include "data/data_messages.h"
class TaskQueue;
class AuthSession;
enum class SparseIdsLoadDirection;
struct MessageGroupId;
struct SendingAlbum;
enum class SendMediaType;
@ -129,7 +129,7 @@ public:
bool adminsEnabled,
base::flat_set<not_null<UserData*>> &&admins);
using SliceType = SparseIdsLoadDirection;
using SliceType = Data::LoadDirection;
void requestSharedMedia(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
@ -143,6 +143,11 @@ public:
not_null<UserData*> user,
PhotoId afterId);
void requestFeedMessages(
not_null<Data::Feed*> feed,
Data::MessagePosition messageId,
SliceType slice);
void stickerSetInstalled(uint64 setId) {
_stickerSetInstalled.fire_copy(setId);
}
@ -300,6 +305,12 @@ private:
PhotoId photoId,
const MTPphotos_Photos &result);
void feedMessagesDone(
not_null<Data::Feed*> feed,
Data::MessagePosition messageId,
SliceType slice,
const MTPmessages_FeedMessages &result);
void sendSharedContact(
const QString &phone,
const QString &firstName,
@ -417,6 +428,11 @@ private:
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
base::flat_map<std::tuple<
not_null<Data::Feed*>,
Data::MessagePosition,
SliceType>, mtpRequestId> _feedMessagesRequests;
rpl::event_stream<SendOptions> _sendActions;
struct ReadRequest {

View File

@ -19,10 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "data/data_abstract_structure.h"
#include "data/data_session.h"
#include "history/history_service_layout.h"
#include "history/history_location_manager.h"
#include "history/history_media_types.h"
#include "history/history_item_components.h"
#include "history/view/history_view_service_message.h"
#include "media/media_audio.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "messenger.h"
@ -2141,7 +2141,7 @@ namespace {
if (App::main()) {
App::main()->updateScrollColors();
}
HistoryLayout::serviceColorsUpdated();
HistoryView::serviceColorsUpdated();
} else if (update.type == Update::Type::New) {
prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg);
prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected);
@ -2149,7 +2149,7 @@ namespace {
if (App::main()) {
App::main()->updateScrollColors();
}
HistoryLayout::serviceColorsUpdated();
HistoryView::serviceColorsUpdated();
}
});
}

View File

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_info_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "history/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_section.h"
#include "window/window_controller.h"
#include "profile/profile_channel_controllers.h"
#include "info/profile/info_profile_button.h"

View File

@ -11,16 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
FeedPosition::FeedPosition(const MTPFeedPosition &position)
: date(position.c_feedPosition().vdate.v)
, msgId(
peerToChannel(peerFromMTP(position.c_feedPosition().vpeer)),
position.c_feedPosition().vid.v) {
}
MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position) {
Expects(position.type() == mtpc_feedPosition);
FeedPosition::FeedPosition(not_null<HistoryItem*> item)
: date(toServerTime(item->date.toTime_t()).v)
, msgId(item->fullId()) {
const auto &data = position.c_feedPosition();
return MessagePosition(data.vdate.v, FullMsgId(
peerToChannel(peerFromMTP(data.vpeer)),
data.vid.v));
}
Feed::Feed(FeedId id)
@ -82,7 +79,7 @@ void Feed::paintUserpic(
}
bool Feed::justSetLastMessage(not_null<HistoryItem*> item) {
if (_lastMessage && FeedPosition(item) <= FeedPosition(_lastMessage)) {
if (_lastMessage && item->position() <= _lastMessage->position()) {
return false;
}
_lastMessage = item;
@ -118,8 +115,4 @@ void Feed::setUnreadCounts(int unreadCount, int unreadMutedCount) {
_unreadMutedCount = unreadMutedCount;
}
void Feed::setUnreadPosition(const FeedPosition &position) {
_unreadPosition = position;
}
} // namespace Data

View File

@ -8,51 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "dialogs/dialogs_entry.h"
#include "data/data_messages.h"
class ChannelData;
namespace Data {
struct FeedPosition {
FeedPosition() = default;
explicit FeedPosition(const MTPFeedPosition &position);
explicit FeedPosition(not_null<HistoryItem*> item);
FeedPosition(TimeId date, FullMsgId msgId) : date(date), msgId(msgId) {
}
explicit operator bool() const {
return (msgId.msg != 0);
}
inline bool operator<(const FeedPosition &other) const {
if (date < other.date) {
return true;
} else if (other.date < date) {
return false;
}
return (msgId < other.msgId);
}
inline bool operator>(const FeedPosition &other) const {
return other < *this;
}
inline bool operator<=(const FeedPosition &other) const {
return !(other < *this);
}
inline bool operator>=(const FeedPosition &other) const {
return !(*this < other);
}
inline bool operator==(const FeedPosition &other) const {
return (date == other.date)
&& (msgId == other.msgId);
}
inline bool operator!=(const FeedPosition &other) const {
return !(*this == other);
}
TimeId date = 0;
FullMsgId msgId;
};
MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position);
class Feed : public Dialogs::Entry {
public:
@ -69,7 +31,15 @@ public:
void historyCleared(not_null<History*> history);
void setUnreadCounts(int unreadCount, int unreadMutedCount);
void setUnreadPosition(const FeedPosition &position);
void setUnreadPosition(const MessagePosition &position) {
_unreadPosition = position;
}
MessagePosition unreadPosition() const {
return _unreadPosition.current();
}
rpl::producer<MessagePosition> unreadPositionChanges() const {
return _unreadPosition.changes();
}
bool toImportant() const override {
return false; // TODO feeds workmode
@ -99,7 +69,7 @@ private:
HistoryItem *_lastMessage = nullptr;
FeedPosition _unreadPosition;
rpl::variable<MessagePosition> _unreadPosition;
int _unreadCount = 0;
int _unreadMutedCount = 0;
bool _complete = false;

View 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

View 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

View 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

View 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

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h"
#include "data/data_session.h"
#include "data/data_messages.h"
namespace Api {
namespace {
@ -23,7 +24,7 @@ MTPmessages_Search PrepareSearchRequest(
Storage::SharedMediaType type,
const QString &query,
MsgId messageId,
SparseIdsLoadDirection direction) {
Data::LoadDirection direction) {
const auto filter = [&] {
using Type = Storage::SharedMediaType;
switch (type) {
@ -58,17 +59,17 @@ MTPmessages_Search PrepareSearchRequest(
const auto limit = messageId ? kSharedMediaLimit : 0;
const auto offsetId = [&] {
switch (direction) {
case SparseIdsLoadDirection::Before:
case SparseIdsLoadDirection::Around: return messageId;
case SparseIdsLoadDirection::After: return messageId + 1;
case Data::LoadDirection::Before:
case Data::LoadDirection::Around: return messageId;
case Data::LoadDirection::After: return messageId + 1;
}
Unexpected("Direction in PrepareSearchRequest");
}();
const auto addOffset = [&] {
switch (direction) {
case SparseIdsLoadDirection::Before: return 0;
case SparseIdsLoadDirection::Around: return -limit / 2;
case SparseIdsLoadDirection::After: return -limit;
case Data::LoadDirection::Before: return 0;
case Data::LoadDirection::Around: return -limit / 2;
case Data::LoadDirection::After: return -limit;
}
Unexpected("Direction in PrepareSearchRequest");
}();
@ -92,7 +93,7 @@ SearchResult ParseSearchResult(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
MsgId messageId,
SparseIdsLoadDirection direction,
Data::LoadDirection direction,
const MTPmessages_Messages &data) {
auto result = SearchResult();
result.noSkipRange = MsgRange{ messageId, messageId };
@ -158,11 +159,11 @@ SearchResult ParseSearchResult(
if (messageId && result.messageIds.empty()) {
result.noSkipRange = [&]() -> MsgRange {
switch (direction) {
case SparseIdsLoadDirection::Before: // All old loaded.
case Data::LoadDirection::Before: // All old loaded.
return { 0, result.noSkipRange.till };
case SparseIdsLoadDirection::Around: // All loaded.
case Data::LoadDirection::Around: // All loaded.
return { 0, ServerMaxMsgId };
case SparseIdsLoadDirection::After: // All new loaded.
case Data::LoadDirection::After: // All new loaded.
return { result.noSkipRange.from, ServerMaxMsgId };
}
Unexpected("Direction in ParseSearchResult");

View File

@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h"
#include "base/value_ordering.h"
namespace Data {
enum class LoadDirection : char;
} // namespace Data
namespace Api {
struct SearchResult {
@ -26,13 +30,13 @@ MTPmessages_Search PrepareSearchRequest(
Storage::SharedMediaType type,
const QString &query,
MsgId messageId,
SparseIdsLoadDirection direction);
Data::LoadDirection direction);
SearchResult ParseSearchResult(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
MsgId messageId,
SparseIdsLoadDirection direction,
Data::LoadDirection direction,
const MTPmessages_Messages &data);
class SearchController : private MTP::Sender {

View File

@ -361,17 +361,17 @@ void SparseIdsSliceBuilder::requestMessages(
RequestDirection direction) {
auto requestAroundData = [&]() -> AroundData {
if (_ids.empty()) {
return { _key, SparseIdsLoadDirection::Around };
return { _key, Data::LoadDirection::Around };
} else if (direction == RequestDirection::Before) {
return { _ids.front(), SparseIdsLoadDirection::Before };
return { _ids.front(), Data::LoadDirection::Before };
}
return { _ids.back(), SparseIdsLoadDirection::After };
return { _ids.back(), Data::LoadDirection::After };
};
_insufficientAround.fire(requestAroundData());
}
void SparseIdsSliceBuilder::requestMessagesCount() {
_insufficientAround.fire({ 0, SparseIdsLoadDirection::Around });
_insufficientAround.fire({ 0, Data::LoadDirection::Around });
}
SparseIdsSlice SparseIdsSliceBuilder::snapshot() const {

View File

@ -7,17 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_messages.h"
namespace Storage {
struct SparseIdsListResult;
struct SparseIdsSliceUpdate;
} // namespace Storage
enum class SparseIdsLoadDirection {
Around,
Before,
After,
};
class SparseIdsSlice {
public:
using Key = MsgId;
@ -178,8 +174,7 @@ public:
void checkInsufficient();
struct AroundData {
MsgId aroundId = 0;
SparseIdsLoadDirection direction
= SparseIdsLoadDirection::Around;
Data::LoadDirection direction = Data::LoadDirection::Around;
inline bool operator<(const AroundData &other) const {
return (aroundId < other.aroundId)

View File

@ -133,29 +133,43 @@ inline bool operator!=(const MsgRange &a, const MsgRange &b) {
}
struct FullMsgId {
FullMsgId() = default;
FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) {
constexpr FullMsgId() = default;
constexpr FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) {
}
explicit operator bool() const {
return msg != 0;
}
inline constexpr bool operator<(const FullMsgId &other) const {
if (channel < other.channel) {
return true;
} else if (channel > other.channel) {
return false;
}
return msg < other.msg;
}
inline constexpr bool operator>(const FullMsgId &other) const {
return other < *this;
}
inline constexpr bool operator<=(const FullMsgId &other) const {
return !(other < *this);
}
inline constexpr bool operator>=(const FullMsgId &other) const {
return !(*this < other);
}
inline constexpr bool operator==(const FullMsgId &other) const {
return (channel == other.channel) && (msg == other.msg);
}
inline constexpr bool operator!=(const FullMsgId &other) const {
return !(*this == other);
}
ChannelId channel = NoChannel;
MsgId msg = 0;
};
inline bool operator==(const FullMsgId &a, const FullMsgId &b) {
return (a.channel == b.channel) && (a.msg == b.msg);
}
inline bool operator!=(const FullMsgId &a, const FullMsgId &b) {
return !(a == b);
}
inline bool operator<(const FullMsgId &a, const FullMsgId &b) {
if (a.channel < b.channel) {
return true;
} else if (a.channel > b.channel) {
return false;
}
return a.msg < b.msg;
}
using MessageIdsList = std::vector<FullMsgId>;

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_indexed_list.h"
#include "dialogs/dialogs_layout.h"
#include "dialogs/dialogs_search_from_controllers.h"
#include "history/feed/history_feed_section.h"
#include "styles/style_dialogs.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_window.h"
@ -1739,8 +1740,8 @@ void DialogsInner::applyFeedDialog(const MTPDdialogFeed &dialog) {
addSavedPeersAfter(feed->chatsListDate());
}
if (dialog.has_read_max_position()) {
const auto position = Data::FeedPosition(dialog.vread_max_position);
feed->setUnreadPosition(position);
feed->setUnreadPosition(
Data::FeedPositionFromMTP(dialog.vread_max_position));
}
}
@ -2307,8 +2308,7 @@ bool DialogsInner::chooseRow() {
if (const auto history = chosen.key.history()) {
App::main()->choosePeer(history->peer->id, chosen.messageId);
} else if (const auto feed = chosen.key.feed()) {
// #TODO feeds open
// _controller->showSection(HistoryFeed::Memento(feed));
_controller->showSection(HistoryFeed::Memento(feed));
}
if (openSearchResult) {
emit searchResultChosen();

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_admin_log_filter.h"
#include "history/admin_log/history_admin_log_filter.h"
#include "styles/style_boxes.h"
#include "ui/widgets/checkbox.h"

View File

@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "boxes/abstract_box.h"
#include "history/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_section.h"
namespace AdminLog {

View File

@ -5,15 +5,15 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_admin_log_inner.h"
#include "history/admin_log/history_admin_log_inner.h"
#include "styles/style_history.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "history/history_service_layout.h"
#include "history/history_admin_log_section.h"
#include "history/history_admin_log_filter.h"
#include "history/history_item_components.h"
#include "history/admin_log/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_filter.h"
#include "history/view/history_view_service_message.h"
#include "chat_helpers/message_field.h"
#include "mainwindow.h"
#include "mainwidget.h"
@ -691,7 +691,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
if (auto date = item->Get<HistoryMessageDate>()) {
date->paint(p, dateY, width);
} else {
HistoryLayout::ServiceMessagePainter::paintDate(p, item->date, dateY, width);
HistoryView::ServiceMessagePainter::paintDate(
p,
item->date,
dateY,
width);
}
}
}
@ -723,7 +727,12 @@ void InnerWidget::paintEmpty(Painter &p) {
auto innerWidth = rectWidth - st::historyAdminLogEmptyPadding.left() - st::historyAdminLogEmptyPadding.right();
auto rectHeight = st::historyAdminLogEmptyPadding.top() + _emptyText.countHeight(innerWidth) + st::historyAdminLogEmptyPadding.bottom();
auto rect = QRect((width() - rectWidth) / 2, (height() - rectHeight) / 3, rectWidth, rectHeight);
HistoryLayout::ServiceMessagePainter::paintBubble(p, rect.x(), rect.y(), rect.width(), rect.height());
HistoryView::ServiceMessagePainter::paintBubble(
p,
rect.x(),
rect.y(),
rect.width(),
rect.height());
p.setPen(st::msgServiceFg);
_emptyText.draw(p, rect.x() + st::historyAdminLogEmptyPadding.left(), rect.y() + st::historyAdminLogEmptyPadding.top(), innerWidth, style::al_top);

View File

@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "history/history_admin_log_item.h"
#include "history/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/admin_log/history_admin_log_section.h"
#include "ui/widgets/tooltip.h"
#include "ui/rp_widget.h"
#include "mtproto/sender.h"

View File

@ -5,11 +5,11 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_admin_log_item.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/history_service.h"
#include "history/history_message.h"
#include "history/history_admin_log_inner.h"
#include "history/admin_log/history_admin_log_inner.h"
#include "lang/lang_keys.h"
#include "boxes/sticker_set_box.h"
#include "core/tl_help.h"

View File

@ -5,10 +5,10 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_admin_log_section.h"
#include "history/admin_log/history_admin_log_section.h"
#include "history/history_admin_log_inner.h"
#include "history/history_admin_log_filter.h"
#include "history/admin_log/history_admin_log_inner.h"
#include "history/admin_log/history_admin_log_filter.h"
#include "profile/profile_back_button.h"
#include "styles/style_history.h"
#include "styles/style_window.h"
@ -379,45 +379,10 @@ void Widget::paintEvent(QPaintEvent *e) {
// updateListSize();
//}
Painter p(this);
auto clip = e->rect();
auto ms = getms();
//auto ms = getms();
//_historyDownShown.step(ms);
auto fill = QRect(0, 0, width(), App::main()->height());
auto fromy = App::main()->backgroundFromY();
auto x = 0, y = 0;
auto cached = App::main()->cachedBackground(fill, x, y);
if (cached.isNull()) {
if (Window::Theme::Background()->tile()) {
auto &pix = Window::Theme::Background()->pixmapForTiled();
auto left = clip.left();
auto top = clip.top();
auto right = clip.left() + clip.width();
auto bottom = clip.top() + clip.height();
auto w = pix.width() / cRetinaFactor();
auto h = pix.height() / cRetinaFactor();
auto sx = qFloor(left / w);
auto sy = qFloor((top - fromy) / h);
auto cx = qCeil(right / w);
auto cy = qCeil((bottom - fromy) / h);
for (auto i = sx; i < cx; ++i) {
for (auto j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
}
}
} else {
PainterHighQualityEnabler hq(p);
auto &pix = Window::Theme::Background()->pixmap();
QRect to, from;
Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from);
to.moveTop(to.top() + fromy);
p.drawPixmap(to, pix, from);
}
} else {
p.drawPixmap(x, fromy + y, cached);
}
SectionWidget::PaintBackground(this, e);
}
void Widget::onScroll() {

View File

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/section_widget.h"
#include "window/section_memento.h"
#include "history/history_admin_log_item.h"
#include "history/admin_log/history_admin_log_item.h"
#include "mtproto/sender.h"
namespace Notify {

View 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 &params) {
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 &params) {
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 &params) {
_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

View 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 &params) override;
bool showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) 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 &params) 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

View File

@ -11,9 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_history.h"
#include "core/file_utilities.h"
#include "history/history_message.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_item_components.h"
#include "history/view/history_view_service_message.h"
#include "ui/text_options.h"
#include "ui/widgets/popup_menu.h"
#include "window/window_controller.h"
@ -475,7 +475,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
p.restoreTextPalette();
}
} else if (noHistoryDisplayed) {
HistoryLayout::paintEmpty(p, width(), height());
HistoryView::paintEmpty(p, width(), height());
}
if (!noHistoryDisplayed) {
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
@ -637,7 +637,11 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (auto date = item->Get<HistoryMessageDate>()) {
date->paint(p, dateY, width);
} else {
HistoryLayout::ServiceMessagePainter::paintDate(p, item->date, dateY, width);
HistoryView::ServiceMessagePainter::paintDate(
p,
item->date,
dateY,
width);
}
}
}
@ -2117,8 +2121,9 @@ bool HistoryInner::canDeleteSelected() const {
return (selectedState.count > 0) && (selectedState.count == selectedState.canDeleteCount);
}
HistoryTopBarWidget::SelectedState HistoryInner::getSelectionState() const {
auto result = HistoryTopBarWidget::SelectedState {};
auto HistoryInner::getSelectionState() const
-> HistoryView::TopBarWidget::SelectedState {
auto result = HistoryView::TopBarWidget::SelectedState {};
for (auto &selected : _selected) {
if (selected.second == FullSelection) {
++result.count;

View File

@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
#include "ui/widgets/tooltip.h"
#include "ui/widgets/scroll_area.h"
#include "history/history_top_bar_widget.h"
#include "history/view/history_view_top_bar_widget.h"
namespace Window {
class Controller;
@ -50,7 +50,7 @@ public:
bool canCopySelected() const;
bool canDeleteSelected() const;
HistoryTopBarWidget::SelectedState getSelectionState() const;
HistoryView::TopBarWidget::SelectedState getSelectionState() const;
void clearSelectedItems(bool onlyTextSelection = false);
MessageIdsList getSelectedItems() const;
void selectItem(not_null<HistoryItem*> item);

View File

@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "history/history_item_components.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_media_grouped.h"
#include "history/history_message.h"
#include "history/view/history_view_service_message.h"
#include "media/media_clip_reader.h"
#include "styles/style_dialogs.h"
#include "styles/style_history.h"
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_controller.h"
#include "core/crash_reports.h"
#include "data/data_session.h"
#include "data/data_messages.h"
#include "data/data_feed.h"
namespace {
@ -635,6 +636,10 @@ QString HistoryItem::directLink() const {
return QString();
}
Data::MessagePosition HistoryItem::position() const {
return Data::MessagePosition(toServerTime(date.toTime_t()).v, fullId());
}
MsgId HistoryItem::replyToId() const {
if (auto reply = Get<HistoryMessageReply>()) {
return reply->replyToId();

View File

@ -37,6 +37,10 @@ struct BotKeyboardButton;
struct RippleAnimation;
} // namespace style
namespace Data {
struct MessagePosition;
} // namespace Data
class HistoryElement {
public:
HistoryElement() = default;
@ -434,6 +438,7 @@ public:
FullMsgId fullId() const {
return FullMsgId(channelId(), id);
}
Data::MessagePosition position() const;
HistoryMedia *getMedia() const {
return _media.get();

View File

@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/effects/ripple_animation.h"
#include "ui/text_options.h"
#include "history/history_service_layout.h"
#include "history/history_message.h"
#include "history/history_media.h"
#include "history/history_media_types.h"
#include "history/view/history_view_service_message.h"
#include "media/media_audio.h"
#include "media/player/media_player_instance.h"
#include "styles/style_widgets.h"
@ -825,7 +825,7 @@ int HistoryMessageDate::height() const {
}
void HistoryMessageDate::paint(Painter &p, int y, int w) const {
HistoryLayout::ServiceMessagePainter::paintDate(p, _text, _width, y, w);
HistoryView::ServiceMessagePainter::paintDate(p, _text, _width, y, w);
}
HistoryMessageLogEntryOriginal::HistoryMessageLogEntryOriginal() = default;

View File

@ -13,9 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "history/history_item_components.h"
#include "history/history_location_manager.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_service.h"
#include "history/view/history_view_service_message.h"
#include "auth_session.h"
#include "boxes/share_box.h"
#include "boxes/confirm_box.h"
@ -1797,7 +1797,7 @@ void HistoryMessage::draw(Painter &p, QRect clip, TextSelection selection, TimeM
|| (_media && _media->skipBubbleTail())
|| (keyboard != nullptr);
auto displayTail = skipTail ? RectPart::None : (outbg && !Adaptive::ChatWide()) ? RectPart::Right : RectPart::Left;
HistoryLayout::paintBubble(p, g, width(), selected, outbg, displayTail);
HistoryView::paintBubble(p, g, width(), selected, outbg, displayTail);
// Entry page is always a bubble bottom.
auto mediaOnBottom = (mediaDisplayed && _media->isBubbleBottom()) || (entry/* && entry->_page->isBubbleBottom()*/);

View File

@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "apiwrap.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "history/history_item_components.h"
#include "history/view/history_view_service_message.h"
#include "data/data_feed.h"
#include "auth_session.h"
#include "window/notifications_manager.h"
@ -512,8 +512,8 @@ void HistoryService::draw(Painter &p, QRect clip, TextSelection selection, TimeM
height -= unreadbarh;
}
HistoryLayout::PaintContext context(ms, clip, selection);
HistoryLayout::ServiceMessagePainter::paint(p, this, context, height);
HistoryView::PaintContext context(ms, clip, selection);
HistoryView::ServiceMessagePainter::paint(p, this, context, height);
if (auto skiph = dateh + unreadbarh) {
p.translate(0, -skiph);

View File

@ -34,9 +34,9 @@ struct HistoryServiceSelfDestruct : public RuntimeComponent<HistoryServiceSelfDe
TimeMs destructAt = 0;
};
namespace HistoryLayout {
namespace HistoryView {
class ServiceMessagePainter;
} // namespace HistoryLayout
} // namespace HistoryView
class HistoryService : public HistoryItem, private HistoryItemInstantiated<HistoryService> {
public:
@ -102,7 +102,7 @@ public:
~HistoryService();
protected:
friend class HistoryLayout::ServiceMessagePainter;
friend class HistoryView::ServiceMessagePainter;
HistoryService(not_null<History*> history, const MTPDmessage &message);
HistoryService(not_null<History*> history, const MTPDmessageService &message);

View File

@ -24,11 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_drafts.h"
#include "data/data_session.h"
#include "history/history_message.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "history/history_drag_area.h"
#include "history/history_inner_widget.h"
#include "history/history_item_components.h"
#include "history/view/history_view_service_message.h"
#include "profile/profile_block_group_members.h"
#include "info/info_memento.h"
#include "core/click_handler_types.h"
@ -51,7 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/media_audio_capture.h"
#include "media/player/media_player_instance.h"
#include "apiwrap.h"
#include "history/history_top_bar_widget.h"
#include "history/view/history_view_top_bar_widget.h"
#include "observer_peer.h"
#include "base/qthelp_regex.h"
#include "ui/widgets/popup_menu.h"
@ -1704,7 +1704,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
noSelectingScroll();
_nonEmptySelection = false;
_topBar->showSelected(HistoryTopBarWidget::SelectedState {});
_topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
App::hoveredItem(nullptr);
App::pressedItem(nullptr);
@ -3039,7 +3039,7 @@ void HistoryWidget::showAnimated(
_a_show.start([this] { animationCallback(); }, 0., 1., st::slideDuration, Window::SlideAnimation::transition());
if (_history) {
_topBar->show();
_topBar->setAnimationMode(true);
_topBar->setAnimatingMode(true);
}
activate();
@ -3056,7 +3056,7 @@ void HistoryWidget::animationCallback() {
}
void HistoryWidget::doneShow() {
_topBar->setAnimationMode(false);
_topBar->setAnimatingMode(false);
updateReportSpamStatus();
updateBotKeyboard();
updateControlsVisibility();
@ -6142,7 +6142,7 @@ MessageIdsList HistoryWidget::getSelectedItems() const {
void HistoryWidget::updateTopBarSelection() {
if (!_list) {
_topBar->showSelected(HistoryTopBarWidget::SelectedState {});
_topBar->showSelected(HistoryView::TopBarWidget::SelectedState {});
return;
}
@ -6489,81 +6489,52 @@ void HistoryWidget::drawPinnedBar(Painter &p) {
}
}
bool HistoryWidget::paintShowAnimationFrame(TimeMs ms) {
auto progress = _a_show.current(ms, 1.);
if (!_a_show.animating()) {
return false;
}
Painter p(this);
auto animationWidth = width();
auto retina = cIntRetinaFactor();
auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft);
auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress);
auto coordOver = fromLeft ? anim::interpolate(0, animationWidth, progress) : anim::interpolate(animationWidth, 0, progress);
auto shadow = fromLeft ? (1. - progress) : progress;
if (coordOver > 0) {
p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, height() * retina));
p.setOpacity(shadow);
p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg);
p.setOpacity(1);
}
p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, 0, _cacheOver.width(), height() * retina));
p.setOpacity(shadow);
st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
return true;
}
void HistoryWidget::paintEvent(QPaintEvent *e) {
if (!App::main() || (App::wnd() && App::wnd()->contentOverlapped(this, e))) {
auto ms = getms();
_historyDownShown.step(ms);
_unreadMentionsShown.step(ms);
if (paintShowAnimationFrame(ms)) {
return;
}
if (Ui::skipPaintEvent(this, e)) {
return;
}
if (hasPendingResizedItems()) {
updateListSize();
}
Window::SectionWidget::PaintBackground(this, e);
Painter p(this);
QRect r(e->rect());
if (r != rect()) {
p.setClipRect(r);
}
auto ms = getms();
_historyDownShown.step(ms);
_unreadMentionsShown.step(ms);
auto progress = _a_show.current(ms, 1.);
if (_a_show.animating()) {
auto animationWidth = width();
auto retina = cIntRetinaFactor();
auto fromLeft = (_showDirection == Window::SlideDirection::FromLeft);
auto coordUnder = fromLeft ? anim::interpolate(-st::slideShift, 0, progress) : anim::interpolate(0, -st::slideShift, progress);
auto coordOver = fromLeft ? anim::interpolate(0, animationWidth, progress) : anim::interpolate(animationWidth, 0, progress);
auto shadow = fromLeft ? (1. - progress) : progress;
if (coordOver > 0) {
p.drawPixmap(QRect(0, 0, coordOver, height()), _cacheUnder, QRect(-coordUnder * retina, 0, coordOver * retina, height() * retina));
p.setOpacity(shadow);
p.fillRect(0, 0, coordOver, height(), st::slideFadeOutBg);
p.setOpacity(1);
}
p.drawPixmap(QRect(coordOver, 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, 0, _cacheOver.width(), height() * retina));
p.setOpacity(shadow);
st::slideShadow.fill(p, QRect(coordOver - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
return;
}
QRect fill(0, 0, width(), App::main()->height());
auto fromy = App::main()->backgroundFromY();
auto x = 0, y = 0;
QPixmap cached = App::main()->cachedBackground(fill, x, y);
if (cached.isNull()) {
if (Window::Theme::Background()->tile()) {
auto &pix = Window::Theme::Background()->pixmapForTiled();
auto left = r.left();
auto top = r.top();
auto right = r.left() + r.width();
auto bottom = r.top() + r.height();
auto w = pix.width() / cRetinaFactor();
auto h = pix.height() / cRetinaFactor();
auto sx = qFloor(left / w);
auto sy = qFloor((top - fromy) / h);
auto cx = qCeil(right / w);
auto cy = qCeil((bottom - fromy) / h);
for (auto i = sx; i < cx; ++i) {
for (auto j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
}
}
} else {
PainterHighQualityEnabler hq(p);
auto &pix = Window::Theme::Background()->pixmap();
QRect to, from;
Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from);
to.moveTop(to.top() + fromy);
p.drawPixmap(to, pix, from);
}
} else {
p.drawPixmap(x, fromy + y, cached);
}
const auto clip = e->rect();
if (_list) {
if (!_field->isHidden() || _recording) {
drawField(p, r);
drawField(p, clip);
if (!_send->isHidden() && _recording) {
drawRecording(p, _send->recordActiveRatio());
}
@ -6575,13 +6546,13 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
}
if (_scroll->isHidden()) {
p.setClipRect(_scroll->geometry());
HistoryLayout::paintEmpty(p, width(), height() - _field->height() - 2 * st::historySendPadding);
HistoryView::paintEmpty(p, width(), height() - _field->height() - 2 * st::historySendPadding);
}
} else {
style::font font(st::msgServiceFont);
int32 w = font->width(lang(lng_willbe_history)) + st::msgPadding.left() + st::msgPadding.right(), h = font->height + st::msgServicePadding.top() + st::msgServicePadding.bottom() + 2;
QRect tr((width() - w) / 2, (height() - _field->height() - 2 * st::historySendPadding - h) / 2, w, h);
HistoryLayout::ServiceMessagePainter::paintBubble(p, tr.x(), tr.y(), tr.width(), tr.height());
HistoryView::ServiceMessagePainter::paintBubble(p, tr.x(), tr.y(), tr.width(), tr.height());
p.setPen(st::msgServiceFg);
p.setFont(font->f);

View File

@ -65,12 +65,15 @@ enum class MimeDataState;
struct PreparedList;
} // namespace Storage
namespace HistoryView {
class TopBarWidget;
} // namespace HistoryView
class DragArea;
class SendFilesBox;
class BotKeyboard;
class MessageField;
class HistoryInner;
class HistoryTopBarWidget;
struct HistoryMessageMarkupButton;
class ReportSpamPanel : public TWidget {
@ -592,6 +595,7 @@ private:
void drawRecording(Painter &p, float64 recordActive);
void drawPinnedBar(Painter &p);
void drawRestrictedWrite(Painter &p);
bool paintShowAnimationFrame(TimeMs ms);
void updateMouseTracking();
@ -740,7 +744,7 @@ private:
MsgId _delayedShowAtMsgId = -1; // wtf?
mtpRequestId _delayedShowAtRequest = 0;
object_ptr<HistoryTopBarWidget> _topBar;
object_ptr<HistoryView::TopBarWidget> _topBar;
object_ptr<Ui::ScrollArea> _scroll;
QPointer<HistoryInner> _list;
History *_migrated = nullptr;

File diff suppressed because it is too large Load Diff

View 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

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_service_layout.h"
#include "history/view/history_view_service_message.h"
#include "history/history_service.h"
#include "history/history_media.h"
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "lang/lang_keys.h"
namespace HistoryLayout {
namespace HistoryView {
namespace {
enum CircleMask {
@ -359,4 +359,4 @@ void paintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool out
App::roundRect(p, rect, bg, cors, &sh, parts);
}
} // namespace HistoryLayout
} // namespace HistoryView

View File

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class HistoryService;
namespace HistoryLayout {
namespace HistoryView {
int WideChatWidth();
@ -49,4 +49,4 @@ void serviceColorsUpdated();
void paintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool outbg, RectPart tailSide);
} // namespace HistoryLayout
} // namespace HistoryView

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_top_bar_widget.h"
#include "history/view/history_view_top_bar_widget.h"
#include <rpl/combine.h>
#include <rpl/combine_previous.h>
@ -34,7 +34,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h"
#include "apiwrap.h"
HistoryTopBarWidget::HistoryTopBarWidget(
namespace HistoryView {
TopBarWidget::TopBarWidget(
QWidget *parent,
not_null<Window::Controller*> controller)
: RpWidget(parent)
@ -122,35 +124,35 @@ HistoryTopBarWidget::HistoryTopBarWidget(
updateControlsVisibility();
}
void HistoryTopBarWidget::refreshLang() {
void TopBarWidget::refreshLang() {
InvokeQueued(this, [this] { updateControlsGeometry(); });
}
void HistoryTopBarWidget::onForwardSelection() {
void TopBarWidget::onForwardSelection() {
if (App::main()) App::main()->forwardSelectedItems();
}
void HistoryTopBarWidget::onDeleteSelection() {
void TopBarWidget::onDeleteSelection() {
if (App::main()) App::main()->confirmDeleteSelectedItems();
}
void HistoryTopBarWidget::onClearSelection() {
void TopBarWidget::onClearSelection() {
if (App::main()) App::main()->clearSelectedItems();
}
void HistoryTopBarWidget::onSearch() {
void TopBarWidget::onSearch() {
if (_historyPeer) {
App::main()->searchInPeer(_historyPeer);
}
}
void HistoryTopBarWidget::onCall() {
void TopBarWidget::onCall() {
if (auto user = _historyPeer->asUser()) {
Calls::Current().startOutgoingCall(user);
}
}
void HistoryTopBarWidget::showMenu() {
void TopBarWidget::showMenu() {
if (!_historyPeer || _menu) {
return;
}
@ -184,7 +186,7 @@ void HistoryTopBarWidget::showMenu() {
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
}
void HistoryTopBarWidget::toggleInfoSection() {
void TopBarWidget::toggleInfoSection() {
if (Adaptive::ThreeColumn()
&& (Auth().settings().thirdSectionInfoEnabled()
|| Auth().settings().tabbedReplacedWithInfo())) {
@ -209,7 +211,7 @@ void HistoryTopBarWidget::toggleInfoSection() {
}
}
bool HistoryTopBarWidget::eventFilter(QObject *obj, QEvent *e) {
bool TopBarWidget::eventFilter(QObject *obj, QEvent *e) {
if (obj == _membersShowArea) {
switch (e->type()) {
case QEvent::MouseButtonPress:
@ -228,8 +230,8 @@ bool HistoryTopBarWidget::eventFilter(QObject *obj, QEvent *e) {
return TWidget::eventFilter(obj, e);
}
void HistoryTopBarWidget::paintEvent(QPaintEvent *e) {
if (_animationMode) {
void TopBarWidget::paintEvent(QPaintEvent *e) {
if (_animatingMode) {
return;
}
Painter p(this);
@ -247,7 +249,7 @@ void HistoryTopBarWidget::paintEvent(QPaintEvent *e) {
}
}
void HistoryTopBarWidget::paintTopBar(Painter &p, TimeMs ms) {
void TopBarWidget::paintTopBar(Painter &p, TimeMs ms) {
auto history = App::historyLoaded(_historyPeer);
if (!history) return;
@ -291,7 +293,7 @@ void HistoryTopBarWidget::paintTopBar(Painter &p, TimeMs ms) {
}
}
QRect HistoryTopBarWidget::getMembersShowAreaGeometry() const {
QRect TopBarWidget::getMembersShowAreaGeometry() const {
int membersTextLeft = _leftTaken;
int membersTextTop = st::topBarHeight - st::topBarArrowPadding.bottom() - st::dialogsTextFont->height;
int membersTextWidth = _titlePeerTextWidth;
@ -300,12 +302,12 @@ QRect HistoryTopBarWidget::getMembersShowAreaGeometry() const {
return myrtlrect(membersTextLeft, membersTextTop, membersTextWidth, membersTextHeight);
}
void HistoryTopBarWidget::mousePressEvent(QMouseEvent *e) {
void TopBarWidget::mousePressEvent(QMouseEvent *e) {
auto handleClick = (e->button() == Qt::LeftButton)
&& (e->pos().y() < st::topBarHeight)
&& (!_selectedCount);
if (handleClick) {
if (_animationMode && _back->rect().contains(e->pos())) {
if (_animatingMode && _back->rect().contains(e->pos())) {
backClicked();
} else if (_historyPeer) {
infoClicked();
@ -313,7 +315,7 @@ void HistoryTopBarWidget::mousePressEvent(QMouseEvent *e) {
}
}
void HistoryTopBarWidget::infoClicked() {
void TopBarWidget::infoClicked() {
if (_historyPeer && _historyPeer->isSelf()) {
_controller->showSection(Info::Memento(
_historyPeer->id,
@ -323,11 +325,11 @@ void HistoryTopBarWidget::infoClicked() {
}
}
void HistoryTopBarWidget::backClicked() {
void TopBarWidget::backClicked() {
_controller->showBackFromStack();
}
void HistoryTopBarWidget::setHistoryPeer(PeerData *historyPeer) {
void TopBarWidget::setHistoryPeer(PeerData *historyPeer) {
if (_historyPeer != historyPeer) {
_historyPeer = historyPeer;
_back->clearState();
@ -353,15 +355,15 @@ void HistoryTopBarWidget::setHistoryPeer(PeerData *historyPeer) {
}
}
void HistoryTopBarWidget::resizeEvent(QResizeEvent *e) {
void TopBarWidget::resizeEvent(QResizeEvent *e) {
updateControlsGeometry();
}
int HistoryTopBarWidget::countSelectedButtonsTop(float64 selectedShown) {
int TopBarWidget::countSelectedButtonsTop(float64 selectedShown) {
return (1. - selectedShown) * (-st::topBarHeight);
}
void HistoryTopBarWidget::updateControlsGeometry() {
void TopBarWidget::updateControlsGeometry() {
auto hasSelected = (_selectedCount > 0);
auto selectedButtonsTop = countSelectedButtonsTop(_selectedShown.current(hasSelected ? 1. : 0.));
auto otherButtonsTop = selectedButtonsTop + st::topBarHeight;
@ -417,22 +419,22 @@ void HistoryTopBarWidget::updateControlsGeometry() {
updateMembersShowArea();
}
void HistoryTopBarWidget::finishAnimating() {
void TopBarWidget::finishAnimating() {
_selectedShown.finish();
updateControlsVisibility();
}
void HistoryTopBarWidget::setAnimationMode(bool enabled) {
if (_animationMode != enabled) {
_animationMode = enabled;
setAttribute(Qt::WA_OpaquePaintEvent, !_animationMode);
void TopBarWidget::setAnimatingMode(bool enabled) {
if (_animatingMode != enabled) {
_animatingMode = enabled;
setAttribute(Qt::WA_OpaquePaintEvent, !_animatingMode);
finishAnimating();
updateControlsVisibility();
}
}
void HistoryTopBarWidget::updateControlsVisibility() {
if (_animationMode) {
void TopBarWidget::updateControlsVisibility() {
if (_animatingMode) {
hideChildren();
return;
}
@ -465,7 +467,7 @@ void HistoryTopBarWidget::updateControlsVisibility() {
updateControlsGeometry();
}
void HistoryTopBarWidget::updateMembersShowArea() {
void TopBarWidget::updateMembersShowArea() {
if (!App::main()) {
return;
}
@ -496,7 +498,7 @@ void HistoryTopBarWidget::updateMembersShowArea() {
_membersShowArea->setGeometry(getMembersShowAreaGeometry());
}
void HistoryTopBarWidget::showSelected(SelectedState state) {
void TopBarWidget::showSelected(SelectedState state) {
auto canDelete = (state.count > 0 && state.count == state.canDeleteCount);
auto canForward = (state.count > 0 && state.count == state.canForwardCount);
if (_selectedCount == state.count && _canDelete == canDelete && _canForward == canForward) {
@ -539,12 +541,12 @@ void HistoryTopBarWidget::showSelected(SelectedState state) {
}
}
void HistoryTopBarWidget::selectedShowCallback() {
void TopBarWidget::selectedShowCallback() {
updateControlsGeometry();
update();
}
void HistoryTopBarWidget::updateAdaptiveLayout() {
void TopBarWidget::updateAdaptiveLayout() {
updateControlsVisibility();
if (Adaptive::OneColumn()) {
createUnreadBadge();
@ -555,7 +557,7 @@ void HistoryTopBarWidget::updateAdaptiveLayout() {
updateInfoToggleActive();
}
void HistoryTopBarWidget::createUnreadBadge() {
void TopBarWidget::createUnreadBadge() {
if (_unreadBadge) {
return;
}
@ -569,7 +571,7 @@ void HistoryTopBarWidget::createUnreadBadge() {
updateUnreadBadge();
}
void HistoryTopBarWidget::updateUnreadBadge() {
void TopBarWidget::updateUnreadBadge() {
if (!_unreadBadge) return;
auto mutedCount = App::histories().unreadMutedCount();
@ -596,7 +598,7 @@ void HistoryTopBarWidget::updateUnreadBadge() {
}(), active);
}
void HistoryTopBarWidget::updateInfoToggleActive() {
void TopBarWidget::updateInfoToggleActive() {
auto infoThirdActive = Adaptive::ThreeColumn()
&& (Auth().settings().thirdSectionInfoEnabled()
|| Auth().settings().tabbedReplacedWithInfo());
@ -610,7 +612,7 @@ void HistoryTopBarWidget::updateInfoToggleActive() {
_infoToggle->setRippleColorOverride(rippleOverride);
}
void HistoryTopBarWidget::updateOnlineDisplay() {
void TopBarWidget::updateOnlineDisplay() {
if (!_historyPeer) return;
QString text;
@ -689,7 +691,7 @@ void HistoryTopBarWidget::updateOnlineDisplay() {
updateOnlineDisplayTimer();
}
void HistoryTopBarWidget::updateOnlineDisplayTimer() {
void TopBarWidget::updateOnlineDisplayTimer() {
if (!_historyPeer) return;
const auto now = unixtime();
@ -709,6 +711,8 @@ void HistoryTopBarWidget::updateOnlineDisplayTimer() {
updateOnlineDisplayIn(minTimeout);
}
void HistoryTopBarWidget::updateOnlineDisplayIn(TimeMs timeout) {
void TopBarWidget::updateOnlineDisplayIn(TimeMs timeout) {
_onlineUpdater.callOnce(timeout);
}
} // namespace HistoryView

View File

@ -22,9 +22,11 @@ namespace Window {
class Controller;
} // namespace Window
class HistoryTopBarWidget : public Ui::RpWidget, private base::Subscriber {
namespace HistoryView {
class TopBarWidget : public Ui::RpWidget, private base::Subscriber {
public:
HistoryTopBarWidget(
TopBarWidget(
QWidget *parent,
not_null<Window::Controller*> controller);
@ -41,15 +43,10 @@ public:
rpl::producer<bool> membersShowAreaActive() const {
return _membersShowAreaActive.events();
}
void setAnimationMode(bool enabled);
void setAnimatingMode(bool enabled);
void setHistoryPeer(PeerData *historyPeer);
static void paintUnreadCounter(
Painter &p,
int outerWidth,
PeerData *substractPeer = nullptr);
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
@ -116,9 +113,11 @@ private:
int _titlePeerTextWidth = 0;
int _leftTaken = 0;
int _rightTaken = 0;
bool _animationMode = false;
bool _animatingMode = false;
int _unreadCounterSubscription = 0;
base::Timer _onlineUpdater;
};
} // namespace HistoryView

View File

@ -791,8 +791,7 @@ void ListWidget::refreshViewer() {
idForViewer,
_idsLimit,
_idsLimit
) | rpl::start_with_next([=](
SparseIdsMergedSlice &&slice) {
) | rpl::start_with_next([=](SparseIdsMergedSlice &&slice) {
if (!slice.fullCount()) {
// Don't display anything while full count is unknown.
return;

View File

@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_widget.h"
#include "history/history_message.h"
#include "history/history_media.h"
#include "history/history_service_layout.h"
#include "history/view/history_view_service_message.h"
#include "lang/lang_keys.h"
#include "lang/lang_cloud_manager.h"
#include "boxes/add_contact_box.h"
@ -3530,7 +3530,7 @@ void MainWidget::updateWindowAdaptiveLayout() {
// auto thirdColumnWidth = _history->tabbedSelectorSectionWidth();
// auto twoColumnsWidth = (layout.bodyWidth - thirdColumnWidth);
// auto sameRatioChatWidth = twoColumnsWidth - qRound(dialogsWidthRatio * twoColumnsWidth);
// auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryLayout::WideChatWidth());
// auto desiredChatWidth = qMax(sameRatioChatWidth, HistoryView::WideChatWidth());
// chatWidth -= thirdColumnWidth;
// auto extendChatBy = desiredChatWidth - chatWidth;
// accumulate_min(extendChatBy, layout.dialogsWidth - st::columnMinimalWidthLeft);

View File

@ -503,9 +503,7 @@ auto GroupThumbs::validateCacheEntry(Key key) -> not_null<Thumb*> {
}
void GroupThumbs::markCacheStale() {
while (!_dying.empty()) {
_dying.pop_back();
}
_dying.clear();
for (const auto &cacheItem : _cache) {
const auto &thumb = cacheItem.second;
thumb->setState(Thumb::State::Unknown);

View File

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "profile/profile_back_button.h"
//#include "history/history_top_bar_widget.h"
//#include "history/view/history_view_top_bar_widget.h"
#include "styles/style_widgets.h"
#include "styles/style_window.h"
#include "styles/style_profile.h"
@ -41,8 +41,6 @@ void BackButton::paintEvent(QPaintEvent *e) {
p.setFont(st::topBarButton.font);
p.setPen(st::topBarButton.textFg);
p.drawTextLeft(st::topBarArrowPadding.left(), st::topBarButton.padding.top() + st::topBarButton.textTop, width(), _text);
// HistoryTopBarWidget::paintUnreadCounter(p, width());
}
void BackButton::onStateChanged(State was, StateChangeSource source) {

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_shared_media.h"
#include "storage/storage_user_photos.h"
#include "storage/storage_feed_messages.h"
namespace Storage {
@ -31,9 +32,20 @@ public:
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() const;
void add(FeedMessagesAddNew &&query);
void add(FeedMessagesAddSlice &&query);
void remove(FeedMessagesRemoveOne &&query);
void remove(FeedMessagesRemoveAll &&query);
rpl::producer<FeedMessagesResult> query(
FeedMessagesQuery &&query) const;
rpl::producer<FeedMessagesSliceUpdate> feedMessagesSliceUpdated() const;
rpl::producer<FeedMessagesRemoveOne> feedMessagesOneRemoved() const;
rpl::producer<FeedMessagesRemoveAll> feedMessagesAllRemoved() const;
private:
SharedMedia _sharedMedia;
UserPhotos _userPhotos;
FeedMessages _feedMessages;
};
@ -97,6 +109,39 @@ rpl::producer<UserPhotosSliceUpdate> Facade::Impl::userPhotosSliceUpdated() cons
return _userPhotos.sliceUpdated();
}
void Facade::Impl::add(FeedMessagesAddNew &&query) {
return _feedMessages.add(std::move(query));
}
void Facade::Impl::add(FeedMessagesAddSlice &&query) {
return _feedMessages.add(std::move(query));
}
void Facade::Impl::remove(FeedMessagesRemoveOne &&query) {
return _feedMessages.remove(std::move(query));
}
void Facade::Impl::remove(FeedMessagesRemoveAll &&query) {
return _feedMessages.remove(std::move(query));
}
rpl::producer<FeedMessagesResult> Facade::Impl::query(
FeedMessagesQuery &&query) const {
return _feedMessages.query(std::move(query));
}
rpl::producer<FeedMessagesSliceUpdate> Facade::Impl::feedMessagesSliceUpdated() const {
return _feedMessages.sliceUpdated();
}
rpl::producer<FeedMessagesRemoveOne> Facade::Impl::feedMessagesOneRemoved() const {
return _feedMessages.oneRemoved();
}
rpl::producer<FeedMessagesRemoveAll> Facade::Impl::feedMessagesAllRemoved() const {
return _feedMessages.allRemoved();
}
Facade::Facade() : _impl(std::make_unique<Impl>()) {
}
@ -160,6 +205,39 @@ rpl::producer<UserPhotosSliceUpdate> Facade::userPhotosSliceUpdated() const {
return _impl->userPhotosSliceUpdated();
}
void Facade::add(FeedMessagesAddNew &&query) {
return _impl->add(std::move(query));
}
void Facade::add(FeedMessagesAddSlice &&query) {
return _impl->add(std::move(query));
}
void Facade::remove(FeedMessagesRemoveOne &&query) {
return _impl->remove(std::move(query));
}
void Facade::remove(FeedMessagesRemoveAll &&query) {
return _impl->remove(std::move(query));
}
rpl::producer<FeedMessagesResult> Facade::query(
FeedMessagesQuery &&query) const {
return _impl->query(std::move(query));
}
rpl::producer<FeedMessagesSliceUpdate> Facade::feedMessagesSliceUpdated() const {
return _impl->feedMessagesSliceUpdated();
}
rpl::producer<FeedMessagesRemoveOne> Facade::feedMessagesOneRemoved() const {
return _impl->feedMessagesOneRemoved();
}
rpl::producer<FeedMessagesRemoveAll> Facade::feedMessagesAllRemoved() const {
return _impl->feedMessagesAllRemoved();
}
Facade::~Facade() = default;
} // namespace Storage

View File

@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/producer.h>
#include "base/enum_mask.h"
namespace Data {
struct MessagesResult;
} // namespace Data
namespace Storage {
struct SparseIdsListResult;
@ -31,6 +35,14 @@ struct UserPhotosQuery;
struct UserPhotosResult;
struct UserPhotosSliceUpdate;
struct FeedMessagesAddNew;
struct FeedMessagesAddSlice;
struct FeedMessagesRemoveOne;
struct FeedMessagesRemoveAll;
struct FeedMessagesQuery;
using FeedMessagesResult = Data::MessagesResult;
struct FeedMessagesSliceUpdate;
class Facade {
public:
Facade();
@ -54,6 +66,17 @@ public:
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() const;
void add(FeedMessagesAddNew &&query);
void add(FeedMessagesAddSlice &&query);
void remove(FeedMessagesRemoveOne &&query);
void remove(FeedMessagesRemoveAll &&query);
rpl::producer<FeedMessagesResult> query(
FeedMessagesQuery &&query) const;
rpl::producer<FeedMessagesSliceUpdate> feedMessagesSliceUpdated() const;
rpl::producer<FeedMessagesRemoveOne> feedMessagesOneRemoved() const;
rpl::producer<FeedMessagesRemoveAll> feedMessagesAllRemoved() const;
~Facade();
private:

View 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

View 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

View File

@ -56,6 +56,7 @@ void SharedMedia::add(SharedMediaAddExisting &&query) {
void SharedMedia::add(SharedMediaAddSlice &&query) {
Expects(IsValidSharedMediaType(query.type));
auto peerIt = enforceLists(query.peerId);
auto index = static_cast<int>(query.type);
peerIt->second[index].addSlice(

View File

@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/range.h>
#include "application.h"
#include "mainwidget.h"
#include "window/section_memento.h"
#include "window/window_slide_animation.h"
#include "window/themes/window_theme.h"
namespace Window {
@ -71,6 +73,46 @@ void SectionWidget::showFast() {
showFinished();
}
void SectionWidget::PaintBackground(QWidget *widget, QPaintEvent *event) {
Painter p(widget);
auto clip = event->rect();
auto fill = QRect(0, 0, widget->width(), App::main()->height());
auto fromy = App::main()->backgroundFromY();
auto x = 0, y = 0;
auto cached = App::main()->cachedBackground(fill, x, y);
if (cached.isNull()) {
if (Window::Theme::Background()->tile()) {
auto &pix = Window::Theme::Background()->pixmapForTiled();
auto left = clip.left();
auto top = clip.top();
auto right = clip.left() + clip.width();
auto bottom = clip.top() + clip.height();
auto w = pix.width() / cRetinaFactor();
auto h = pix.height() / cRetinaFactor();
auto sx = qFloor(left / w);
auto sy = qFloor((top - fromy) / h);
auto cx = qCeil(right / w);
auto cy = qCeil((bottom - fromy) / h);
for (auto i = sx; i < cx; ++i) {
for (auto j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
}
}
} else {
PainterHighQualityEnabler hq(p);
auto &pix = Window::Theme::Background()->pixmap();
QRect to, from;
Window::Theme::ComputeBackgroundRects(fill, pix.size(), to, from);
to.moveTop(to.top() + fromy);
p.drawPixmap(to, pix, from);
}
} else {
p.drawPixmap(x, fromy + y, cached);
}
}
void SectionWidget::paintEvent(QPaintEvent *e) {
if (_showAnimation) {
Painter p(this);

View File

@ -126,6 +126,8 @@ public:
return false;
}
static void PaintBackground(QWidget *widget, QPaintEvent *event);
protected:
void paintEvent(QPaintEvent *e) override;

View File

@ -169,8 +169,12 @@
<(src_loc)/data/data_drafts.h
<(src_loc)/data/data_feed.cpp
<(src_loc)/data/data_feed.h
<(src_loc)/data/data_feed_messages.cpp
<(src_loc)/data/data_feed_messages.h
<(src_loc)/data/data_flags.h
<(src_loc)/data/data_game.h
<(src_loc)/data/data_messages.cpp
<(src_loc)/data/data_messages.h
<(src_loc)/data/data_notify_settings.cpp
<(src_loc)/data/data_notify_settings.h
<(src_loc)/data/data_peer.cpp
@ -211,16 +215,24 @@
<(src_loc)/dialogs/dialogs_search_from_controllers.h
<(src_loc)/dialogs/dialogs_widget.cpp
<(src_loc)/dialogs/dialogs_widget.h
<(src_loc)/history/admin_log/history_admin_log_filter.cpp
<(src_loc)/history/admin_log/history_admin_log_filter.h
<(src_loc)/history/admin_log/history_admin_log_inner.cpp
<(src_loc)/history/admin_log/history_admin_log_inner.h
<(src_loc)/history/admin_log/history_admin_log_item.cpp
<(src_loc)/history/admin_log/history_admin_log_item.h
<(src_loc)/history/admin_log/history_admin_log_section.cpp
<(src_loc)/history/admin_log/history_admin_log_section.h
<(src_loc)/history/feed/history_feed_section.cpp
<(src_loc)/history/feed/history_feed_section.h
<(src_loc)/history/view/history_view_list_widget.cpp
<(src_loc)/history/view/history_view_list_widget.h
<(src_loc)/history/view/history_view_service_message.cpp
<(src_loc)/history/view/history_view_service_message.h
<(src_loc)/history/view/history_view_top_bar_widget.cpp
<(src_loc)/history/view/history_view_top_bar_widget.h
<(src_loc)/history/history.cpp
<(src_loc)/history/history.h
<(src_loc)/history/history_admin_log_filter.cpp
<(src_loc)/history/history_admin_log_filter.h
<(src_loc)/history/history_admin_log_inner.cpp
<(src_loc)/history/history_admin_log_inner.h
<(src_loc)/history/history_admin_log_item.cpp
<(src_loc)/history/history_admin_log_item.h
<(src_loc)/history/history_admin_log_section.cpp
<(src_loc)/history/history_admin_log_section.h
<(src_loc)/history/history_drag_area.cpp
<(src_loc)/history/history_drag_area.h
<(src_loc)/history/history_item.cpp
@ -241,10 +253,6 @@
<(src_loc)/history/history_message.h
<(src_loc)/history/history_service.cpp
<(src_loc)/history/history_service.h
<(src_loc)/history/history_service_layout.cpp
<(src_loc)/history/history_service_layout.h
<(src_loc)/history/history_top_bar_widget.cpp
<(src_loc)/history/history_top_bar_widget.h
<(src_loc)/history/history_widget.cpp
<(src_loc)/history/history_widget.h
<(src_loc)/info/info_content_widget.cpp
@ -531,6 +539,8 @@
<(src_loc)/storage/serialize_document.h
<(src_loc)/storage/storage_facade.cpp
<(src_loc)/storage/storage_facade.h
<(src_loc)/storage/storage_feed_messages.cpp
<(src_loc)/storage/storage_feed_messages.h
<(src_loc)/storage/storage_media_prepare.cpp
<(src_loc)/storage/storage_media_prepare.h
<(src_loc)/storage/storage_shared_media.cpp