From 2363a6bd4411505551fd8a66d9686ff1a93f0a5f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 18 Aug 2017 22:14:31 +0300 Subject: [PATCH] Add SharedMediaSlice to observe shared media. Start testing / using it in MediaView. --- Telegram/SourceFiles/apiwrap.cpp | 153 ++++ Telegram/SourceFiles/apiwrap.h | 46 +- Telegram/SourceFiles/auth_session.h | 2 +- Telegram/SourceFiles/base/observer.h | 4 +- Telegram/SourceFiles/config.h | 1 - .../history/history_shared_media.cpp | 409 +++++++++ .../history/history_shared_media.h | 316 +++++++ Telegram/SourceFiles/mediaview.cpp | 804 +++++++++--------- Telegram/SourceFiles/mediaview.h | 33 +- Telegram/SourceFiles/overviewwidget.cpp | 2 +- .../SourceFiles/storage/storage_facade.cpp | 27 + Telegram/SourceFiles/storage/storage_facade.h | 5 + .../storage/storage_shared_media.cpp | 109 ++- .../storage/storage_shared_media.h | 85 +- Telegram/gyp/telegram_sources.txt | 2 + 15 files changed, 1548 insertions(+), 450 deletions(-) create mode 100644 Telegram/SourceFiles/history/history_shared_media.cpp create mode 100644 Telegram/SourceFiles/history/history_shared_media.h diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 72dc4655a..9fd29eb9e 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -35,6 +35,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/notifications_manager.h" #include "chat_helpers/message_field.h" #include "chat_helpers/stickers.h" +#include "storage/storage_facade.h" +#include "storage/storage_shared_media.h" namespace { @@ -46,6 +48,7 @@ constexpr auto kStickersUpdateTimeout = 3600000; // update not more than once in constexpr auto kUnreadMentionsPreloadIfLess = 5; constexpr auto kUnreadMentionsFirstRequestLimit = 10; constexpr auto kUnreadMentionsNextRequestLimit = 100; +constexpr auto kSharedMediaLimit = 10; } // namespace @@ -1911,4 +1914,154 @@ void ApiWrap::sendSaveChatAdminsRequests(not_null<ChatData*> chat) { requestSendDelayed(); } +void ApiWrap::requestSharedMedia( + not_null<PeerData*> peer, + SharedMediaType type, + MsgId messageId, + SliceType slice) { + auto key = std::make_tuple(peer, type, messageId, slice); + if (_sharedMediaRequests.contains(key)) { + return; + } + + auto filter = [&] { + using Type = SharedMediaType; + switch (type) { + case Type::Photo: return MTP_inputMessagesFilterPhotos(); + case Type::Video: return MTP_inputMessagesFilterVideo(); + case Type::MusicFile: return MTP_inputMessagesFilterMusic(); + case Type::File: return MTP_inputMessagesFilterDocument(); + case Type::VoiceFile: return MTP_inputMessagesFilterVoice(); + case Type::RoundVoiceFile: return MTP_inputMessagesFilterRoundVoice(); + case Type::GIF: return MTP_inputMessagesFilterGif(); + case Type::Link: return MTP_inputMessagesFilterUrl(); + case Type::ChatPhoto: return MTP_inputMessagesFilterChatPhotos(); + } + return MTP_inputMessagesFilterEmpty(); + }(); + if (filter.type() == mtpc_inputMessagesFilterEmpty) { + return; + } + + auto minId = 0; + auto maxId = 0; + auto limit = messageId ? kSharedMediaLimit : 0; + auto offsetId = [&] { + switch (slice) { + case SliceType::Before: + case SliceType::Around: return messageId; + case SliceType::After: return messageId + 1; + } + Unexpected("Slice type in ApiWrap::requestSharedMedia"); + }(); + auto addOffset = [&] { + switch (slice) { + case SliceType::Before: return 0; + case SliceType::Around: return -limit / 2; + case SliceType::After: return -limit; + } + Unexpected("Slice type in ApiWrap::requestSharedMedia"); + }(); + + LOG(("REQUESTING SHARED MEDIA: %1, %2, %3").arg(static_cast<int>(type)).arg(messageId).arg(static_cast<int>(slice))); + auto requestId = request(MTPmessages_Search( + MTP_flags(0), + peer->input, + MTPstring(), + MTP_inputUserEmpty(), + filter, + MTP_int(0), + MTP_int(0), + MTP_int(offsetId), + MTP_int(addOffset), + MTP_int(limit), + MTP_int(maxId), + MTP_int(minId) + )).done([this, peer, type, messageId, slice](const MTPmessages_Messages &result) { + _sharedMediaRequests.remove(std::make_tuple(peer, type, messageId, slice)); + sharedMediaDone(peer, type, messageId, slice, result); + }).fail([this, key](const RPCError &error) { + _sharedMediaRequests.remove(key); + }).send(); + _sharedMediaRequests.emplace(key, requestId); +} + +void ApiWrap::sharedMediaDone( + not_null<PeerData*> peer, + SharedMediaType type, + MsgId messageId, + SliceType slice, + const MTPmessages_Messages &result) { + + auto fullCount = 0; + auto &messages = *[&] { + switch (result.type()) { + case mtpc_messages_messages: { + auto &d = result.c_messages_messages(); + App::feedUsers(d.vusers); + App::feedChats(d.vchats); + fullCount = d.vmessages.v.size(); + return &d.vmessages.v; + } break; + + case mtpc_messages_messagesSlice: { + auto &d = result.c_messages_messagesSlice(); + App::feedUsers(d.vusers); + App::feedChats(d.vchats); + fullCount = d.vcount.v; + return &d.vmessages.v; + } break; + + case mtpc_messages_channelMessages: { + auto &d = result.c_messages_channelMessages(); + if (auto channel = peer->asChannel()) { + channel->ptsReceived(d.vpts.v); + } else { + LOG(("API Error: received messages.channelMessages when no channel was passed! (ApiWrap::sharedMediaDone)")); + } + App::feedUsers(d.vusers); + App::feedChats(d.vchats); + fullCount = d.vcount.v; + return &d.vmessages.v; + } break; + } + Unexpected("messages.Messages type in sharedMediaDone()"); + }(); + + auto noSkipRange = MsgRange { messageId, messageId }; + auto messageIds = std::vector<MsgId>(); + auto addType = NewMessageExisting; + messageIds.reserve(messages.size()); + for (auto &message : messages) { + if (auto item = App::histories().addNewMessage(message, addType)) { + if (item->sharedMediaTypes().test(type)) { + auto itemId = item->id; + messageIds.push_back(itemId); + accumulate_min(noSkipRange.from, itemId); + accumulate_max(noSkipRange.till, itemId); + } + } + } + if (messageId && messageIds.empty()) { + noSkipRange = [&]() -> MsgRange { + switch (slice) { + case SliceType::Before: // All old loaded. + return { 0, noSkipRange.till }; + case SliceType::Around: // All loaded. + return { 0, ServerMaxMsgId }; + case SliceType::After: // All new loaded. + return { noSkipRange.from, ServerMaxMsgId }; + } + Unexpected("Slice type in ApiWrap::sharedMediaDone"); + }(); + } + Auth().storage().add(Storage::SharedMediaAddSlice( + peer->id, + type, + std::move(messageIds), + noSkipRange, + fullCount + )); +} + ApiWrap::~ApiWrap() = default; diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 7b7a94b8f..d71493569 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -28,6 +28,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org class AuthSession; +namespace Storage { +enum class SharedMediaType : char; +} // namespace Storage + namespace Api { inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) { @@ -108,6 +112,22 @@ public: bool adminsEnabled, base::flat_set<not_null<UserData*>> &&admins); + enum class SliceType { + Around, + Before, + After, + }; + void requestSharedMedia( + not_null<PeerData*> peer, + Storage::SharedMediaType type, + MsgId messageId, + SliceType slice); + void requestSharedMediaCount( + not_null<PeerData*> peer, + Storage::SharedMediaType type) { + requestSharedMedia(peer, type, 0, SliceType::Before); + } + ~ApiWrap(); private: @@ -117,6 +137,7 @@ private: Callbacks callbacks; }; using MessageDataRequests = QMap<MsgId, MessageDataRequest>; + using SharedMediaType = Storage::SharedMediaType; void requestAppChangelogs(); void addLocalChangelogs(int oldAppVersion); @@ -153,6 +174,13 @@ private: void saveChatAdmins(not_null<ChatData*> chat); void sendSaveChatAdminsRequests(not_null<ChatData*> chat); + void sharedMediaDone( + not_null<PeerData*> peer, + SharedMediaType type, + MsgId messageId, + SliceType slice, + const MTPmessages_Messages &result); + not_null<AuthSession*> _session; mtpRequestId _changelogSubscription = 0; @@ -205,9 +233,21 @@ private: base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests; - base::flat_map<not_null<ChatData*>, mtpRequestId> _chatAdminsEnabledRequests; - base::flat_map<not_null<ChatData*>, base::flat_set<not_null<UserData*>>> _chatAdminsToSave; - base::flat_map<not_null<ChatData*>, base::flat_set<mtpRequestId>> _chatAdminsSaveRequests; + base::flat_map< + not_null<ChatData*>, + mtpRequestId> _chatAdminsEnabledRequests; + base::flat_map< + not_null<ChatData*>, + base::flat_set<not_null<UserData*>>> _chatAdminsToSave; + base::flat_map< + not_null<ChatData*>, + base::flat_set<mtpRequestId>> _chatAdminsSaveRequests; + + base::flat_map<std::tuple< + not_null<PeerData*>, + SharedMediaType, + MsgId, + SliceType>, mtpRequestId> _sharedMediaRequests; base::Observable<PeerData*> _fullPeerUpdated; diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h index 9430b67c1..8f9134b0c 100644 --- a/Telegram/SourceFiles/auth_session.h +++ b/Telegram/SourceFiles/auth_session.h @@ -227,7 +227,7 @@ public: void checkAutoLockIn(TimeMs time); base::Observable<DocumentData*> documentUpdated; - base::Observable<std::pair<HistoryItem*, MsgId>> messageIdChanging; + base::Observable<std::pair<not_null<HistoryItem*>, MsgId>> messageIdChanging; ~AuthSession(); diff --git a/Telegram/SourceFiles/base/observer.h b/Telegram/SourceFiles/base/observer.h index 362c6d42b..24de58399 100644 --- a/Telegram/SourceFiles/base/observer.h +++ b/Telegram/SourceFiles/base/observer.h @@ -366,9 +366,9 @@ class Observable : public internal::BaseObservable<EventType, Handler, base::typ public: Observable() = default; Observable(const Observable &other) = delete; - Observable(Observable &&other) = delete; + Observable(Observable &&other) = default; Observable &operator=(const Observable &other) = delete; - Observable &operator=(Observable &&other) = delete; + Observable &operator=(Observable &&other) = default; }; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 633c4e4a6..0e87f9549 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -87,7 +87,6 @@ enum { SearchManyPerPage = 100, LinksOverviewPerPage = 12, MediaOverviewStartPerPage = 5, - MediaOverviewPreloadCount = 4, AudioVoiceMsgMaxLength = 100 * 60, // 100 minutes AudioVoiceMsgUpdateView = 100, // 100ms diff --git a/Telegram/SourceFiles/history/history_shared_media.cpp b/Telegram/SourceFiles/history/history_shared_media.cpp new file mode 100644 index 000000000..774fb9d83 --- /dev/null +++ b/Telegram/SourceFiles/history/history_shared_media.cpp @@ -0,0 +1,409 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "history/history_shared_media.h" + +#include "auth_session.h" +#include "apiwrap.h" +#include "storage/storage_facade.h" +#include "storage/storage_shared_media.h" + +namespace { + +using Type = SharedMediaViewer::Type; + +inline MediaOverviewType SharedMediaTypeToOverview(Type type) { + switch (type) { + case Type::Photo: return OverviewPhotos; + case Type::Video: return OverviewVideos; + case Type::MusicFile: return OverviewMusicFiles; + case Type::File: return OverviewFiles; + case Type::VoiceFile: return OverviewVoiceFiles; + case Type::Link: return OverviewLinks; + default: break; + } + return OverviewCount; +} + +not_null<History*> GetActualHistory(not_null<History*> history) { + if (auto to = history->peer->migrateTo()) { + return App::history(to); + } + return history; +} + +History *GetMigratedHistory( + not_null<History*> passedHistory, + not_null<History*> actualHistory) { + if (actualHistory != passedHistory) { + return passedHistory; + } else if (auto from = actualHistory->peer->migrateFrom()) { + return App::history(from); + } + return nullptr; +} + +} // namespace + +SharedMediaViewer::SharedMediaViewer( + Key key, + int limitBefore, + int limitAfter) + : _key(key) + , _limitBefore(limitBefore) + , _limitAfter(limitAfter) + , _data(_key) { +} + +base::optional<Storage::SharedMediaType> SharedMediaOverviewType( + Storage::SharedMediaType type) { + if (SharedMediaTypeToOverview(type) != OverviewCount) { + return type; + } + return base::none; +} + +void SharedMediaShowOverview( + Storage::SharedMediaType type, + not_null<History*> history) { + if (SharedMediaOverviewType(type)) { + Ui::showPeerOverview(history, SharedMediaTypeToOverview(type)); + } +} + +void SharedMediaViewer::start() { + auto applyUpdateCallback = [this](auto &update) { + this->applyUpdate(update); + }; + subscribe(Auth().storage().sharedMediaSliceUpdated(), applyUpdateCallback); + subscribe(Auth().storage().sharedMediaOneRemoved(), applyUpdateCallback); + subscribe(Auth().storage().sharedMediaAllRemoved(), applyUpdateCallback); + + loadInitial(); +} + +void SharedMediaViewer::loadInitial() { + auto weak = base::make_weak_unique(this); + Auth().storage().query(Storage::SharedMediaQuery( + _key, + _limitBefore, + _limitAfter), [weak](Storage::SharedMediaResult &&result) { + if (weak) { + weak->applyStoredResult(std::move(result)); + } + }); +} + +void SharedMediaViewer::applyStoredResult(Storage::SharedMediaResult &&result) { + mergeSliceData( + result.count, + result.messageIds, + result.skippedBefore, + result.skippedAfter); +} + +void SharedMediaViewer::mergeSliceData( + base::optional<int> count, + const base::flat_set<MsgId> &messageIds, + base::optional<int> skippedBefore, + base::optional<int> skippedAfter) { + if (messageIds.empty()) { + if (count && *_data._fullCount != *count) { + _data._fullCount = count; + updated.notify(_data); + } + return; + } + if (count) { + _data._fullCount = count; + } + auto wasMinId = _data._ids.empty() ? -1 : _data._ids.front(); + auto wasMaxId = _data._ids.empty() ? -1 : _data._ids.back(); + _data._ids.merge(messageIds.begin(), messageIds.end()); + + auto adjustSkippedBefore = [&](MsgId oldId, int oldSkippedBefore) { + auto it = _data._ids.find(oldId); + Assert(it != _data._ids.end()); + _data._skippedBefore = oldSkippedBefore - (it - _data._ids.begin()); + accumulate_max(*_data._skippedBefore, 0); + }; + if (skippedBefore) { + adjustSkippedBefore(messageIds.front(), *skippedBefore); + } else if (wasMinId >= 0 && _data._skippedBefore) { + adjustSkippedBefore(wasMinId, *_data._skippedBefore); + } else { + _data._skippedBefore = base::none; + } + + auto adjustSkippedAfter = [&](MsgId oldId, int oldSkippedAfter) { + auto it = _data._ids.find(oldId); + Assert(it != _data._ids.end()); + _data._skippedAfter = oldSkippedAfter - (_data._ids.end() - it - 1); + accumulate_max(*_data._skippedAfter, 0); + }; + if (skippedAfter) { + adjustSkippedAfter(messageIds.back(), *skippedAfter); + } else if (wasMaxId >= 0 && _data._skippedAfter) { + adjustSkippedAfter(wasMaxId, *_data._skippedAfter); + } else { + _data._skippedAfter = base::none; + } + + if (_data._fullCount) { + if (_data._skippedBefore && !_data._skippedAfter) { + _data._skippedAfter = *_data._fullCount + - *_data._skippedBefore + - int(_data._ids.size()); + } else if (_data._skippedAfter && !_data._skippedBefore) { + _data._skippedBefore = *_data._fullCount + - *_data._skippedAfter + - int(_data._ids.size()); + } + } + + sliceToLimits(); + + updated.notify(_data); +} + +void SharedMediaViewer::applyUpdate(const SliceUpdate &update) { + if (update.peerId != _key.peerId || update.type != _key.type) { + return; + } + auto intersects = [](MsgRange range1, MsgRange range2) { + return (range1.from <= range2.till) && (range2.from <= range1.till); + }; + if (!intersects(update.range, { + _data._ids.empty() ? _key.messageId : _data._ids.front(), + _data._ids.empty() ? _key.messageId : _data._ids.back() })) { + return; + } + auto skippedBefore = (update.range.from == 0) + ? 0 + : base::optional<int> {}; + auto skippedAfter = (update.range.till == ServerMaxMsgId) + ? 0 + : base::optional<int> {}; + mergeSliceData( + update.count, + update.messages ? *update.messages : base::flat_set<MsgId> {}, + skippedBefore, + skippedAfter); +} + +void SharedMediaViewer::applyUpdate(const OneRemoved &update) { + if (update.peerId != _key.peerId || !update.types.test(_key.type)) { + return; + } + auto changed = false; + if (_data._fullCount && *_data._fullCount > 0) { + --*_data._fullCount; + changed = true; + } + if (_data._ids.contains(update.messageId)) { + _data._ids.remove(update.messageId); + changed = true; + } else if (!_data._ids.empty()) { + if (_data._ids.front() > update.messageId + && _data._skippedBefore + && *_data._skippedBefore > 0) { + --*_data._skippedBefore; + changed = true; + } else if (_data._ids.back() < update.messageId + && _data._skippedAfter + && *_data._skippedAfter > 0) { + --*_data._skippedAfter; + changed = true; + } + } + if (changed) { + updated.notify(_data); + } +} + +void SharedMediaViewer::applyUpdate(const AllRemoved &update) { + if (update.peerId != _key.peerId) { + return; + } + _data = SharedMediaSlice(_key, 0); + updated.notify(_data); +} + +void SharedMediaViewer::sliceToLimits() { + auto aroundIt = base::lower_bound(_data._ids, _key.messageId); + auto removeFromBegin = (aroundIt - _data._ids.begin() - _limitBefore); + auto removeFromEnd = (_data._ids.end() - aroundIt - _limitAfter - 1); + if (removeFromBegin > 0) { + _data._ids.erase(_data._ids.begin(), _data._ids.begin() + removeFromBegin); + if (_data._skippedBefore) { + *_data._skippedBefore += removeFromBegin; + } + } else if (removeFromBegin < 0 && (!_data._skippedBefore || *_data._skippedBefore > 0)) { + requestMessages(RequestDirection::Before); + } + if (removeFromEnd > 0) { + _data._ids.erase(_data._ids.end() - removeFromEnd, _data._ids.end()); + if (_data._skippedAfter) { + *_data._skippedAfter += removeFromEnd; + } + } else if (removeFromEnd < 0 && (!_data._skippedAfter || *_data._skippedAfter > 0)) { + requestMessages(RequestDirection::After); + } +} + +void SharedMediaViewer::requestMessages(RequestDirection direction) { + using SliceType = ApiWrap::SliceType; + auto requestAroundData = [&]() -> std::pair<MsgId, SliceType> { + if (_data._ids.empty()) { + return { _key.messageId, SliceType::Around }; + } else if (direction == RequestDirection::Before) { + return { _data._ids.front(), SliceType::Before }; + } + return { _data._ids.back(), SliceType::After }; + }(); + Auth().api().requestSharedMedia( + App::peer(_key.peerId), + _key.type, + requestAroundData.first, + requestAroundData.second); +} + +// +//base::optional<int> SharedMediaViewerMerged::Data::fullCount() const { +// if (_historyCount && _migratedCount) { +// return (*_historyCount + *_migratedCount); +// } +// return base::none; +//} +//base::optional<int> SharedMediaViewerMerged::Data::skippedBefore() const { +// if (_ids.empty()) { +// return base::none; +// } else if (!IsServerMsgId(_ids.front())) { +// return _migratedSkippedBefore; +// } else if (_historySkippedBefore && _migratedCount) { +// return *_historySkippedBefore + *_migratedCount; +// } +// return base::none; +//} +// +//base::optional<int> SharedMediaViewerMerged::Data::skippedAfter() const { +// if (_ids.empty()) { +// return base::none; +// } else if (IsServerMsgId(_ids.back())) { +// return _historySkippedAfter; +// } else if (_migratedSkippedAfter && _historyCount) { +// return *_migratedSkippedAfter + *_historyCount; +// } +// return base::none; +//} +// +//SharedMediaViewerMerged::SharedMediaViewerMerged( +// Type type, +// not_null<History*> history, +// MsgId aroundId, +// int limitBefore, +// int limitAfter) +//: _type(type) +//, _history(GetActualHistory(history)) +//, _migrated(GetMigratedHistory(history, _history)) +//, _universalAroundId((_history == _migrated) ? -aroundId : aroundId) +//, _limitBefore(limitBefore) +//, _limitAfter(limitAfter) +//, _data(_history, _migrated) { +//} +// +//bool SharedMediaViewerMerged::hasOverview() const { +// return SharedMediaTypeToOverview(_type) != OverviewCount; +//} +// +//void SharedMediaViewerMerged::showOverview() const { +// if (hasOverview()) { +// Ui::showPeerOverview(_history, SharedMediaTypeToOverview(_type)); +// } +//} +// +//bool SharedMediaViewerMerged::moveTo(const SharedMediaViewerMerged &other) { +// if (_history != other._history || _type != other._type) { +// return false; +// } +// _universalAroundId = other._universalAroundId; +// if (!containsAroundId()) { +// clearAfterMove(); +// } +// load(); +// return true; +//} +// +//bool SharedMediaViewerMerged::containsAroundId() const { +// if (_data._ids.empty()) { +// return false; +// } +// auto min = _data._ids.front(); +// auto max = _data._ids.back(); +// if (IsServerMsgId(_universalAroundId)) { +// return (!IsServerMsgId(min) || min <= aroundId()) +// && (IsServerMsgId(max) && max >= aroundId()); +// } +// return (!IsServerMsgId(min) && -min <= aroundId()) +// && (IsServerMsgId(max) || -max >= aroundId()); +//} +// +//bool SharedMediaViewerMerged::amAroundMigrated() const { +// return !IsServerMsgId(_universalAroundId); +//} +// +//not_null<History*> SharedMediaViewerMerged::aroundHistory() const { +// return amAroundMigrated() ? _migrated : _history; +//} +// +//MsgId SharedMediaViewerMerged::aroundId() const { +// return amAroundMigrated() ? -_universalAroundId : _universalAroundId; +//} +// +//void SharedMediaViewerMerged::clearAfterMove() { +// _data = Data(_history, _migrated, _data._historyCount, _data._migratedCount); +//} +// +//void SharedMediaViewerMerged::load() { +// auto weak = base::make_weak_unique(this); +// auto peer = aroundHistory()->peer; +// Auth().storage().query(Storage::SharedMediaQuery( +// peer->id, +// _type, +// aroundId(), +// _limitBefore, +// _limitAfter), [weak, peer](Storage::SharedMediaResult &&result) { +// if (weak) { +// weak->applyStoredResult(peer, std::move(result)); +// } +// }); +//} +// +//void SharedMediaViewerMerged::applyStoredResult( +// not_null<PeerData*> peer, +// Storage::SharedMediaResult &&result) { +// if (aroundHistory()->peer != peer) { +// return; +// } +// auto aroundMigrated = amAroundMigrated(); +// if (result.count) { +// (aroundMigrated ? _data._migratedCount : _data._historyCount) = result.count; +// } +//} diff --git a/Telegram/SourceFiles/history/history_shared_media.h b/Telegram/SourceFiles/history/history_shared_media.h new file mode 100644 index 000000000..bc94d74ca --- /dev/null +++ b/Telegram/SourceFiles/history/history_shared_media.h @@ -0,0 +1,316 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "storage/storage_shared_media.h" +#include "mtproto/sender.h" +#include "base/weak_unique_ptr.h" + +base::optional<Storage::SharedMediaType> SharedMediaOverviewType( + Storage::SharedMediaType type); +void SharedMediaShowOverview( + Storage::SharedMediaType type, + not_null<History*> history); + +class SharedMediaViewer; +class SharedMediaSlice { +public: + using Key = Storage::SharedMediaKey; + + SharedMediaSlice( + Key key, + base::optional<int> fullCount = base::none) + : _key(key) + , _fullCount(fullCount) { + } + + base::optional<int> fullCount() const { + return _fullCount; + } + base::optional<int> skippedBefore() const { + return _skippedBefore; + } + base::optional<int> skippedAfter() const { + return _skippedAfter; + } + base::optional<int> indexOf(MsgId msgId) const { + auto it = _ids.find(msgId); + if (it != _ids.end()) { + return (it - _ids.begin()); + } + return base::none; + } + int size() const { + return _ids.size(); + } + + using iterator = base::flat_set<MsgId>::const_iterator; + + iterator begin() const { + return _ids.begin(); + } + iterator end() const { + return _ids.end(); + } + iterator cbegin() const { + return begin(); + } + iterator cend() const { + return end(); + } + + base::optional<int> distance(const Key &a, const Key &b) const { + if (a.type != _key.type + || b.type != _key.type + || a.peerId != _key.peerId + || b.peerId != _key.peerId) { + return base::none; + } + auto i = _ids.find(a.messageId); + auto j = _ids.find(b.messageId); + if (i == _ids.end() || j == _ids.end()) { + return base::none; + } + return j - i; + } + +private: + Key _key; + base::flat_set<MsgId> _ids; + MsgRange _range; + base::optional<int> _fullCount; + base::optional<int> _skippedBefore; + base::optional<int> _skippedAfter; + + friend class SharedMediaViewer; + +}; + +class SharedMediaViewer : + private base::Subscriber, + public base::enable_weak_from_this { +public: + using Type = Storage::SharedMediaType; + using Key = Storage::SharedMediaKey; + + SharedMediaViewer( + Key key, + int limitBefore, + int limitAfter); + + void start(); + + base::Observable<SharedMediaSlice> updated; + +private: + using InitialResult = Storage::SharedMediaResult; + using SliceUpdate = Storage::SharedMediaSliceUpdate; + using OneRemoved = Storage::SharedMediaRemoveOne; + using AllRemoved = Storage::SharedMediaRemoveAll; + + void loadInitial(); + enum class RequestDirection { + Before, + After, + }; + void requestMessages(RequestDirection direction); + void applyStoredResult(InitialResult &&result); + void applyUpdate(const SliceUpdate &update); + void applyUpdate(const OneRemoved &update); + void applyUpdate(const AllRemoved &update); + void sliceToLimits(); + + void mergeSliceData( + base::optional<int> count, + const base::flat_set<MsgId> &messageIds, + base::optional<int> skippedBefore = base::none, + base::optional<int> skippedAfter = base::none); + + + Key _key; + int _limitBefore = 0; + int _limitAfter = 0; + mtpRequestId _beforeRequestId = 0; + mtpRequestId _afterRequestId = 0; + SharedMediaSlice _data; + +}; +// +//class SharedMediaSliceMerged : +// private MTP::Sender, +// private base::Subscriber, +// public base::enable_weak_from_this { +//public: +// class Data; +// +//private: +// friend class Data; +// using UniversalMsgId = MsgId; +// +//public: +// using Type = Storage::SharedMediaType; +// +// SharedMediaSliceMerged( +// Type type, +// not_null<History*> history, +// MsgId aroundId, +// int limitBefore, +// int limitAfter); +// +// bool hasOverview() const; +// void showOverview() const; +// bool moveTo(const SharedMediaSliceMerged &other); +// +// void load(); +// +// class Data { +// public: +// base::optional<int> fullCount() const; +// base::optional<int> skippedBefore() const; +// base::optional<int> skippedAfter() const; +// int size() const { +// return _ids.size(); +// } +// +// class iterator { +// public: +// FullMsgId operator*() const { +// auto id = _data->_ids[_index]; +// Assert(IsServerMsgId(id) +// || (_data->_migrated != nullptr && IsServerMsgId(-id))); +// return IsServerMsgId(id) +// ? FullMsgId(_data->_history->channelId(), id) +// : FullMsgId(_data->_migrated->channelId(), -id); +// } +// iterator &operator--() { +// --_index; +// return *this; +// } +// iterator operator--(int) { +// auto result = *this; +// --*this; +// return result; +// } +// iterator &operator++() { +// ++_index; +// return *this; +// } +// iterator operator++(int) { +// auto result = *this; +// ++*this; +// return result; +// } +// iterator &operator+=(int offset) { +// _index += offset; +// return *this; +// } +// iterator operator+(int offset) const { +// auto result = *this; +// return result += offset; +// } +// bool operator==(iterator other) const { +// return (_data == other._data) && (_index == other._index); +// } +// bool operator!=(iterator other) const { +// return !(*this == other); +// } +// bool operator<(iterator other) const { +// return (_data < other._data) +// || (_data == other._data && _index < other._index); +// } +// +// private: +// friend class Data; +// +// iterator(not_null<const Data*> data, int index) +// : _data(data) +// , _index(index) { +// } +// +// not_null<const Data*> _data; +// int _index = 0; +// +// }; +// +// iterator begin() const { +// return iterator(this, 0); +// } +// iterator end() const { +// iterator(this, _ids.size()); +// } +// iterator cbegin() const { +// return begin(); +// } +// iterator cend() const { +// return end(); +// } +// +// private: +// friend class iterator; +// friend class SharedMediaSliceMerged; +// +// Data( +// not_null<History*> history, +// History *migrated, +// base::optional<int> historyCount = base::none, +// base::optional<int> migratedCount = base::none) +// : _history(history) +// , _migrated(migrated) +// , _historyCount(historyCount) +// , _migratedCount(migratedCount) { +// if (!_migrated) { +// _migratedCount = 0; +// } +// } +// +// not_null<History*> _history; +// History *_migrated = nullptr; +// std::vector<UniversalMsgId> _ids; +// base::optional<int> _historyCount; +// base::optional<int> _historySkippedBefore; +// base::optional<int> _historySkippedAfter; +// base::optional<int> _migratedCount; +// base::optional<int> _migratedSkippedBefore; +// base::optional<int> _migratedSkippedAfter; +// +// }; +// base::Observable<Data> updated; +// +//private: +// bool amAroundMigrated() const; +// not_null<History*> aroundHistory() const; +// MsgId aroundId() const; +// +// void applyStoredResult( +// not_null<PeerData*> peer, +// Storage::SharedMediaResult &&result); +// bool containsAroundId() const; +// void clearAfterMove(); +// +// Type _type = Type::kCount; +// not_null<History*> _history; +// History *_migrated = nullptr; +// UniversalMsgId _universalAroundId = 0; +// int _limitBefore = 0; +// int _limitAfter = 0; +// Data _data; +// +//}; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index f530946ff..97457cfdd 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -44,6 +44,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace { +constexpr auto kPreloadCount = 4; + TextParseOptions _captionTextOptions = { TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags 0, // maxw @@ -57,21 +59,24 @@ TextParseOptions _captionBotOptions = { Qt::LayoutDirectionAuto, // dir }; -bool typeHasMediaOverview(MediaOverviewType type) { - switch (type) { - case OverviewPhotos: - case OverviewVideos: - case OverviewMusicFiles: - case OverviewFiles: - case OverviewVoiceFiles: - case OverviewLinks: return true; - default: break; - } - return false; -} +// Preload X message ids before and after current. +constexpr auto kIdsLimit = 32; + +// Preload next messages if we went further from current than that. +constexpr auto kIdsPreloadAfter = 28; } // namespace +struct MediaView::SharedMedia { + SharedMedia(SharedMediaViewer::Key key) + : key(key) + , slice(key, kIdsLimit, kIdsLimit) { + } + + SharedMediaViewer::Key key; + SharedMediaViewer slice; +}; + MediaView::MediaView() : TWidget(nullptr) , _transparentBrush(style::transparentPlaceholderBrush()) , _animStarted(getms()) @@ -111,9 +116,11 @@ MediaView::MediaView() : TWidget(nullptr) documentUpdated(document); } }); - subscribe(Auth().messageIdChanging, [this](std::pair<HistoryItem*, MsgId> update) { + subscribe(Auth().messageIdChanging, [this](std::pair<not_null<HistoryItem*>, MsgId> update) { changingMsgId(update.first, update.second); }); + } else { + _sharedMedia = nullptr; } }; subscribe(Messenger::Instance().authSessionChanged(), [handleAuthSessionChange] { @@ -121,11 +128,6 @@ MediaView::MediaView() : TWidget(nullptr) }); handleAuthSessionChange(); - auto observeEvents = Notify::PeerUpdate::Flag::SharedMediaChanged; - subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) { - mediaOverviewUpdated(update); - })); - setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint); moveToScreen(); setAttribute(Qt::WA_NoSystemBackground, true); @@ -190,54 +192,41 @@ void MediaView::moveToScreen() { _saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2); } -void MediaView::mediaOverviewUpdated(const Notify::PeerUpdate &update) { - if (isHidden() || (!_photo && !_doc)) return; - if (_photo && _overview == OverviewChatPhotos && _history && !_history->peer->isUser()) { - auto lastChatPhoto = computeLastOverviewChatPhoto(); - if (_index < 0 && _photo == lastChatPhoto.photo && _photo == _additionalChatPhoto) { - auto firstOpened = _firstOpenedPeerPhoto; - showPhoto(_photo, lastChatPhoto.item); - _firstOpenedPeerPhoto = firstOpened; - return; - } - computeAdditionalChatPhoto(_history->peer, lastChatPhoto.photo); +void MediaView::handleSharedMediaUpdate(const SharedMediaSlice &update) { + if (isHidden() || (!_photo && !_doc) || !_sharedMedia) { + _index = _fullIndex = _fullCount = base::none; + return; } + //if (_photo && _overview == OverviewChatPhotos && _history && !_history->peer->isUser()) { // TODO chat + // auto lastChatPhoto = computeLastOverviewChatPhoto(); + // if (_index < 0 && _photo == lastChatPhoto.photo && _photo == _additionalChatPhoto) { + // auto firstOpened = _firstOpenedPeerPhoto; + // showPhoto(_photo, lastChatPhoto.item); + // _firstOpenedPeerPhoto = firstOpened; + // return; + // } + // computeAdditionalChatPhoto(_history->peer, lastChatPhoto.photo); + //} - if (_history && (_history->peer == update.peer || (_migrated && _migrated->peer == update.peer)) && (update.mediaTypesMask & (1 << _overview)) && _msgid) { - _index = -1; - auto i = 0; - if (_msgmigrated) { - for_const (auto msgId, _migrated->overview(_overview)) { - if (msgId == _msgid) { - _index = i; - break; - } - ++i; - } - } else { - for_const (auto msgId, _history->overview(_overview)) { - if (msgId == _msgid) { - _index = i; - break; - } - ++i; - } - } - updateControls(); - preloadData(0); - } else if (_user == update.peer && update.mediaTypesMask & (1 << OverviewCount)) { - if (!_photo) return; + _sharedMediaData = update; - _index = -1; - for (int i = 0, l = _user->photos.size(); i < l; ++i) { - if (_user->photos[i] == _photo) { - _index = i; - break; - } - } - updateControls(); - preloadData(0); - } + findCurrent(); + updateControls(); + preloadData(0); + + //if (_user == update.peer && update.mediaTypesMask & (1 << OverviewCount)) { + // if (!_photo) return; + + // _index = -1; + // for (int i = 0, l = _user->photos.size(); i < l; ++i) { + // if (_user->photos[i] == _photo) { + // _index = i; + // break; + // } + // } + // updateControls(); + // preloadData(0); + //} // TODO user } bool MediaView::fileShown() const { @@ -285,17 +274,10 @@ void MediaView::documentUpdated(DocumentData *doc) { } } -void MediaView::changingMsgId(HistoryItem *row, MsgId newId) { +void MediaView::changingMsgId(not_null<HistoryItem*> row, MsgId newId) { if (row->id == _msgid) { _msgid = newId; - } - - // Send a fake update. - if (!isHidden()) { - Notify::PeerUpdate update(row->history()->peer); - update.flags |= Notify::PeerUpdate::Flag::SharedMediaChanged; - update.mediaTypesMask |= (1 << _overview); - mediaOverviewUpdated(update); + validateSharedMedia(); } } @@ -332,6 +314,17 @@ void MediaView::updateDocSize() { } } +void MediaView::refreshNavVisibility() { + if (_sharedMediaData) { + _leftNavVisible = _index && (*_index > 0); + _rightNavVisible = _index && (*_index + 1 < _sharedMediaData->size()); + } else { + _leftNavVisible = false; + _rightNavVisible = false; + } + // TODO user +} + void MediaView::updateControls() { if (_doc && fileBubbleShown()) { if (_doc->loading()) { @@ -393,28 +386,7 @@ void MediaView::updateControls() { _dateNav = myrtlrect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height); } updateHeader(); - if (_photo || (_history && _overview != OverviewCount)) { - _leftNavVisible = (_index > 0) || (_index == 0 && ( - (!_msgmigrated && _history && _history->overview(_overview).size() < _history->overviewCount(_overview)) || - (_msgmigrated && _migrated && _migrated->overview(_overview).size() < _migrated->overviewCount(_overview)) || - (!_msgmigrated && _history && _migrated && (!_migrated->overview(_overview).isEmpty() || _migrated->overviewCount(_overview) > 0)))) || - (_index < 0 && _photo == _additionalChatPhoto && - ((_history && _history->overviewCount(_overview) > 0) || - (_migrated && _history->overviewLoaded(_overview) && _migrated->overviewCount(_overview) > 0)) - ); - _rightNavVisible = (_index >= 0) && ( - (!_msgmigrated && _history && _index + 1 < _history->overview(_overview).size()) || - (_msgmigrated && _migrated && _index + 1 < _migrated->overview(_overview).size()) || - (_msgmigrated && _migrated && _history && (!_history->overview(_overview).isEmpty() || _history->overviewCount(_overview) > 0)) || - (!_msgmigrated && _history && _index + 1 == _history->overview(_overview).size() && _additionalChatPhoto) || - (_msgmigrated && _migrated && _index + 1 == _migrated->overview(_overview).size() && _history->overviewCount(_overview) == 0 && _additionalChatPhoto) || - (!_history && _user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount))); - if (_msgmigrated && !_history->overviewLoaded(_overview)) { - _leftNavVisible = _rightNavVisible = false; - } - } else { - _leftNavVisible = _rightNavVisible = false; - } + refreshNavVisibility(); if (!_caption.isEmpty()) { int32 skipw = qMax(_dateNav.left() + _dateNav.width(), _headerNav.left() + _headerNav.width()); @@ -453,7 +425,10 @@ void MediaView::updateActions() { _actions.push_back({ lang(lng_mediaview_delete), SLOT(onDelete()) }); } _actions.push_back({ lang(lng_mediaview_save_as), SLOT(onSaveAs()) }); - if (_history && typeHasMediaOverview(_overview)) { + + if (auto overviewType = + sharedMediaType() + | SharedMediaOverviewType) { _actions.push_back({ lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all), SLOT(onOverview()) }); } } @@ -687,6 +662,7 @@ void MediaView::updateMixerVideoVolume() const { } void MediaView::close() { + _sharedMedia = nullptr; if (_menu) _menu->hideMenu(true); Messenger::Instance().hideMediaView(); } @@ -1002,12 +978,13 @@ void MediaView::onDelete() { void MediaView::onOverview() { if (_menu) _menu->hideMenu(true); - if (!_history || !typeHasMediaOverview(_overview)) { - update(); - return; + update(); + if (auto overviewType = + sharedMediaType() + | SharedMediaOverviewType) { + close(); + SharedMediaShowOverview(*overviewType, _history); } - close(); - if (_history->peer) App::main()->showMediaOverview(_history->peer, _overview); } void MediaView::onCopy() { @@ -1025,7 +1002,81 @@ void MediaView::onCopy() { } } -void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { +base::optional<MediaView::SharedMediaType> MediaView::sharedMediaType() const { + using Type = SharedMediaType; + auto channelId = _msgmigrated ? NoChannel : _channel; + if (auto item = App::histItemById(channelId, _msgid)) { + if (_photo) { + if (item->toHistoryMessage()) { + return Type::Photo; + } + return Type::ChatPhoto; + } else if (_doc) { + if (_doc->isGifv()) { + return Type::GIF; + } else if (_doc->isVideo()) { + return Type::Video; + } + return Type::File; + } + } + return base::none; +} + +base::optional<MediaView::SharedMediaKey> MediaView::sharedMediaKey() const { + auto keyForType = [this](SharedMediaType type) -> SharedMediaKey { + return { (_msgmigrated ? _migrated : _history)->peer->id, type, _msgid }; + }; + return + sharedMediaType() + | keyForType; +} + +bool MediaView::validSharedMedia() const { + if (auto key = sharedMediaKey()) { + if (!_sharedMedia) { + return false; + } + auto countDistanceInData = [](const auto &a, const auto &b) { + return [&](const SharedMediaSlice &data) { + return data.distance(a, b); + }; + }; + + auto distance = (key == _sharedMedia->key) ? 0 : + _sharedMediaData + | countDistanceInData(*key, _sharedMedia->key) + | base::abs; + if (distance) { + return (*distance < kIdsPreloadAfter); + } + } + return (_sharedMedia == nullptr); +} + +void MediaView::validateSharedMedia() { + if (auto key = sharedMediaKey()) { + _sharedMedia = std::make_unique<SharedMedia>(*key); + subscribe(_sharedMedia->slice.updated, [this](const SharedMediaSlice &data) { + handleSharedMediaUpdate(data); + }); + _sharedMedia->slice.start(); + } else { + _sharedMedia = nullptr; + _sharedMediaData = base::none; + } +} + +void MediaView::refreshSharedMedia() { + if (!validSharedMedia()) { + validateSharedMedia(); + } + findCurrent(); + updateControls(); + preloadData(0); +} + +void MediaView::showPhoto(not_null<PhotoData*> photo, HistoryItem *context) { _history = context ? context->history().get() : nullptr; _migrated = nullptr; if (_history) { @@ -1052,23 +1103,20 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { } if (!_animOpacities.isEmpty()) _animOpacities.clear(); - _index = -1; _msgid = context ? context->id : 0; _msgmigrated = context ? (context->history() == _migrated) : false; _channel = _history ? _history->channelId() : NoChannel; _canForward = context ? context->canForward() : false; _canDelete = context ? context->canDelete() : false; _photo = photo; + + validateSharedMedia(); if (_history) { if (context && !context->toHistoryMessage()) { - _overview = OverviewChatPhotos; if (!_history->peer->isUser()) { computeAdditionalChatPhoto(_history->peer, computeLastOverviewChatPhoto().photo); } - } else { - _overview = OverviewPhotos; } - findCurrent(); } displayPhoto(photo, context); @@ -1076,7 +1124,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { activateControls(); } -void MediaView::showPhoto(PhotoData *photo, PeerData *context) { +void MediaView::showPhoto(not_null<PhotoData*> photo, PeerData *context) { _history = _migrated = nullptr; _additionalChatPhoto = nullptr; _firstOpenedPeerPhoto = true; @@ -1096,23 +1144,23 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { _msgmigrated = false; _channel = NoChannel; _canForward = _canDelete = false; - _index = -1; _photo = photo; - _overview = OverviewCount; - if (_user) { - if (_user->photos.isEmpty() && _user->photosCount < 0 && _user->photoId && _user->photoId != UnknownPeerPhotoId) { - _index = 0; - } - for (int i = 0, l = _user->photos.size(); i < l; ++i) { - if (_user->photos.at(i) == photo) { - _index = i; - break; - } - } - if (_user->photosCount < 0) { - loadBack(); - } + validateSharedMedia(); + if (_user) { + //if (_user->photos.isEmpty() && _user->photosCount < 0 && _user->photoId && _user->photoId != UnknownPeerPhotoId) { + // _fullIndex = 0; + //} + //for (int i = 0, l = _user->photos.size(); i < l; ++i) { + // if (_user->photos.at(i) == photo) { + // _fullIndex = i; + // break; + // } + //} + + //if (_user->photosCount < 0) { + // loadBack(); + //} // TODO user } else if ((_history = App::historyLoaded(_peer))) { if (_history->peer->migrateFrom()) { _migrated = App::history(_history->peer->migrateFrom()->id); @@ -1129,20 +1177,20 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { } computeAdditionalChatPhoto(_history->peer, lastChatPhoto.photo); - if (_additionalChatPhoto == _photo) { - _overview = OverviewChatPhotos; - findCurrent(); - } else { + //if (_additionalChatPhoto == _photo) { // TODO chat + // _overview = OverviewChatPhotos; + // findCurrent(); + //} else { _additionalChatPhoto = nullptr; _history = _migrated = nullptr; - } + //} } displayPhoto(photo, 0); preloadData(0); activateControls(); } -void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { +void MediaView::showDocument(not_null<DocumentData*> document, HistoryItem *context) { _photo = 0; _history = context ? context->history().get() : nullptr; _migrated = nullptr; @@ -1169,25 +1217,21 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { } if (!_animOpacities.isEmpty()) _animOpacities.clear(); - _index = -1; _msgid = context ? context->id : 0; _msgmigrated = context ? (context->history() == _migrated) : false; _channel = _history ? _history->channelId() : NoChannel; _canForward = context ? context->canForward() : false; _canDelete = context ? context->canDelete() : false; - if (_history) { - _overview = doc->isGifv() ? OverviewGIFs : doc->isVideo() ? OverviewVideos : OverviewFiles; - findCurrent(); + + if (document->isVideo() || document->isRoundVideo()) { + _autoplayVideoDocument = document; } - if (doc->isVideo() || doc->isRoundVideo()) { - _autoplayVideoDocument = doc; - } - displayDocument(doc, context); + displayDocument(document, context); preloadData(0); activateControls(); } -void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { +void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) { stopGif(); destroyThemePreview(); _doc = nullptr; @@ -1195,6 +1239,8 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { _photo = photo; _radial.stop(); + validateSharedMedia(); + _photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize); _zoom = 0; @@ -1268,6 +1314,8 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty _photo = nullptr; _radial.stop(); + validateSharedMedia(); + if (_autoplayVideoDocument && _doc != _autoplayVideoDocument) { _autoplayVideoDocument = nullptr; } @@ -2160,180 +2208,185 @@ void MediaView::setZoomLevel(int newZoom) { } bool MediaView::moveToNext(int32 delta) { - if (_index < 0) { - if (delta == -1 && _photo == _additionalChatPhoto) { - auto lastChatPhoto = computeLastOverviewChatPhoto(); - if (lastChatPhoto.item) { - if (lastChatPhoto.item->history() == _history) { - _index = _history->overview(_overview).size() - 1; - _msgmigrated = false; - } else { - _index = _migrated->overview(_overview).size() - 1; - _msgmigrated = true; - } - _msgid = lastChatPhoto.item->id; - _channel = _history ? _history->channelId() : NoChannel; - _canForward = lastChatPhoto.item->canForward(); - _canDelete = lastChatPhoto.item->canDelete(); - displayPhoto(lastChatPhoto.photo, lastChatPhoto.item); - preloadData(delta); - return true; - } else if (_history && (_history->overviewCount(OverviewChatPhotos) != 0 || ( - _migrated && _migrated->overviewCount(OverviewChatPhotos) != 0))) { - loadBack(); - return true; - } - } + if (!_index) { return false; } - if (_overview == OverviewCount && (_history || !_user)) { + auto newIndex = *_index + delta; + if (newIndex < 0 || newIndex >= _sharedMediaData->size()) { return false; } - if (_msgmigrated && !_history->overviewLoaded(_overview)) { - return true; - } - - int32 newIndex = _index + delta; - if (_history && _overview != OverviewCount) { - bool newMigrated = _msgmigrated; - if (!newMigrated && newIndex < 0 && _migrated) { - newIndex += _migrated->overview(_overview).size(); - newMigrated = true; - } else if (newMigrated && newIndex >= _migrated->overview(_overview).size()) { - newIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size()); - newMigrated = false; - } - if (newIndex >= 0 && newIndex < (newMigrated ? _migrated : _history)->overview(_overview).size()) { - if (auto item = App::histItemById(newMigrated ? 0 : _channel, getMsgIdFromOverview(newMigrated ? _migrated : _history, newIndex))) { - _index = newIndex; - _msgid = item->id; - _msgmigrated = (item->history() == _migrated); - _channel = _history ? _history->channelId() : NoChannel; - _canForward = item->canForward(); - _canDelete = item->canDelete(); - stopGif(); - if (auto media = item->getMedia()) { - switch (media->type()) { - case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break; - case MediaTypeFile: - case MediaTypeVideo: - case MediaTypeGif: - case MediaTypeSticker: displayDocument(media->getDocument(), item); preloadData(delta); break; - } - } else { - displayDocument(nullptr, item); - preloadData(delta); - } + if (auto item = App::histItemById(_history->channelId(), *(_sharedMediaData->begin() + newIndex))) { + _index = newIndex; + _msgid = item->id; + _msgmigrated = (item->history() == _migrated); + _channel = _history ? _history->channelId() : NoChannel; + _canForward = item->canForward(); + _canDelete = item->canDelete(); + stopGif(); + if (auto media = item->getMedia()) { + switch (media->type()) { + case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break; + case MediaTypeFile: + case MediaTypeVideo: + case MediaTypeGif: + case MediaTypeSticker: displayDocument(media->getDocument(), item); preloadData(delta); break; } - } else if (!newMigrated && newIndex == _history->overview(_overview).size() && _additionalChatPhoto) { - _index = -1; - _msgid = 0; - _msgmigrated = false; - _canForward = false; - _canDelete = false; - displayPhoto(_additionalChatPhoto, 0); - } - if (delta < 0 && _index < MediaOverviewStartPerPage) { - loadBack(); - } - } else if (_user) { - if (newIndex >= 0 && newIndex < _user->photos.size()) { - _index = newIndex; - displayPhoto(_user->photos[_index], 0); + } else { + displayDocument(nullptr, item); preloadData(delta); } - if (delta > 0 && _index > _user->photos.size() - MediaOverviewStartPerPage) { - loadBack(); - } + return true; } + return false; + + //if (_index < 0) { // TODO chat + // if (delta == -1 && _photo == _additionalChatPhoto) { + // auto lastChatPhoto = computeLastOverviewChatPhoto(); + // if (lastChatPhoto.item) { + // if (lastChatPhoto.item->history() == _history) { + // _index = _history->overview(_overview).size() - 1; + // _msgmigrated = false; + // } else { + // _index = _migrated->overview(_overview).size() - 1; + // _msgmigrated = true; + // } + // _msgid = lastChatPhoto.item->id; + // _channel = _history ? _history->channelId() : NoChannel; + // _canForward = lastChatPhoto.item->canForward(); + // _canDelete = lastChatPhoto.item->canDelete(); + // displayPhoto(lastChatPhoto.photo, lastChatPhoto.item); + // preloadData(delta); + // return true; + // } else if (_history && (_history->overviewCount(OverviewChatPhotos) != 0 || ( + // _migrated && _migrated->overviewCount(OverviewChatPhotos) != 0))) { + // loadBack(); + // return true; + // } + // } + // return false; + //} + //if (_overview == OverviewCount && (_history || !_user)) { + // return false; + //} + //if (_msgmigrated && !_history->overviewLoaded(_overview)) { + // return true; + //} + + //int32 newIndex = _index + delta; + //if (_history && _overview != OverviewCount) { + // bool newMigrated = _msgmigrated; + // if (!newMigrated && newIndex < 0 && _migrated) { + // newIndex += _migrated->overview(_overview).size(); + // newMigrated = true; + // } else if (newMigrated && newIndex >= _migrated->overview(_overview).size()) { + // newIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size()); + // newMigrated = false; + // } + // if (newIndex >= 0 && newIndex < (newMigrated ? _migrated : _history)->overview(_overview).size()) { + // if (auto item = App::histItemById(newMigrated ? 0 : _channel, getMsgIdFromOverview(newMigrated ? _migrated : _history, newIndex))) { + // _index = newIndex; + // _msgid = item->id; + // _msgmigrated = (item->history() == _migrated); + // _channel = _history ? _history->channelId() : NoChannel; + // _canForward = item->canForward(); + // _canDelete = item->canDelete(); + // stopGif(); + // if (auto media = item->getMedia()) { + // switch (media->type()) { + // case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break; + // case MediaTypeFile: + // case MediaTypeVideo: + // case MediaTypeGif: + // case MediaTypeSticker: displayDocument(media->getDocument(), item); preloadData(delta); break; + // } + // } else { + // displayDocument(nullptr, item); + // preloadData(delta); + // } + // } + // } else if (!newMigrated && newIndex == _history->overview(_overview).size() && _additionalChatPhoto) { + // _index = -1; + // _msgid = 0; + // _msgmigrated = false; + // _canForward = false; + // _canDelete = false; + // displayPhoto(_additionalChatPhoto, 0); + // } + // if (delta < 0 && _index < MediaOverviewStartPerPage) { + // loadBack(); + // } + //} else if (_user) { + // if (newIndex >= 0 && newIndex < _user->photos.size()) { + // _index = newIndex; + // displayPhoto(_user->photos[_index], 0); + // preloadData(delta); + // } + // if (delta > 0 && _index > _user->photos.size() - MediaOverviewStartPerPage) { + // loadBack(); + // } + //} return true; } void MediaView::preloadData(int32 delta) { - int indexInOverview = _index; - bool indexOfMigratedItem = _msgmigrated; - if (_index < 0) { - if (_overview != OverviewChatPhotos || !_history) return; - indexInOverview = _history->overview(OverviewChatPhotos).size(); - indexOfMigratedItem = false; + if (!_index) { + return; } - if (!_user && _overview == OverviewCount) return; + auto from = *_index + (delta ? delta : -1); + auto till = *_index + (delta ? delta * kPreloadCount : 1); + if (from > till) std::swap(from, till); - auto from = indexInOverview + (delta ? delta : -1); - auto to = indexInOverview + (delta ? delta * MediaOverviewPreloadCount : 1); - if (from > to) qSwap(from, to); - if (_history && _overview != OverviewCount) { - auto forgetIndex = indexInOverview - delta * 2; - auto forgetHistory = indexOfMigratedItem ? _migrated : _history; - if (_migrated) { - if (indexOfMigratedItem && forgetIndex >= _migrated->overview(_overview).size()) { - forgetHistory = _history; - forgetIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size()); - } else if (!indexOfMigratedItem && forgetIndex < 0) { - forgetHistory = _migrated; - forgetIndex += _migrated->overview(_overview).size(); + auto forgetIndex = *_index - delta * 2; + if (forgetIndex >= 0 && forgetIndex < _sharedMediaData->size()) { + if (auto item = App::histItemById(_history->channelId(), *(_sharedMediaData->begin() + forgetIndex))) { + if (auto media = item->getMedia()) { + switch (media->type()) { + case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->forget(); break; + case MediaTypeFile: + case MediaTypeVideo: + case MediaTypeGif: + case MediaTypeSticker: media->getDocument()->forget(); break; + } } } - if (forgetIndex >= 0 && forgetIndex < forgetHistory->overview(_overview).size() && (forgetHistory != (indexOfMigratedItem ? _migrated : _history) || forgetIndex != indexInOverview)) { - if (auto item = App::histItemById(forgetHistory->channelId(), getMsgIdFromOverview(forgetHistory, forgetIndex))) { + } + + for (auto index = from; index != till; ++index) { + if (index >= 0 && index < _sharedMediaData->size()) { + if (auto item = App::histItemById(_history->channelId(), *(_sharedMediaData->begin() + index))) { if (auto media = item->getMedia()) { switch (media->type()) { - case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->forget(); break; + case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->download(); break; case MediaTypeFile: case MediaTypeVideo: - case MediaTypeGif: - case MediaTypeSticker: media->getDocument()->forget(); break; + case MediaTypeGif: { + auto doc = media->getDocument(); + doc->thumb->load(); + doc->automaticLoad(item); + } break; + case MediaTypeSticker: media->getDocument()->sticker()->img->load(); break; } } } } - - for (int32 i = from; i <= to; ++i) { - History *previewHistory = indexOfMigratedItem ? _migrated : _history; - int32 previewIndex = i; - if (_migrated) { - if (indexOfMigratedItem && previewIndex >= _migrated->overview(_overview).size()) { - previewHistory = _history; - previewIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size()); - } else if (!indexOfMigratedItem && previewIndex < 0) { - previewHistory = _migrated; - previewIndex += _migrated->overview(_overview).size(); - } - } - if (previewIndex >= 0 && previewIndex < previewHistory->overview(_overview).size() && (previewHistory != (indexOfMigratedItem ? _migrated : _history) || previewIndex != indexInOverview)) { - if (auto item = App::histItemById(previewHistory->channelId(), getMsgIdFromOverview(previewHistory, previewIndex))) { - if (auto media = item->getMedia()) { - switch (media->type()) { - case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->download(); break; - case MediaTypeFile: - case MediaTypeVideo: - case MediaTypeGif: { - auto doc = media->getDocument(); - doc->thumb->load(); - doc->automaticLoad(item); - } break; - case MediaTypeSticker: media->getDocument()->sticker()->img->load(); break; - } - } - } - } - } - } else if (_user) { - for (int32 i = from; i <= to; ++i) { - if (i >= 0 && i < _user->photos.size() && i != indexInOverview) { - _user->photos[i]->thumb->load(); - } - } - for (int32 i = from; i <= to; ++i) { - if (i >= 0 && i < _user->photos.size() && i != indexInOverview) { - _user->photos[i]->download(); - } - } - int32 forgetIndex = indexInOverview - delta * 2; - if (forgetIndex >= 0 && forgetIndex < _user->photos.size() && forgetIndex != indexInOverview) { - _user->photos[forgetIndex]->forget(); - } } + + //} else if (_user) { + // for (int32 i = from; i <= to; ++i) { + // if (i >= 0 && i < _user->photos.size() && i != indexInOverview) { + // _user->photos[i]->thumb->load(); + // } + // } + // for (int32 i = from; i <= to; ++i) { + // if (i >= 0 && i < _user->photos.size() && i != indexInOverview) { + // _user->photos[i]->download(); + // } + // } + // int32 forgetIndex = indexInOverview - delta * 2; + // if (forgetIndex >= 0 && forgetIndex < _user->photos.size() && forgetIndex != indexInOverview) { + // _user->photos[forgetIndex]->forget(); + // } + //} // TODO user } void MediaView::mousePressEvent(QMouseEvent *e) { @@ -2763,69 +2816,81 @@ void MediaView::updateImage() { } void MediaView::findCurrent() { - auto i = 0; - if (_msgmigrated) { - for (auto msgId : _migrated->overview(_overview)) { - if (msgId == _msgid) { - _index = i; - break; - } - ++i; - } - if (!_history->overviewCountLoaded(_overview)) { - loadBack(); - } else if (_history->overviewLoaded(_overview) && !_migrated->overviewLoaded(_overview)) { // all loaded - if (!_migrated->overviewCountLoaded(_overview) || (_index < 2 && _migrated->overviewCount(_overview) > 0)) { - loadBack(); - } - } - } else { - for (auto msgId : _history->overview(_overview)) { - if (msgId == _msgid) { - _index = i; - break; - } - ++i; - } - if (!_history->overviewLoaded(_overview)) { - if (!_history->overviewCountLoaded(_overview) || (_index < 2 && _history->overviewCount(_overview) > 0) || (_index < 1 && _migrated && !_migrated->overviewLoaded(_overview))) { - loadBack(); - } - } else if (_index < 1 && _migrated && !_migrated->overviewLoaded(_overview)) { - loadBack(); - } - if (_migrated && !_migrated->overviewCountLoaded(_overview)) { - App::main()->preloadOverview(_migrated->peer, _overview); - } + if (!_sharedMediaData) { + _index = _fullIndex = _fullCount = base::none; + return; } + _index = _sharedMediaData->indexOf(_msgid); + if (_index && _sharedMediaData->skippedBefore()) { + _fullIndex = (*_index + *_sharedMediaData->skippedBefore()); + } else { + _fullIndex = base::none; + } + _fullCount = _sharedMediaData->fullCount(); + + //auto i = 0; + //if (_msgmigrated) { + // for (auto msgId : _migrated->overview(_overview)) { + // if (msgId == _msgid) { + // _index = i; + // break; + // } + // ++i; + // } + // if (!_history->overviewCountLoaded(_overview)) { + // loadBack(); + // } else if (_history->overviewLoaded(_overview) && !_migrated->overviewLoaded(_overview)) { // all loaded + // if (!_migrated->overviewCountLoaded(_overview) || (_index < 2 && _migrated->overviewCount(_overview) > 0)) { + // loadBack(); + // } + // } + //} else { + // for (auto msgId : _history->overview(_overview)) { + // if (msgId == _msgid) { + // _index = i; + // break; + // } + // ++i; + // } + // if (!_history->overviewLoaded(_overview)) { + // if (!_history->overviewCountLoaded(_overview) || (_index < 2 && _history->overviewCount(_overview) > 0) || (_index < 1 && _migrated && !_migrated->overviewLoaded(_overview))) { + // loadBack(); + // } + // } else if (_index < 1 && _migrated && !_migrated->overviewLoaded(_overview)) { + // loadBack(); + // } + // if (_migrated && !_migrated->overviewCountLoaded(_overview)) { + // App::main()->preloadOverview(_migrated->peer, _overview); + // } + //} // TODO user } void MediaView::loadBack() { - if (_loadRequest || (_overview == OverviewCount && !_user)) { - return; - } - if (_index < 0 && (!_additionalChatPhoto || _photo != _additionalChatPhoto || !_history)) { - return; - } + //if (_loadRequest || (_overview == OverviewCount && !_user)) { + // return; + //} + //if (_index < 0 && (!_additionalChatPhoto || _photo != _additionalChatPhoto || !_history)) { + // return; + //} - if (_history && _overview != OverviewCount && (!_history->overviewLoaded(_overview) || (_migrated && !_migrated->overviewLoaded(_overview)))) { - if (App::main()) { - if (_msgmigrated || (_migrated && _index == 0 && _history->overviewLoaded(_overview))) { - App::main()->loadMediaBack(_migrated->peer, _overview); - } else { - App::main()->loadMediaBack(_history->peer, _overview); - if (_migrated && _index == 0 && (_migrated->overviewCount(_overview) < 0 || _migrated->overview(_overview).isEmpty()) && !_migrated->overviewLoaded(_overview)) { - App::main()->loadMediaBack(_migrated->peer, _overview); - } - } - if (_msgmigrated && !_history->overviewCountLoaded(_overview)) { - App::main()->preloadOverview(_history->peer, _overview); - } - } - } else if (_user && _user->photosCount != 0) { - int32 limit = (_index < MediaOverviewStartPerPage && _user->photos.size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; - _loadRequest = MTP::send(MTPphotos_GetUserPhotos(_user->inputUser, MTP_int(_user->photos.size()), MTP_long(0), MTP_int(limit)), rpcDone(&MediaView::userPhotosLoaded, _user)); - } + //if (_history && _overview != OverviewCount && (!_history->overviewLoaded(_overview) || (_migrated && !_migrated->overviewLoaded(_overview)))) { + // if (App::main()) { + // if (_msgmigrated || (_migrated && _index == 0 && _history->overviewLoaded(_overview))) { + // App::main()->loadMediaBack(_migrated->peer, _overview); + // } else { + // App::main()->loadMediaBack(_history->peer, _overview); + // if (_migrated && _index == 0 && (_migrated->overviewCount(_overview) < 0 || _migrated->overview(_overview).isEmpty()) && !_migrated->overviewLoaded(_overview)) { + // App::main()->loadMediaBack(_migrated->peer, _overview); + // } + // } + // if (_msgmigrated && !_history->overviewCountLoaded(_overview)) { + // App::main()->preloadOverview(_history->peer, _overview); + // } + // } + //} else if (_user && _user->photosCount != 0) { + // int32 limit = (_index < MediaOverviewStartPerPage && _user->photos.size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; + // _loadRequest = MTP::send(MTPphotos_GetUserPhotos(_user->inputUser, MTP_int(_user->photos.size()), MTP_long(0), MTP_int(limit)), rpcDone(&MediaView::userPhotosLoaded, _user)); + //} // TODO user } MediaView::LastChatPhoto MediaView::computeLastOverviewChatPhoto() { @@ -2903,34 +2968,11 @@ void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mt } void MediaView::updateHeader() { - int32 index = _index, count = 0, addcount = (_migrated && _overview != OverviewCount) ? _migrated->overviewCount(_overview) : 0; + auto index = _fullIndex ? *_fullIndex : -1; + auto count = _fullCount ? *_fullCount : -1; if (_history) { - if (_overview != OverviewCount) { - bool lastOverviewPhotoLoaded = (!_history->overview(_overview).isEmpty() || ( - _migrated && _history->overviewCount(_overview) == 0 && !_migrated->overview(_overview).isEmpty())); - count = _history->overviewCount(_overview); - if (addcount >= 0 && count >= 0) { - count += addcount; - } - if (index >= 0 && (_msgmigrated ? (count >= 0 && addcount >= 0 && _history->overviewLoaded(_overview)) : (count >= 0))) { - if (_msgmigrated) { - index += addcount - _migrated->overview(_overview).size(); - } else { - index += count - _history->overview(_overview).size(); - } - if (_additionalChatPhoto && lastOverviewPhotoLoaded) { - ++count; - } - } else if (index < 0 && _additionalChatPhoto && _photo == _additionalChatPhoto && lastOverviewPhotoLoaded) { - // Additional chat photo (not in the list => place it at the end of the list). - index = count; - ++count; - } else { - count = 0; // unknown yet - } - } } else if (_user) { - count = _user->photosCount ? _user->photosCount : _user->photos.size(); + count = _user->photosCount ? _user->photosCount : _user->photos.size(); // TODO user } if (index >= 0 && index < count && count > 1) { if (_doc) { @@ -2951,8 +2993,10 @@ void MediaView::updateHeader() { _headerText = lang(lng_mediaview_single_photo); } } - _headerHasLink = _history && typeHasMediaOverview(_overview); - int32 hwidth = st::mediaviewThickFont->width(_headerText); + _headerHasLink = ( + sharedMediaType() + | SharedMediaOverviewType) != base::none; + auto hwidth = st::mediaviewThickFont->width(_headerText); if (hwidth > width() / 3) { hwidth = width() / 3; _headerText = st::mediaviewThickFont->elided(_headerText, hwidth, Qt::ElideMiddle); @@ -2966,13 +3010,13 @@ float64 MediaView::overLevel(OverState control) const { } MsgId MediaView::getMsgIdFromOverview(not_null<History*> history, int index) const { - auto &overview = history->overview(_overview); - if (index >= 0 && index < overview.size()) { - auto it = overview.begin(); - for (auto i = 0; i != index; ++i) { - ++it; - } - return *it; - } + //auto &overview = history->overview(_overview); + //if (index >= 0 && index < overview.size()) { + // auto it = overview.begin(); + // for (auto i = 0; i != index; ++i) { + // ++it; + // } + // return *it; + //} return 0; } diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index b75eac532..c17df973d 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/dropdown_menu.h" #include "ui/effects/radial_animation.h" +#include "history/history_shared_media.h" namespace Media { namespace Player { @@ -58,9 +59,9 @@ public: void updateOver(QPoint mpos); - void showPhoto(PhotoData *photo, HistoryItem *context); - void showPhoto(PhotoData *photo, PeerData *context); - void showDocument(DocumentData *doc, HistoryItem *context); + void showPhoto(not_null<PhotoData*> photo, HistoryItem *context); + void showPhoto(not_null<PhotoData*> photo, PeerData *context); + void showDocument(not_null<DocumentData*> document, HistoryItem *context); void moveToScreen(); bool moveToNext(int32 delta); void preloadData(int32 delta); @@ -154,13 +155,25 @@ private: void showSaveMsgFile(); void updateMixerVideoVolume() const; + struct SharedMedia; + using SharedMediaType = SharedMediaViewer::Type; + using SharedMediaKey = SharedMediaViewer::Key; + base::optional<SharedMediaType> sharedMediaType() const; + base::optional<SharedMediaKey> sharedMediaKey() const; + void validateSharedMedia(); + bool validSharedMedia() const; + std::unique_ptr<SharedMedia> createSharedMedia() const; + void refreshSharedMedia(); + void handleSharedMediaUpdate(const SharedMediaSlice &update); + void refreshNavVisibility(); + void dropdownHidden(); void updateDocSize(); void updateControls(); void updateActions(); - void displayPhoto(PhotoData *photo, HistoryItem *item); - void displayDocument(DocumentData *doc, HistoryItem *item); + void displayPhoto(not_null<PhotoData*> photo, HistoryItem *item); + void displayDocument(DocumentData *document, HistoryItem *item); void displayFinished(); void findCurrent(); void loadBack(); @@ -184,7 +197,7 @@ private: void updateThemePreviewGeometry(); void documentUpdated(DocumentData *doc); - void changingMsgId(HistoryItem *row, MsgId newId); + void changingMsgId(not_null<HistoryItem*> row, MsgId newId); // Radial animation interface. float64 radialProgress() const; @@ -230,7 +243,9 @@ private: PhotoData *_photo = nullptr; DocumentData *_doc = nullptr; - MediaOverviewType _overview = OverviewCount; + std::unique_ptr<SharedMedia> _sharedMedia; + base::optional<SharedMediaSlice> _sharedMediaData; + QRect _closeNav, _closeNavIcon; QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon; QRect _headerNav, _nameNav, _dateNav; @@ -308,7 +323,9 @@ private: PeerData *_from = nullptr; Text _fromName; - int _index = -1; // index in photos or files array, -1 if just photo + base::optional<int> _index; // Index in current _sharedMedia data. + base::optional<int> _fullIndex; // Index in full shared media. + base::optional<int> _fullCount; MsgId _msgid = 0; // msgId of current photo or file bool _msgmigrated = false; // msgId is from _migrated history ChannelId _channel = NoChannel; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 71f994483..d05e0bb6a 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -96,7 +96,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, Ui::ScrollArea *scroll, P subscribe(App::wnd()->dragFinished(), [this] { dragActionUpdate(QCursor::pos()); }); - subscribe(Auth().messageIdChanging, [this](std::pair<HistoryItem*, MsgId> update) { + subscribe(Auth().messageIdChanging, [this](std::pair<not_null<HistoryItem*>, MsgId> update) { changingMsgId(update.first, update.second); }); diff --git a/Telegram/SourceFiles/storage/storage_facade.cpp b/Telegram/SourceFiles/storage/storage_facade.cpp index 201accd64..4a5683f01 100644 --- a/Telegram/SourceFiles/storage/storage_facade.cpp +++ b/Telegram/SourceFiles/storage/storage_facade.cpp @@ -35,6 +35,10 @@ public: SharedMediaQuery &&query, base::lambda_once<void(SharedMediaResult&&)> &&callback); + base::Observable<SharedMediaSliceUpdate> &sharedMediaSliceUpdated(); + base::Observable<SharedMediaRemoveOne> &sharedMediaOneRemoved(); + base::Observable<SharedMediaRemoveAll> &sharedMediaAllRemoved(); + private: SharedMedia _sharedMedia; @@ -66,6 +70,17 @@ void Facade::Impl::query( _sharedMedia.query(query, std::move(callback)); } +base::Observable<SharedMediaSliceUpdate> &Facade::Impl::sharedMediaSliceUpdated() { + return _sharedMedia.sliceUpdated; +} + +base::Observable<SharedMediaRemoveOne> &Facade::Impl::sharedMediaOneRemoved() { + return _sharedMedia.oneRemoved; +} + +base::Observable<SharedMediaRemoveAll> &Facade::Impl::sharedMediaAllRemoved() { + return _sharedMedia.allRemoved; +} Facade::Facade() : _impl(std::make_unique<Impl>()) { } @@ -96,6 +111,18 @@ void Facade::query( _impl->query(std::move(query), std::move(callback)); } +base::Observable<SharedMediaSliceUpdate> &Facade::sharedMediaSliceUpdated() { + return _impl->sharedMediaSliceUpdated(); +} + +base::Observable<SharedMediaRemoveOne> &Facade::sharedMediaOneRemoved() { + return _impl->sharedMediaOneRemoved(); +} + +base::Observable<SharedMediaRemoveAll> &Facade::sharedMediaAllRemoved() { + return _impl->sharedMediaAllRemoved(); +} + Facade::~Facade() = default; } // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_facade.h b/Telegram/SourceFiles/storage/storage_facade.h index 52e9d3476..fb7a074cb 100644 --- a/Telegram/SourceFiles/storage/storage_facade.h +++ b/Telegram/SourceFiles/storage/storage_facade.h @@ -31,6 +31,7 @@ struct SharedMediaRemoveOne; struct SharedMediaRemoveAll; struct SharedMediaQuery; struct SharedMediaResult; +struct SharedMediaSliceUpdate; class Facade { public: @@ -45,6 +46,10 @@ public: SharedMediaQuery &&query, base::lambda_once<void(SharedMediaResult&&)> &&callback); + base::Observable<SharedMediaSliceUpdate> &sharedMediaSliceUpdated(); + base::Observable<SharedMediaRemoveOne> &sharedMediaOneRemoved(); + base::Observable<SharedMediaRemoveAll> &sharedMediaAllRemoved(); + ~Facade(); private: diff --git a/Telegram/SourceFiles/storage/storage_shared_media.cpp b/Telegram/SourceFiles/storage/storage_shared_media.cpp index e80e0bcc7..abfe86eb2 100644 --- a/Telegram/SourceFiles/storage/storage_shared_media.cpp +++ b/Telegram/SourceFiles/storage/storage_shared_media.cpp @@ -47,15 +47,16 @@ void SharedMedia::List::Slice::merge( template <typename Range> int SharedMedia::List::uniteAndAdd( + SliceUpdate &update, base::flat_set<Slice>::iterator uniteFrom, base::flat_set<Slice>::iterator uniteTill, const Range &messages, MsgRange noSkipRange) { + auto uniteFromIndex = uniteFrom - _slices.begin(); auto was = uniteFrom->messages.size(); _slices.modify(uniteFrom, [&](Slice &slice) { slice.merge(messages, noSkipRange); }); - auto result = uniteFrom->messages.size() - was; auto firstToErase = uniteFrom + 1; if (firstToErase != uniteTill) { for (auto it = firstToErase; it != uniteTill; ++it) { @@ -64,21 +65,21 @@ int SharedMedia::List::uniteAndAdd( }); } _slices.erase(firstToErase, uniteTill); + uniteFrom = _slices.begin() + uniteFromIndex; } - return result; + update.messages = &uniteFrom->messages; + update.range = uniteFrom->range; + return uniteFrom->messages.size() - was; } template <typename Range> int SharedMedia::List::addRangeItemsAndCount( + SliceUpdate &update, const Range &messages, MsgRange noSkipRange, base::optional<int> count) { Expects((noSkipRange.from < noSkipRange.till) || (noSkipRange.from == noSkipRange.till && messages.begin() == messages.end())); - - if (count) { - _count = count; - } if (noSkipRange.from == noSkipRange.till) { return 0; } @@ -92,7 +93,7 @@ int SharedMedia::List::addRangeItemsAndCount( noSkipRange.till, [](MsgId till, const Slice &slice) { return till < slice.range.from; }); if (uniteFrom < uniteTill) { - return uniteAndAdd(uniteFrom, uniteTill, messages, noSkipRange); + return uniteAndAdd(update, uniteFrom, uniteTill, messages, noSkipRange); } auto sliceMessages = base::flat_set<MsgId> { @@ -101,29 +102,39 @@ int SharedMedia::List::addRangeItemsAndCount( auto slice = _slices.emplace( std::move(sliceMessages), noSkipRange); + update.messages = &slice->messages; + update.range = slice->range; return slice->messages.size(); } template <typename Range> -int SharedMedia::List::addRange( +void SharedMedia::List::addRange( const Range &messages, MsgRange noSkipRange, - base::optional<int> count) { - auto result = addRangeItemsAndCount(messages, noSkipRange, count); + base::optional<int> count, + bool incrementCount) { + Expects(!count || !incrementCount); + + auto wasCount = _count; + auto update = SliceUpdate(); + auto result = addRangeItemsAndCount(update, messages, noSkipRange, count); + if (count) { + _count = count; + } else if (incrementCount && _count && result > 0) { + *_count += result; + } if (_slices.size() == 1) { if (_slices.front().range == MsgRange { 0, ServerMaxMsgId }) { _count = _slices.front().messages.size(); } } - return result; + update.count = _count; + sliceUpdated.notify(update, true); } void SharedMedia::List::addNew(MsgId messageId) { auto range = { messageId }; - auto added = addRange(range, { messageId, ServerMaxMsgId }, base::none); - if (added > 0 && _count) { - *_count += added; - } + addRange(range, { messageId, ServerMaxMsgId }, base::none, true); } void SharedMedia::List::addExisting( @@ -169,9 +180,9 @@ void SharedMedia::List::query( auto slice = base::lower_bound( _slices, - query.messageId, + query.key.messageId, [](const Slice &slice, MsgId id) { return slice.range.till < id; }); - if (slice != _slices.end() && slice->range.from <= query.messageId) { + if (slice != _slices.end() && slice->range.from <= query.key.messageId) { result = queryFromSlice(query, *slice); } else { result.count = _count; @@ -189,18 +200,20 @@ SharedMediaResult SharedMedia::List::queryFromSlice( const SharedMediaQuery &query, const Slice &slice) { auto result = SharedMediaResult {}; - auto position = base::lower_bound(slice.messages, query.messageId); - auto haveBefore = position - slice.messages.begin(); - auto haveEqualOrAfter = slice.messages.end() - position; + auto position = base::lower_bound(slice.messages, query.key.messageId); + auto haveBefore = int(position - slice.messages.begin()); + auto haveEqualOrAfter = int(slice.messages.end() - position); auto before = qMin(haveBefore, query.limitBefore); auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1); - result.messageIds.reserve(before + equalOrAfter); + auto ids = std::vector<MsgId>(); + ids.reserve(before + equalOrAfter); for ( auto from = position - before, till = position + equalOrAfter; from != till; ++from) { - result.messageIds.push_back(*from); + ids.push_back(*from); } + result.messageIds.merge(ids.begin(), ids.end()); if (slice.range.from == 0) { result.skippedBefore = haveBefore - before; } @@ -212,21 +225,41 @@ SharedMediaResult SharedMedia::List::queryFromSlice( if (!result.skippedBefore && result.skippedAfter) { result.skippedBefore = *result.count - *result.skippedAfter - - result.messageIds.size(); + - int(result.messageIds.size()); } else if (!result.skippedAfter && result.skippedBefore) { result.skippedAfter = *result.count - *result.skippedBefore - - result.messageIds.size(); + - int(result.messageIds.size()); } } return result; } -void SharedMedia::add(SharedMediaAddNew &&query) { - auto peerIt = _lists.find(query.peerId); - if (peerIt == _lists.end()) { - peerIt = _lists.emplace(query.peerId, Lists {}).first; +std::map<PeerId, SharedMedia::Lists>::iterator + SharedMedia::enforceLists(PeerId peer) { + auto result = _lists.find(peer); + if (result != _lists.end()) { + return result; } + result = _lists.emplace(peer, Lists {}).first; + for (auto index = 0; index != kSharedMediaTypeCount; ++index) { + auto &list = result->second[index]; + auto type = static_cast<SharedMediaType>(index); + subscribe(list.sliceUpdated, [this, type, peer](const SliceUpdate &update) { + sliceUpdated.notify(SharedMediaSliceUpdate( + peer, + type, + update.messages, + update.range, + update.count), true); + }); + } + return result; +} + +void SharedMedia::add(SharedMediaAddNew &&query) { + auto peer = query.peerId; + auto peerIt = enforceLists(peer); for (auto index = 0; index != kSharedMediaTypeCount; ++index) { auto type = static_cast<SharedMediaType>(index); if (query.types.test(type)) { @@ -236,10 +269,7 @@ void SharedMedia::add(SharedMediaAddNew &&query) { } void SharedMedia::add(SharedMediaAddExisting &&query) { - auto peerIt = _lists.find(query.peerId); - if (peerIt == _lists.end()) { - peerIt = _lists.emplace(query.peerId, Lists {}).first; - } + auto peerIt = enforceLists(query.peerId); for (auto index = 0; index != kSharedMediaTypeCount; ++index) { auto type = static_cast<SharedMediaType>(index); if (query.types.test(type)) { @@ -250,10 +280,7 @@ void SharedMedia::add(SharedMediaAddExisting &&query) { void SharedMedia::add(SharedMediaAddSlice &&query) { Expects(IsValidSharedMediaType(query.type)); - auto peerIt = _lists.find(query.peerId); - if (peerIt == _lists.end()) { - peerIt = _lists.emplace(query.peerId, Lists {}).first; - } + auto peerIt = enforceLists(query.peerId); auto index = static_cast<int>(query.type); peerIt->second[index].addSlice(std::move(query.messageIds), query.noSkipRange, query.count); } @@ -265,6 +292,7 @@ void SharedMedia::remove(SharedMediaRemoveOne &&query) { auto type = static_cast<SharedMediaType>(index); if (query.types.test(type)) { peerIt->second[index].removeOne(query.messageId); + oneRemoved.notify(query, true); } } } @@ -276,18 +304,19 @@ void SharedMedia::remove(SharedMediaRemoveAll &&query) { for (auto index = 0; index != kSharedMediaTypeCount; ++index) { peerIt->second[index].removeAll(); } + allRemoved.notify(query, true); } } void SharedMedia::query( const SharedMediaQuery &query, base::lambda_once<void(SharedMediaResult&&)> &&callback) { - Expects(IsValidSharedMediaType(query.type)); - auto peerIt = _lists.find(query.peerId); + Expects(IsValidSharedMediaType(query.key.type)); + auto peerIt = _lists.find(query.key.peerId); if (peerIt != _lists.end()) { - auto index = static_cast<int>(query.type); + auto index = static_cast<int>(query.key.type); peerIt->second[index].query(query, std::move(callback)); } } -} // namespace Storage \ No newline at end of file +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_shared_media.h b/Telegram/SourceFiles/storage/storage_shared_media.h index 1a40daac4..22b1b0707 100644 --- a/Telegram/SourceFiles/storage/storage_shared_media.h +++ b/Telegram/SourceFiles/storage/storage_shared_media.h @@ -122,25 +122,44 @@ struct SharedMediaRemoveAll { }; -struct SharedMediaQuery { - SharedMediaQuery( +struct SharedMediaKey { + SharedMediaKey( PeerId peerId, SharedMediaType type, - MsgId messageId, - int limitBefore, - int limitAfter) + MsgId messageId) : peerId(peerId) - , messageId(messageId) - , limitBefore(limitBefore) - , limitAfter(limitAfter) - , type(type) { + , type(type) + , messageId(messageId) { + } + + bool operator==(const SharedMediaKey &other) const { + return (peerId == other.peerId) + && (type == other.type) + && (messageId == other.messageId); + } + bool operator!=(const SharedMediaKey &other) const { + return !(*this == other); } PeerId peerId = 0; + SharedMediaType type = SharedMediaType::kCount; MsgId messageId = 0; + +}; + +struct SharedMediaQuery { + SharedMediaQuery( + SharedMediaKey key, + int limitBefore, + int limitAfter) + : key(key) + , limitBefore(limitBefore) + , limitAfter(limitAfter) { + } + + SharedMediaKey key; int limitBefore = 0; int limitAfter = 0; - SharedMediaType type = SharedMediaType::kCount; }; @@ -148,10 +167,31 @@ struct SharedMediaResult { base::optional<int> count; base::optional<int> skippedBefore; base::optional<int> skippedAfter; - std::vector<MsgId> messageIds; + base::flat_set<MsgId> messageIds; }; -class SharedMedia { +struct SharedMediaSliceUpdate { + SharedMediaSliceUpdate( + PeerId peerId, + SharedMediaType type, + const base::flat_set<MsgId> *messages, + MsgRange range, + base::optional<int> count) + : peerId(peerId) + , type(type) + , messages(messages) + , range(range) + , count(count) { + } + + PeerId peerId = 0; + SharedMediaType type = SharedMediaType::kCount; + const base::flat_set<MsgId> *messages = nullptr; + MsgRange range; + base::optional<int> count; +}; + +class SharedMedia : private base::Subscriber { public: using Type = SharedMediaType; @@ -164,6 +204,10 @@ public: const SharedMediaQuery &query, base::lambda_once<void(SharedMediaResult&&)> &&callback); + base::Observable<SharedMediaSliceUpdate> sliceUpdated; + base::Observable<SharedMediaRemoveOne> oneRemoved; + base::Observable<SharedMediaRemoveAll> allRemoved; + private: class List { public: @@ -179,6 +223,13 @@ private: const SharedMediaQuery &query, base::lambda_once<void(SharedMediaResult&&)> &&callback); + struct SliceUpdate { + const base::flat_set<MsgId> *messages = nullptr; + MsgRange range; + base::optional<int> count; + }; + base::Observable<SliceUpdate> sliceUpdated; + private: struct Slice { Slice(base::flat_set<MsgId> &&messages, MsgRange range); @@ -197,20 +248,23 @@ private: template <typename Range> int uniteAndAdd( + SliceUpdate &update, base::flat_set<Slice>::iterator uniteFrom, base::flat_set<Slice>::iterator uniteTill, const Range &messages, MsgRange noSkipRange); template <typename Range> int addRangeItemsAndCount( + SliceUpdate &update, const Range &messages, MsgRange noSkipRange, base::optional<int> count); template <typename Range> - int addRange( + void addRange( const Range &messages, MsgRange noSkipRange, - base::optional<int> count); + base::optional<int> count, + bool incrementCount = false); SharedMediaResult queryFromSlice( const SharedMediaQuery &query, @@ -220,8 +274,11 @@ private: base::flat_set<Slice> _slices; }; + using SliceUpdate = List::SliceUpdate; using Lists = std::array<List, kSharedMediaTypeCount>; + std::map<PeerId, Lists>::iterator enforceLists(PeerId peerId); + std::map<PeerId, Lists> _lists; }; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index ef644e171..a16df536d 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -186,6 +186,8 @@ <(src_loc)/history/history_service.h <(src_loc)/history/history_service_layout.cpp <(src_loc)/history/history_service_layout.h +<(src_loc)/history/history_shared_media.cpp +<(src_loc)/history/history_shared_media.h <(src_loc)/history/history_widget.cpp <(src_loc)/history/history_widget.h <(src_loc)/inline_bots/inline_bot_layout_internal.cpp