From 2690618da2ad878f09d6f2d0febfd71912a48e87 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 29 Aug 2017 22:52:52 +0300 Subject: [PATCH] Add Storage::UserPhotos and UserPhotosSlice. --- Telegram/SourceFiles/apiwrap.cpp | 66 ++- Telegram/SourceFiles/apiwrap.h | 11 + .../history/history_shared_media.cpp | 20 +- .../history/history_shared_media.h | 3 - .../history/history_user_photos.cpp | 189 +++++++ .../SourceFiles/history/history_user_photos.h | 90 ++++ Telegram/SourceFiles/mainwidget.cpp | 30 +- Telegram/SourceFiles/mediaview.cpp | 481 +++++++----------- Telegram/SourceFiles/mediaview.h | 29 +- .../SourceFiles/storage/storage_facade.cpp | 64 +++ Telegram/SourceFiles/storage/storage_facade.h | 18 + .../storage/storage_shared_media.cpp | 16 +- .../storage/storage_shared_media.h | 7 +- .../storage/storage_user_photos.cpp | 176 +++++++ .../SourceFiles/storage/storage_user_photos.h | 184 +++++++ Telegram/SourceFiles/structs.h | 4 - Telegram/gyp/telegram_sources.txt | 4 + 17 files changed, 1008 insertions(+), 384 deletions(-) create mode 100644 Telegram/SourceFiles/history/history_user_photos.cpp create mode 100644 Telegram/SourceFiles/history/history_user_photos.h create mode 100644 Telegram/SourceFiles/storage/storage_user_photos.cpp create mode 100644 Telegram/SourceFiles/storage/storage_user_photos.h diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 9fd29eb9e5..c5f6f6c37d 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "chat_helpers/stickers.h" #include "storage/storage_facade.h" #include "storage/storage_shared_media.h" +#include "storage/storage_user_photos.h" namespace { @@ -48,7 +49,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; +constexpr auto kSharedMediaLimit = 100; } // namespace @@ -1963,7 +1964,6 @@ void ApiWrap::requestSharedMedia( Unexpected("Slice type in ApiWrap::requestSharedMedia"); }(); - LOG(("REQUESTING SHARED MEDIA: %1, %2, %3").arg(static_cast(type)).arg(messageId).arg(static_cast(slice))); auto requestId = request(MTPmessages_Search( MTP_flags(0), peer->input, @@ -1992,7 +1992,6 @@ void ApiWrap::sharedMediaDone( MsgId messageId, SliceType slice, const MTPmessages_Messages &result) { - auto fullCount = 0; auto &messages = *[&] { switch (result.type()) { @@ -2064,4 +2063,65 @@ void ApiWrap::sharedMediaDone( )); } +void ApiWrap::requestUserPhotos( + not_null user, + PhotoId afterId) { + if (_userPhotosRequests.contains(user)) { + return; + } + + auto limit = kSharedMediaLimit; + + auto requestId = request(MTPphotos_GetUserPhotos( + user->inputUser, + MTP_int(0), + MTP_long(afterId), + MTP_int(limit) + )).done([this, user, afterId](const MTPphotos_Photos &result) { + _userPhotosRequests.remove(user); + userPhotosDone(user, afterId, result); + }).fail([this, user](const RPCError &error) { + _userPhotosRequests.remove(user); + }).send(); + _userPhotosRequests.emplace(user, requestId); +} + +void ApiWrap::userPhotosDone( + not_null user, + PhotoId photoId, + const MTPphotos_Photos &result) { + auto fullCount = 0; + auto &photos = *[&] { + switch (result.type()) { + case mtpc_photos_photos: { + auto &d = result.c_photos_photos(); + App::feedUsers(d.vusers); + fullCount = d.vphotos.v.size(); + return &d.vphotos.v; + } break; + + case mtpc_photos_photosSlice: { + auto &d = result.c_photos_photosSlice(); + App::feedUsers(d.vusers); + fullCount = d.vcount.v; + return &d.vphotos.v; + } break; + } + Unexpected("photos.Photos type in userPhotosDone()"); + }(); + + auto photoIds = std::vector(); + photoIds.reserve(photos.size()); + for (auto &photo : photos) { + if (auto photoData = App::feedPhoto(photo)) { + photoIds.push_back(photoData->id); + } + } + Auth().storage().add(Storage::UserPhotosAddSlice( + user->id, + std::move(photoIds), + fullCount + )); +} + ApiWrap::~ApiWrap() = default; diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index d71493569d..1ea453c1d6 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -128,6 +128,10 @@ public: requestSharedMedia(peer, type, 0, SliceType::Before); } + void requestUserPhotos( + not_null user, + PhotoId afterId); + ~ApiWrap(); private: @@ -181,6 +185,11 @@ private: SliceType slice, const MTPmessages_Messages &result); + void userPhotosDone( + not_null user, + PhotoId photoId, + const MTPphotos_Photos &result); + not_null _session; mtpRequestId _changelogSubscription = 0; @@ -249,6 +258,8 @@ private: MsgId, SliceType>, mtpRequestId> _sharedMediaRequests; + base::flat_map, mtpRequestId> _userPhotosRequests; + base::Observable _fullPeerUpdated; }; diff --git a/Telegram/SourceFiles/history/history_shared_media.cpp b/Telegram/SourceFiles/history/history_shared_media.cpp index 3050d68029..b3ea72123e 100644 --- a/Telegram/SourceFiles/history/history_shared_media.cpp +++ b/Telegram/SourceFiles/history/history_shared_media.cpp @@ -43,24 +43,6 @@ inline MediaOverviewType SharedMediaTypeToOverview(Type type) { return OverviewCount; } -not_null GetActualHistory(not_null history) { - if (auto to = history->peer->migrateTo()) { - return App::history(to); - } - return history; -} - -History *GetMigratedHistory( - not_null passedHistory, - not_null actualHistory) { - if (actualHistory != passedHistory) { - return passedHistory; - } else if (auto from = actualHistory->peer->migrateFrom()) { - return App::history(from); - } - return nullptr; -} - } // namespace base::optional SharedMediaOverviewType( @@ -133,7 +115,7 @@ QString SharedMediaSlice::debug() const { ? QString::number((*this)[0]) + " .. " + QString::number((*this)[size() - 1]) : (size() > 1) ? QString::number((*this)[0]) + ' ' + QString::number((*this)[1]) - : ((size() > 0) ? QString((*this)[0]) : QString()); + : ((size() > 0) ? QString::number((*this)[0]) : QString()); return before + middle + after; } diff --git a/Telegram/SourceFiles/history/history_shared_media.h b/Telegram/SourceFiles/history/history_shared_media.h index 67872caae2..e4abec3bb4 100644 --- a/Telegram/SourceFiles/history/history_shared_media.h +++ b/Telegram/SourceFiles/history/history_shared_media.h @@ -21,7 +21,6 @@ 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 SharedMediaOverviewType( @@ -102,8 +101,6 @@ private: Key _key; int _limitBefore = 0; int _limitAfter = 0; - mtpRequestId _beforeRequestId = 0; - mtpRequestId _afterRequestId = 0; SharedMediaSlice _data; }; diff --git a/Telegram/SourceFiles/history/history_user_photos.cpp b/Telegram/SourceFiles/history/history_user_photos.cpp new file mode 100644 index 0000000000..e0f73edb12 --- /dev/null +++ b/Telegram/SourceFiles/history/history_user_photos.cpp @@ -0,0 +1,189 @@ +/* +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_user_photos.h" + +#include "auth_session.h" +#include "apiwrap.h" +#include "storage/storage_facade.h" +#include "storage/storage_user_photos.h" + +UserPhotosSlice::UserPhotosSlice(Key key) : UserPhotosSlice(key, base::none) { +} + +UserPhotosSlice::UserPhotosSlice( + Key key, + base::optional fullCount) + : _key(key) + , _fullCount(fullCount) { +} + +base::optional UserPhotosSlice::indexOf(PhotoId photoId) const { + auto it = base::find(_ids, photoId); + if (it != _ids.end()) { + return (it - _ids.begin()); + } + return base::none; +} + +PhotoId UserPhotosSlice::operator[](int index) const { + Expects(index >= 0 && index < size()); + + return *(_ids.begin() + index); +} + +base::optional UserPhotosSlice::distance(const Key &a, const Key &b) const { + if (a.userId != _key.userId + || b.userId != _key.userId) { + return base::none; + } + if (auto i = indexOf(a.photoId)) { + if (auto j = indexOf(b.photoId)) { + return *j - *i; + } + } + return base::none; +} + +QString UserPhotosSlice::debug() const { + auto before = _skippedBefore + ? (*_skippedBefore + ? ('(' + QString::number(*_skippedBefore) + ").. ") + : QString()) + : QString(".. "); + auto after = _skippedAfter + ? (" ..(" + QString::number(_skippedAfter) + ')') + : QString(" .."); + auto middle = (size() > 2) + ? QString::number((*this)[0]) + " .. " + QString::number((*this)[size() - 1]) + : (size() > 1) + ? QString::number((*this)[0]) + ' ' + QString::number((*this)[1]) + : ((size() > 0) ? QString::number((*this)[0]) : QString()); + return before + middle + after; +} + +UserPhotosViewer::UserPhotosViewer( + Key key, + int limitBefore, + int limitAfter) + : _key(key) + , _limitBefore(limitBefore) + , _limitAfter(limitAfter) + , _data(_key) { +} + +void UserPhotosViewer::start() { + auto applyUpdateCallback = [this](auto &update) { + this->applyUpdate(update); + }; + subscribe(Auth().storage().userPhotosSliceUpdated(), applyUpdateCallback); + + loadInitial(); +} + +void UserPhotosViewer::loadInitial() { + auto weak = base::make_weak_unique(this); + Auth().storage().query(Storage::UserPhotosQuery( + _key, + _limitBefore, + _limitAfter), [weak](Storage::UserPhotosResult &&result) { + if (weak) { + weak->applyStoredResult(std::move(result)); + } + }); +} + +void UserPhotosViewer::applyStoredResult(Storage::UserPhotosResult &&result) { + mergeSliceData( + result.count, + result.photoIds, + result.skippedBefore, + result.skippedAfter); +} + +void UserPhotosViewer::mergeSliceData( + base::optional count, + const std::deque &photoIds, + base::optional skippedBefore, + int skippedAfter) { + if (photoIds.empty()) { + if (_data._fullCount != count) { + _data._fullCount = count; + if (_data._fullCount && *_data._fullCount <= _data.size()) { + _data._fullCount = _data.size(); + _data._skippedBefore = _data._skippedAfter = 0; + } + updated.notify(_data); + } + sliceToLimits(); + return; + } + if (count) { + _data._fullCount = count; + } + _data._skippedAfter = skippedAfter; + _data._ids = photoIds; + + if (_data._fullCount) { + _data._skippedBefore = *_data._fullCount + - _data._skippedAfter + - int(_data._ids.size()); + } + + sliceToLimits(); + + updated.notify(_data); +} + +void UserPhotosViewer::applyUpdate(const SliceUpdate &update) { + if (update.userId != _key.userId) { + return; + } + auto idsCount = update.photoIds ? int(update.photoIds->size()) : 0; + mergeSliceData( + update.count, + update.photoIds ? *update.photoIds : std::deque {}, + update.count | func::add(-idsCount), + 0); +} + +void UserPhotosViewer::sliceToLimits() { + auto aroundIt = base::find(_data._ids, _key.photoId); + auto removeFromBegin = (aroundIt - _data._ids.begin() - _limitBefore); + auto removeFromEnd = (_data._ids.end() - aroundIt - _limitAfter - 1); + if (removeFromEnd > 0) { + _data._ids.erase(_data._ids.end() - removeFromEnd, _data._ids.end()); + _data._skippedAfter += removeFromEnd; + } + 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)) { + requestPhotos(); + } +} + +void UserPhotosViewer::requestPhotos() { + Auth().api().requestUserPhotos( + App::user(_key.userId), + _data._ids.empty() ? 0 : _data._ids.front()); +} diff --git a/Telegram/SourceFiles/history/history_user_photos.h b/Telegram/SourceFiles/history/history_user_photos.h new file mode 100644 index 0000000000..1102a0458c --- /dev/null +++ b/Telegram/SourceFiles/history/history_user_photos.h @@ -0,0 +1,90 @@ +/* +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_user_photos.h" +#include "base/weak_unique_ptr.h" + +class UserPhotosViewer; +class UserPhotosSlice { +public: + using Key = Storage::UserPhotosKey; + + UserPhotosSlice(Key key); + UserPhotosSlice(Key key, base::optional fullCount); + + const Key &key() const { return _key; } + + base::optional fullCount() const { return _fullCount; } + base::optional skippedBefore() const { return _skippedBefore; } + int skippedAfter() const { return _skippedAfter; } + base::optional indexOf(PhotoId msgId) const; + int size() const { return _ids.size(); } + PhotoId operator[](int index) const; + base::optional distance(const Key &a, const Key &b) const; + + QString debug() const; + +private: + Key _key; + std::deque _ids; + base::optional _fullCount; + base::optional _skippedBefore; + int _skippedAfter = 0; + + friend class UserPhotosViewer; + +}; + +class UserPhotosViewer : + private base::Subscriber, + public base::enable_weak_from_this { +public: + using Key = Storage::UserPhotosKey; + + UserPhotosViewer(Key key, int limitBefore, int limitAfter); + + void start(); + + base::Observable updated; + +private: + using InitialResult = Storage::UserPhotosResult; + using SliceUpdate = Storage::UserPhotosSliceUpdate; + + void loadInitial(); + void requestPhotos(); + void applyStoredResult(InitialResult &&result); + void applyUpdate(const SliceUpdate &update); + void sliceToLimits(); + + void mergeSliceData( + base::optional count, + const std::deque &photoIds, + base::optional skippedBefore, + int skippedAfter); + + Key _key; + int _limitBefore = 0; + int _limitAfter = 0; + UserPhotosSlice _data; + +}; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index df9699de97..bb02010b27 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -77,6 +77,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "storage/storage_facade.h" #include "storage/storage_shared_media.h" +#include "storage/storage_user_photos.h" namespace { @@ -969,13 +970,8 @@ void MainWidget::deletePhotoLayer(PhotoData *photo) { } else if (photo->peer && !photo->peer->isUser() && photo->peer->photoId == photo->id) { Messenger::Instance().peerClearPhoto(photo->peer->id); } else { - for (int i = 0, l = me->photos.size(); i != l; ++i) { - if (me->photos.at(i) == photo) { - me->photos.removeAt(i); - MTP::send(MTPphotos_DeletePhotos(MTP_vector(1, MTP_inputPhoto(MTP_long(photo->id), MTP_long(photo->access))))); - break; - } - } + MTP::send(MTPphotos_DeletePhotos(MTP_vector(1, MTP_inputPhoto(MTP_long(photo->id), MTP_long(photo->access))))); + Auth().storage().remove(Storage::UserPhotosRemoveOne(me->bareId(), photo->id)); } }))); } @@ -5091,19 +5087,17 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (auto user = App::userLoaded(d.vuser_id.v)) { user->setPhoto(d.vphoto); user->loadUserpic(); - if (mtpIsTrue(d.vprevious)) { - user->photosCount = -1; - user->photos.clear(); + if (mtpIsTrue(d.vprevious) + || !user->photoId + || user->photoId == UnknownPeerPhotoId) { + Auth().storage().remove(Storage::UserPhotosRemoveAfter( + user->bareId(), + user->photoId)); } else { - if (user->photoId && user->photoId != UnknownPeerPhotoId) { - if (user->photosCount > 0) ++user->photosCount; - user->photos.push_front(App::photo(user->photoId)); - } else { - user->photosCount = -1; - user->photos.clear(); - } + Auth().storage().add(Storage::UserPhotosAddNew( + user->bareId(), + user->photoId)); } - Notify::mediaOverviewUpdated(user, OverviewCount); } } break; diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 64a6658e25..d89365bcb4 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -77,6 +77,16 @@ struct MediaView::SharedMedia { SharedMediaViewerWithLast slice; }; +struct MediaView::UserPhotos { + UserPhotos(UserPhotosViewer::Key key) + : key(key) + , slice(key, kIdsLimit, kIdsLimit) { + } + + UserPhotosViewer::Key key; + UserPhotosViewer slice; +}; + MediaView::MediaView() : TWidget(nullptr) , _transparentBrush(style::transparentPlaceholderBrush()) , _animStarted(getms()) @@ -121,6 +131,7 @@ MediaView::MediaView() : TWidget(nullptr) }); } else { _sharedMedia = nullptr; + _userPhotos = nullptr; } }; subscribe(Messenger::Instance().authSessionChanged(), [handleAuthSessionChange] { @@ -192,33 +203,6 @@ void MediaView::moveToScreen() { _saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2); } -void MediaView::handleSharedMediaUpdate(const SharedMediaSliceWithLast &update) { - if (isHidden() || (!_photo && !_doc) || !_sharedMedia) { - _index = _fullIndex = _fullCount = base::none; - return; - } - - _sharedMediaData = update; - - 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 { return !_current.isNull() || gifShown(); } @@ -267,7 +251,7 @@ void MediaView::documentUpdated(DocumentData *doc) { void MediaView::changingMsgId(not_null row, MsgId newId) { if (row->fullId() == _msgid) { _msgid = FullMsgId(_msgid.channel, newId); - refreshSharedMedia(); + refreshMediaViewer(); } } @@ -308,11 +292,13 @@ void MediaView::refreshNavVisibility() { if (_sharedMediaData) { _leftNavVisible = _index && (*_index > 0); _rightNavVisible = _index && (*_index + 1 < _sharedMediaData->size()); + } else if (_userPhotosData) { + _leftNavVisible = _index && (*_index > 0); + _rightNavVisible = _index && (*_index + 1 < _userPhotosData->size()); } else { _leftNavVisible = false; _rightNavVisible = false; } - // TODO user } void MediaView::updateControls() { @@ -408,10 +394,24 @@ void MediaView::updateActions() { if ((_doc && fileShown()) || (_photo && _photo->loaded())) { _actions.push_back({ lang(lng_mediaview_copy), SLOT(onCopy()) }); } - if (_canForward) { + if (_canForwardItem) { _actions.push_back({ lang(lng_mediaview_forward), SLOT(onForward()) }); } - if (_canDelete || (_photo && App::self() && _user == App::self()) || (_photo && _photo->peer && _photo->peer->photoId == _photo->id && (_photo->peer->isChat() || (_photo->peer->isChannel() && _photo->peer->asChannel()->amCreator())))) { + auto canDelete = [&] { + if (_canDeleteItem) { + return true; + } else if (!_msgid && _photo && App::self() && _user == App::self()) { + return _userPhotosData && _fullIndex && _fullCount; + } else if (_photo && _photo->peer && _photo->peer->photoId == _photo->id) { + if (auto chat = _photo->peer->asChat()) { + return chat->canEdit(); + } else if (auto channel = _photo->peer->asChannel()) { + return channel->canEditInformation(); + } + } + return false; + }(); + if (canDelete) { _actions.push_back({ lang(lng_mediaview_delete), SLOT(onDelete()) }); } _actions.push_back({ lang(lng_mediaview_save_as), SLOT(onSaveAs()) }); @@ -652,6 +652,9 @@ void MediaView::updateMixerVideoVolume() const { void MediaView::close() { _sharedMedia = nullptr; + _sharedMediaData = base::none; + _userPhotos = nullptr; + _userPhotosData = base::none; if (_menu) _menu->hideMenu(true); Messenger::Instance().hideMediaView(); } @@ -1071,10 +1074,80 @@ void MediaView::validateSharedMedia() { } } -void MediaView::refreshSharedMedia() { +void MediaView::handleSharedMediaUpdate(const SharedMediaSliceWithLast &update) { + if (isHidden() || (!_photo && !_doc) || !_sharedMedia) { + _sharedMediaData = base::none; + } else { + _sharedMediaData = update; + } + findCurrent(); + updateControls(); + preloadData(0); +} + +base::optional MediaView::userPhotosKey() const { + if (!_msgid && _user && _photo) { + return UserPhotosKey { + _user->bareId(), + _photo->id + }; + } + return base::none; +} + +bool MediaView::validUserPhotos() const { + if (auto key = userPhotosKey()) { + if (!_userPhotos) { + return false; + } + auto countDistanceInData = [](const auto &a, const auto &b) { + return [&](const UserPhotosSlice &data) { + return data.distance(a, b); + }; + }; + + auto distance = (key == _userPhotos->key) ? 0 : + _userPhotosData + | countDistanceInData(*key, _userPhotos->key) + | func::abs; + if (distance) { + return (*distance < kIdsPreloadAfter); + } + } + return (_userPhotos == nullptr); +} + +void MediaView::validateUserPhotos() { + if (auto key = userPhotosKey()) { + _userPhotos = std::make_unique(*key); + subscribe(_userPhotos->slice.updated, [this](const UserPhotosSlice &data) { + handleUserPhotosUpdate(data); + }); + _userPhotos->slice.start(); + } else { + _userPhotos = nullptr; + _userPhotosData = base::none; + } +} + +void MediaView::handleUserPhotosUpdate(const UserPhotosSlice &update) { + if (isHidden() || !_photo || !_userPhotos) { + _userPhotosData = base::none; + } else { + _userPhotosData = update; + } + findCurrent(); + updateControls(); + preloadData(0); +} + +void MediaView::refreshMediaViewer() { if (!validSharedMedia()) { validateSharedMedia(); } + if (!validUserPhotos()) { + validateUserPhotos(); + } findCurrent(); updateControls(); preloadData(0); @@ -1098,7 +1171,7 @@ void MediaView::showPhoto(not_null photo, HistoryItem *context) { _photo = photo; - refreshSharedMedia(); + refreshMediaViewer(); displayPhoto(photo, context); preloadData(0); @@ -1121,22 +1194,8 @@ void MediaView::showPhoto(not_null photo, not_null contex _photo = photo; - refreshSharedMedia(); - 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; - // } - //} + refreshMediaViewer(); - //if (_user->photosCount < 0) { - // loadBack(); - //} // TODO user - } displayPhoto(photo, 0); preloadData(0); activateControls(); @@ -1178,7 +1237,7 @@ void MediaView::displayPhoto(not_null photo, HistoryItem *item) { _photo = photo; _radial.stop(); - refreshSharedMedia(); + refreshMediaViewer(); _photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize); @@ -1253,7 +1312,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty _photo = nullptr; _radial.stop(); - refreshSharedMedia(); + refreshMediaViewer(); if (_autoplayVideoDocument && _doc != _autoplayVideoDocument) { _autoplayVideoDocument = nullptr; @@ -2146,8 +2205,25 @@ void MediaView::setZoomLevel(int newZoom) { update(); } -MediaView::Entity MediaView::entityForSharedMediaValue( - SharedMediaSliceWithLast::Value value) const { +MediaView::Entity MediaView::entityForUserPhotos(int index) const { + Expects(!!_userPhotosData); + + if (index < 0 || index >= _userPhotosData->size()) { + return { base::none, nullptr }; + } + if (auto photo = App::photo((*_userPhotosData)[index])) { + return { photo, nullptr }; + } + return { base::none, nullptr }; +} + +MediaView::Entity MediaView::entityForSharedMedia(int index) const { + Expects(!!_sharedMediaData); + + if (index < 0 || index >= _sharedMediaData->size()) { + return { base::none, nullptr }; + } + auto value = (*_sharedMediaData)[index]; if (auto photo = base::get_if>(&value)) { // Last peer photo. return { *photo, nullptr }; @@ -2171,23 +2247,32 @@ MediaView::Entity MediaView::entityForSharedMediaValue( return { base::none, nullptr }; } +MediaView::Entity MediaView::entityByIndex(int index) const { + if (_sharedMediaData) { + return entityForSharedMedia(index); + } else if (_userPhotosData) { + return entityForUserPhotos(index); + } + return { base::none, nullptr }; +} + void MediaView::setContext(base::optional_variant< not_null, not_null> context) { if (auto item = base::get_if>(&context)) { _msgid = (*item)->fullId(); - _canForward = (*item)->canForward(); - _canDelete = (*item)->canDelete(); + _canForwardItem = (*item)->canForward(); + _canDeleteItem = (*item)->canDelete(); _history = (*item)->history(); _peer = _history->peer; } else if (auto peer = base::get_if>(&context)) { _msgid = FullMsgId(); - _canForward = _canDelete = false; + _canForwardItem = _canDeleteItem = false; _history = App::history(*peer); _peer = *peer; } else { _msgid = FullMsgId(); - _canForward = _canDelete = false; + _canForwardItem = _canDeleteItem = false; _history = nullptr; _peer = nullptr; } @@ -2208,10 +2293,7 @@ bool MediaView::moveToNext(int32 delta) { return false; } auto newIndex = *_index + delta; - if (newIndex < 0 || newIndex >= _sharedMediaData->size()) { - return false; - } - auto entity = entityForSharedMediaValue((*_sharedMediaData)[newIndex]); + auto entity = entityByIndex(*_index + delta); if (!entity.data && !entity.item) { return false; } @@ -2233,94 +2315,6 @@ bool MediaView::moveToNext(int32 delta) { } preloadData(delta); return true; - - //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(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) { @@ -2332,47 +2326,26 @@ void MediaView::preloadData(int32 delta) { if (from > till) std::swap(from, till); auto forgetIndex = *_index - delta * 2; - if (forgetIndex >= 0 && forgetIndex < _sharedMediaData->size()) { - auto entity = entityForSharedMediaValue((*_sharedMediaData)[forgetIndex]); - if (auto photo = base::get_if>(&entity.data)) { - (*photo)->forget(); - } else if (auto document = base::get_if>(&entity.data)) { - (*document)->forget(); - } + auto entity = entityByIndex(forgetIndex); + if (auto photo = base::get_if>(&entity.data)) { + (*photo)->forget(); + } else if (auto document = base::get_if>(&entity.data)) { + (*document)->forget(); } for (auto index = from; index != till; ++index) { - if (index >= 0 && index < _sharedMediaData->size()) { - auto entity = entityForSharedMediaValue((*_sharedMediaData)[index]); - if (auto photo = base::get_if>(&entity.data)) { - (*photo)->download(); - } else if (auto document = base::get_if>(&entity.data)) { - if (auto sticker = (*document)->sticker()) { - sticker->img->load(); - } else { - (*document)->thumb->load(); - (*document)->automaticLoad(entity.item); - } + auto entity = entityByIndex(index); + if (auto photo = base::get_if>(&entity.data)) { + (*photo)->download(); + } else if (auto document = base::get_if>(&entity.data)) { + if (auto sticker = (*document)->sticker()) { + sticker->img->load(); + } else { + (*document)->thumb->load(); + (*document)->automaticLoad(entity.item); } } } - - //} 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) { @@ -2802,128 +2775,28 @@ void MediaView::updateImage() { } void MediaView::findCurrent() { - if (!_sharedMediaData) { - _index = _fullIndex = _fullCount = base::none; - return; - } - _index = _msgid - ? _sharedMediaData->indexOf(_msgid) - : _photo ? _sharedMediaData->indexOf(_photo) : base::none; - if (_index && _sharedMediaData->skippedBefore()) { - _fullIndex = (*_index + *_sharedMediaData->skippedBefore()); + if (_sharedMediaData) { + _index = _msgid + ? _sharedMediaData->indexOf(_msgid) + : _photo ? _sharedMediaData->indexOf(_photo) : base::none; + _fullIndex = _sharedMediaData->skippedBefore() + ? (_index | func::add(*_sharedMediaData->skippedBefore())) + : base::none; + _fullCount = _sharedMediaData->fullCount(); + } else if (_userPhotosData) { + _index = _photo ? _userPhotosData->indexOf(_photo->id) : base::none; + _fullIndex = _userPhotosData->skippedBefore() + ? (_index | func::add(*_userPhotosData->skippedBefore())) + : base::none; + _fullCount = _userPhotosData->fullCount(); } else { - _fullIndex = base::none; + _index = _fullIndex = _fullCount = 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 (_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 -} - -void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req) { - if (req == _loadRequest) { - _loadRequest = 0; - } - - const QVector *v = nullptr; - switch (photos.type()) { - case mtpc_photos_photos: { - auto &d = photos.c_photos_photos(); - App::feedUsers(d.vusers); - v = &d.vphotos.v; - u->photosCount = 0; - } break; - - case mtpc_photos_photosSlice: { - auto &d = photos.c_photos_photosSlice(); - App::feedUsers(d.vusers); - u->photosCount = d.vcount.v; - v = &d.vphotos.v; - } break; - - default: return; - } - - if (v->isEmpty()) { - u->photosCount = 0; - } - - for (auto i = v->cbegin(), e = v->cend(); i != e; ++i) { - auto photo = App::feedPhoto(*i); - photo->thumb->load(); - u->photos.push_back(photo); - } - Notify::mediaOverviewUpdated(u, OverviewCount); } void MediaView::updateHeader() { auto index = _fullIndex ? *_fullIndex : -1; auto count = _fullCount ? *_fullCount : -1; - if (_history) { - } else if (_user) { - count = _user->photosCount ? _user->photosCount : _user->photos.size(); // TODO user - } if (index >= 0 && index < count && count > 1) { if (_doc) { _headerText = lng_mediaview_file_n_of_count(lt_file, _doc->name.isEmpty() ? lang(lng_mediaview_doc_image) : _doc->name, lt_n, QString::number(index + 1), lt_count, QString::number(count)); @@ -2959,15 +2832,3 @@ float64 MediaView::overLevel(OverState control) const { auto i = _animOpacities.constFind(control); return (i == _animOpacities.cend()) ? (_over == control ? 1 : 0) : i->current(); } - -MsgId MediaView::getMsgIdFromOverview(not_null 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; - //} - return 0; -} diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 638b222254..e92a3e511e 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -23,6 +23,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" +#include "history/history_user_photos.h" namespace Media { namespace Player { @@ -156,7 +157,9 @@ private: not_null> data; HistoryItem *item; }; - Entity entityForSharedMediaValue(SharedMediaSliceWithLast::Value value) const; + Entity entityForUserPhotos(int index) const; + Entity entityForSharedMedia(int index) const; + Entity entityByIndex(int index) const; void setContext(base::optional_variant< not_null, not_null> context); @@ -170,11 +173,18 @@ private: using SharedMediaKey = SharedMediaViewerWithLast::Key; base::optional sharedMediaType() const; base::optional sharedMediaKey() const; - void validateSharedMedia(); bool validSharedMedia() const; - std::unique_ptr createSharedMedia() const; - void refreshSharedMedia(); + void validateSharedMedia(); void handleSharedMediaUpdate(const SharedMediaSliceWithLast &update); + + struct UserPhotos; + using UserPhotosKey = UserPhotosViewer::Key; + base::optional userPhotosKey() const; + bool validUserPhotos() const; + void validateUserPhotos(); + void handleUserPhotosUpdate(const UserPhotosSlice &update); + + void refreshMediaViewer(); void refreshNavVisibility(); void dropdownHidden(); @@ -186,7 +196,6 @@ private: void displayDocument(DocumentData *document, HistoryItem *item); void displayFinished(); void findCurrent(); - void loadBack(); void updateCursor(); void setZoomLevel(int newZoom); @@ -216,8 +225,6 @@ private: void radialStart(); TimeMs radialTimeShift() const; - void userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req); - void deletePhotosDone(const MTPVector &result); bool deletePhotosFail(const RPCError &error); @@ -239,14 +246,14 @@ private: bool updateOverState(OverState newState); float64 overLevel(OverState control) const; - MsgId getMsgIdFromOverview(not_null history, int index) const; - QBrush _transparentBrush; PhotoData *_photo = nullptr; DocumentData *_doc = nullptr; std::unique_ptr _sharedMedia; base::optional _sharedMediaData; + std::unique_ptr _userPhotos; + base::optional _userPhotosData; QRect _closeNav, _closeNavIcon; QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon; @@ -325,8 +332,8 @@ private: base::optional _fullIndex; // Index in full shared media. base::optional _fullCount; FullMsgId _msgid; - bool _canForward = false; - bool _canDelete = false; + bool _canForwardItem = false; + bool _canDeleteItem = false; mtpRequestId _loadRequest = 0; diff --git a/Telegram/SourceFiles/storage/storage_facade.cpp b/Telegram/SourceFiles/storage/storage_facade.cpp index 4a5683f014..d967f3beae 100644 --- a/Telegram/SourceFiles/storage/storage_facade.cpp +++ b/Telegram/SourceFiles/storage/storage_facade.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "storage/storage_facade.h" #include "storage/storage_shared_media.h" +#include "storage/storage_user_photos.h" namespace Storage { @@ -39,8 +40,19 @@ public: base::Observable &sharedMediaOneRemoved(); base::Observable &sharedMediaAllRemoved(); + void add(UserPhotosAddNew &&query); + void add(UserPhotosAddSlice &&query); + void remove(UserPhotosRemoveOne &&query); + void remove(UserPhotosRemoveAfter &&query); + void query( + UserPhotosQuery &&query, + base::lambda_once &&callback); + + base::Observable &userPhotosSliceUpdated(); + private: SharedMedia _sharedMedia; + UserPhotos _userPhotos; }; @@ -82,6 +94,32 @@ base::Observable &Facade::Impl::sharedMediaAllRemoved() { return _sharedMedia.allRemoved; } +void Facade::Impl::add(UserPhotosAddNew &&query) { + return _userPhotos.add(std::move(query)); +} + +void Facade::Impl::add(UserPhotosAddSlice &&query) { + return _userPhotos.add(std::move(query)); +} + +void Facade::Impl::remove(UserPhotosRemoveOne &&query) { + return _userPhotos.remove(std::move(query)); +} + +void Facade::Impl::remove(UserPhotosRemoveAfter &&query) { + return _userPhotos.remove(std::move(query)); +} + +void Facade::Impl::query( + UserPhotosQuery &&query, + base::lambda_once &&callback) { + return _userPhotos.query(std::move(query), std::move(callback)); +} + +base::Observable &Facade::Impl::userPhotosSliceUpdated() { + return _userPhotos.sliceUpdated; +} + Facade::Facade() : _impl(std::make_unique()) { } @@ -123,6 +161,32 @@ base::Observable &Facade::sharedMediaAllRemoved() { return _impl->sharedMediaAllRemoved(); } +void Facade::add(UserPhotosAddNew &&query) { + return _impl->add(std::move(query)); +} + +void Facade::add(UserPhotosAddSlice &&query) { + return _impl->add(std::move(query)); +} + +void Facade::remove(UserPhotosRemoveOne &&query) { + return _impl->remove(std::move(query)); +} + +void Facade::remove(UserPhotosRemoveAfter &&query) { + return _impl->remove(std::move(query)); +} + +void Facade::query( + UserPhotosQuery &&query, + base::lambda_once &&callback) { + return _impl->query(std::move(query), std::move(callback)); +} + +base::Observable &Facade::userPhotosSliceUpdated() { + return _impl->userPhotosSliceUpdated(); +} + Facade::~Facade() = default; } // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_facade.h b/Telegram/SourceFiles/storage/storage_facade.h index fb7a074cbf..66b1851b11 100644 --- a/Telegram/SourceFiles/storage/storage_facade.h +++ b/Telegram/SourceFiles/storage/storage_facade.h @@ -33,6 +33,14 @@ struct SharedMediaQuery; struct SharedMediaResult; struct SharedMediaSliceUpdate; +struct UserPhotosAddNew; +struct UserPhotosAddSlice; +struct UserPhotosRemoveOne; +struct UserPhotosRemoveAfter; +struct UserPhotosQuery; +struct UserPhotosResult; +struct UserPhotosSliceUpdate; + class Facade { public: Facade(); @@ -50,6 +58,16 @@ public: base::Observable &sharedMediaOneRemoved(); base::Observable &sharedMediaAllRemoved(); + void add(UserPhotosAddNew &&query); + void add(UserPhotosAddSlice &&query); + void remove(UserPhotosRemoveOne &&query); + void remove(UserPhotosRemoveAfter &&query); + void query( + UserPhotosQuery &&query, + base::lambda_once &&callback); + + base::Observable &userPhotosSliceUpdated(); + ~Facade(); private: diff --git a/Telegram/SourceFiles/storage/storage_shared_media.cpp b/Telegram/SourceFiles/storage/storage_shared_media.cpp index da68ecdc91..eca1aee204 100644 --- a/Telegram/SourceFiles/storage/storage_shared_media.cpp +++ b/Telegram/SourceFiles/storage/storage_shared_media.cpp @@ -73,11 +73,10 @@ int SharedMedia::List::uniteAndAdd( } template -int SharedMedia::List::addRangeItemsAndCount( +int SharedMedia::List::addRangeItemsAndCountNew( SliceUpdate &update, const Range &messages, - MsgRange noSkipRange, - base::optional count) { + MsgRange noSkipRange) { Expects((noSkipRange.from < noSkipRange.till) || (noSkipRange.from == noSkipRange.till && messages.begin() == messages.end())); if (noSkipRange.from == noSkipRange.till) { @@ -117,7 +116,7 @@ void SharedMedia::List::addRange( auto wasCount = _count; auto update = SliceUpdate(); - auto result = addRangeItemsAndCount(update, messages, noSkipRange, count); + auto result = addRangeItemsAndCountNew(update, messages, noSkipRange); if (count) { _count = count; } else if (incrementCount && _count && result > 0) { @@ -205,14 +204,7 @@ SharedMediaResult SharedMedia::List::queryFromSlice( auto haveEqualOrAfter = int(slice.messages.end() - position); auto before = qMin(haveBefore, query.limitBefore); auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1); - auto ids = std::vector(); - ids.reserve(before + equalOrAfter); - for ( - auto from = position - before, till = position + equalOrAfter; - from != till; - ++from) { - ids.push_back(*from); - } + auto ids = std::vector(position - before, position + equalOrAfter); result.messageIds.merge(ids.begin(), ids.end()); if (slice.range.from == 0) { result.skippedBefore = haveBefore - before; diff --git a/Telegram/SourceFiles/storage/storage_shared_media.h b/Telegram/SourceFiles/storage/storage_shared_media.h index 22b1b07078..cf86bbb1b1 100644 --- a/Telegram/SourceFiles/storage/storage_shared_media.h +++ b/Telegram/SourceFiles/storage/storage_shared_media.h @@ -254,11 +254,10 @@ private: const Range &messages, MsgRange noSkipRange); template - int addRangeItemsAndCount( + int addRangeItemsAndCountNew( SliceUpdate &update, const Range &messages, - MsgRange noSkipRange, - base::optional count); + MsgRange noSkipRange); template void addRange( const Range &messages, @@ -277,7 +276,7 @@ private: using SliceUpdate = List::SliceUpdate; using Lists = std::array; - std::map::iterator enforceLists(PeerId peerId); + std::map::iterator enforceLists(PeerId peer); std::map _lists; diff --git a/Telegram/SourceFiles/storage/storage_user_photos.cpp b/Telegram/SourceFiles/storage/storage_user_photos.cpp new file mode 100644 index 0000000000..dbf3a1007b --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_user_photos.cpp @@ -0,0 +1,176 @@ +/* +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 "storage/storage_user_photos.h" + +#include "base/task_queue.h" + +namespace Storage { + +void UserPhotos::List::addNew(PhotoId photoId) { + if (!base::contains(_photoIds, photoId)) { + _photoIds.push_back(photoId); + if (_count) { + ++*_count; + } + sendUpdate(); + } +} + +void UserPhotos::List::addSlice( + std::vector &&photoIds, + int count) { + for (auto photoId : photoIds) { + if (!base::contains(_photoIds, photoId)) { + _photoIds.push_front(photoId); + } + } + + _count = count; + if ((_count && *_count < _photoIds.size()) || photoIds.empty()) { + _count = _photoIds.size(); + } + sendUpdate(); +} + +void UserPhotos::List::removeOne(PhotoId photoId) { + auto position = base::find(_photoIds, photoId); + if (position == _photoIds.end()) { + _count = base::none; + } else { + if (_count) { + --*_count; + } + _photoIds.erase(position); + } + sendUpdate(); +} + +void UserPhotos::List::removeAfter(PhotoId photoId) { + auto position = base::find(_photoIds, photoId); + if (position == _photoIds.end()) { + _count = base::none; + _photoIds.clear(); + } else { + if (_count) { + *_count -= (_photoIds.end() - position); + } + _photoIds.erase(position, _photoIds.end()); + } + sendUpdate(); +} + +void UserPhotos::List::sendUpdate() { + auto update = SliceUpdate(); + update.photoIds = &_photoIds; + update.count = _count; + sliceUpdated.notify(update, true); +} + +void UserPhotos::List::query( + const UserPhotosQuery &query, + base::lambda_once &&callback) { + auto result = UserPhotosResult {}; + result.count = _count; + + auto position = base::find(_photoIds, query.key.photoId); + if (position != _photoIds.end()) { + auto haveBefore = int(position - _photoIds.begin()); + auto haveEqualOrAfter = int(_photoIds.end() - position); + auto before = qMin(haveBefore, query.limitBefore); + auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1); + result.photoIds = std::deque( + position - before, + position + equalOrAfter); + + auto skippedInIds = (haveBefore - before); + result.skippedBefore = _count + | func::add(-int(_photoIds.size()) + skippedInIds); + result.skippedBefore = haveBefore - before; + result.skippedAfter = (haveEqualOrAfter - equalOrAfter); + } + base::TaskQueue::Main().Put( + [ + callback = std::move(callback), + result = std::move(result) + ]() mutable { + callback(std::move(result)); + }); +} + +std::map::iterator +UserPhotos::enforceLists(UserId user) { + auto result = _lists.find(user); + if (result != _lists.end()) { + return result; + } + result = _lists.emplace(user, List {}).first; + subscribe(result->second.sliceUpdated, [this, user](const SliceUpdate &update) { + sliceUpdated.notify(UserPhotosSliceUpdate( + user, + update.photoIds, + update.count), true); + }); + return result; +} + +void UserPhotos::add(UserPhotosAddNew &&query) { + auto userIt = enforceLists(query.userId); + userIt->second.addNew(query.photoId); +} + +void UserPhotos::add(UserPhotosAddSlice &&query) { + auto userIt = enforceLists(query.userId); + userIt->second.addSlice( + std::move(query.photoIds), + query.count); +} + +void UserPhotos::remove(UserPhotosRemoveOne &&query) { + auto userIt = _lists.find(query.userId); + if (userIt != _lists.end()) { + userIt->second.removeOne(query.photoId); + } +} + +void UserPhotos::remove(UserPhotosRemoveAfter &&query) { + auto userIt = _lists.find(query.userId); + if (userIt != _lists.end()) { + userIt->second.removeAfter(query.photoId); + } +} + +void UserPhotos::query( + const UserPhotosQuery &query, + base::lambda_once &&callback) { + auto userIt = _lists.find(query.key.userId); + if (userIt != _lists.end()) { + userIt->second.query(query, std::move(callback)); + } else { + base::TaskQueue::Main().Put( + [ + callback = std::move(callback) + ]() mutable { + callback(UserPhotosResult()); + }); + } +} + +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_user_photos.h b/Telegram/SourceFiles/storage/storage_user_photos.h new file mode 100644 index 0000000000..746bade557 --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_user_photos.h @@ -0,0 +1,184 @@ +/* +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_facade.h" + +namespace Storage { + +struct UserPhotosAddNew { + UserPhotosAddNew(UserId userId, PhotoId photoId) + : userId(userId), photoId(photoId) { + } + + UserId userId = 0; + PhotoId photoId = 0; + +}; + +struct UserPhotosAddSlice { + UserPhotosAddSlice( + UserId userId, + std::vector &&photoIds, + int count) + : userId(userId) + , photoIds(std::move(photoIds)) + , count(count) { + } + + UserId userId = 0; + std::vector photoIds; + int count = 0; + +}; + +struct UserPhotosRemoveOne { + UserPhotosRemoveOne( + UserId userId, + PhotoId photoId) + : userId(userId) + , photoId(photoId) { + } + + UserId userId = 0; + PhotoId photoId = 0; + +}; + +struct UserPhotosRemoveAfter { + UserPhotosRemoveAfter( + UserId userId, + PhotoId photoId) + : userId(userId) + , photoId(photoId) { + } + + UserId userId = 0; + PhotoId photoId = 0; + +}; + +struct UserPhotosKey { + UserPhotosKey( + UserId userId, + PhotoId photoId) + : userId(userId) + , photoId(photoId) { + } + + bool operator==(const UserPhotosKey &other) const { + return (userId == other.userId) + && (photoId == other.photoId); + } + bool operator!=(const UserPhotosKey &other) const { + return !(*this == other); + } + + PeerId userId = 0; + PhotoId photoId = 0; + +}; + +struct UserPhotosQuery { + UserPhotosQuery( + UserPhotosKey key, + int limitBefore, + int limitAfter) + : key(key) + , limitBefore(limitBefore) + , limitAfter(limitAfter) { + } + + UserPhotosKey key; + int limitBefore = 0; + int limitAfter = 0; + +}; + +struct UserPhotosResult { + base::optional count; + base::optional skippedBefore; + int skippedAfter = 0; + std::deque photoIds; +}; + +struct UserPhotosSliceUpdate { + UserPhotosSliceUpdate( + UserId userId, + const std::deque *photoIds, + base::optional count) + : userId(userId) + , photoIds(photoIds) + , count(count) { + } + + UserId userId = 0; + const std::deque *photoIds = nullptr; + base::optional count; +}; + +class UserPhotos : private base::Subscriber { +public: + void add(UserPhotosAddNew &&query); + void add(UserPhotosAddSlice &&query); + void remove(UserPhotosRemoveOne &&query); + void remove(UserPhotosRemoveAfter &&query); + void query( + const UserPhotosQuery &query, + base::lambda_once &&callback); + + base::Observable sliceUpdated; + +private: + class List { + public: + void addNew(PhotoId photoId); + void addSlice( + std::vector &&photoIds, + int count); + void removeOne(PhotoId photoId); + void removeAfter(PhotoId photoId); + void query( + const UserPhotosQuery &query, + base::lambda_once &&callback); + + struct SliceUpdate { + const std::deque *photoIds = nullptr; + base::optional count; + }; + base::Observable sliceUpdated; + + private: + void sendUpdate(); + + base::optional _count; + std::deque _photoIds; + + }; + using SliceUpdate = List::SliceUpdate; + + std::map::iterator enforceLists(UserId user); + + std::map _lists; + +}; + +} // namespace Storage diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index dd1708aef3..9f2a0b32ce 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -568,10 +568,6 @@ public: bool hasCalls() const; void setCallsStatus(CallsStatus callsStatus); - typedef QList Photos; - Photos photos; - int photosCount = -1; // -1 not loaded, 0 all loaded - bool setAbout(const QString &newAbout); const QString &about() const { return _about; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 724847c8f0..73ba528a8d 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -189,6 +189,8 @@ <(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_user_photos.cpp +<(src_loc)/history/history_user_photos.h <(src_loc)/history/history_widget.cpp <(src_loc)/history/history_widget.h <(src_loc)/inline_bots/inline_bot_layout_internal.cpp @@ -449,6 +451,8 @@ <(src_loc)/storage/storage_facade.h <(src_loc)/storage/storage_shared_media.cpp <(src_loc)/storage/storage_shared_media.h +<(src_loc)/storage/storage_user_photos.cpp +<(src_loc)/storage/storage_user_photos.h <(src_loc)/ui/effects/cross_animation.cpp <(src_loc)/ui/effects/cross_animation.h <(src_loc)/ui/effects/panel_animation.cpp