diff --git a/Telegram/SourceFiles/auth_session.cpp b/Telegram/SourceFiles/auth_session.cpp index 08db86bfd..aa93cc44a 100644 --- a/Telegram/SourceFiles/auth_session.cpp +++ b/Telegram/SourceFiles/auth_session.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "storage/file_download.h" #include "storage/file_upload.h" #include "storage/localstorage.h" +#include "storage/storage_facade.h" #include "storage/serialize_common.h" #include "window/notifications_manager.h" #include "platform/platform_specific.h" @@ -174,6 +175,7 @@ AuthSession::AuthSession(UserId userId) , _calls(std::make_unique()) , _downloader(std::make_unique()) , _uploader(std::make_unique()) +, _storage(std::make_unique()) , _notifications(std::make_unique(this)) { Expects(_userId != 0); _saveDataTimer.setCallback([this] { diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h index 3124a26b3..9430b67c1 100644 --- a/Telegram/SourceFiles/auth_session.h +++ b/Telegram/SourceFiles/auth_session.h @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Storage { class Downloader; class Uploader; +class Facade; } // namespace Storage namespace Window { @@ -199,6 +200,9 @@ public: Storage::Uploader &uploader() { return *_uploader; } + Storage::Facade &storage() { + return *_storage; + } base::Observable &downloaderTaskFinished(); @@ -239,6 +243,7 @@ private: const std::unique_ptr _calls; const std::unique_ptr _downloader; const std::unique_ptr _uploader; + const std::unique_ptr _storage; const std::unique_ptr _notifications; }; diff --git a/Telegram/SourceFiles/base/algorithm.h b/Telegram/SourceFiles/base/algorithm.h index 2231f3896..518bc53c3 100644 --- a/Telegram/SourceFiles/base/algorithm.h +++ b/Telegram/SourceFiles/base/algorithm.h @@ -63,4 +63,78 @@ decltype(auto) find_if(Range &&range, Predicate &&predicate) { std::forward(predicate)); } +template +decltype(auto) lower_bound(Range &&range, Type &&value) { + return std::lower_bound( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(value)); +} + +template +decltype(auto) lower_bound(Range &&range, Type &&value, Predicate &&predicate) { + return std::lower_bound( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(value), + std::forward(predicate)); +} + +template +decltype(auto) upper_bound(Range &&range, Type &&value) { + return std::upper_bound( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(value)); +} + +template +decltype(auto) upper_bound(Range &&range, Type &&value, Predicate &&predicate) { + return std::upper_bound( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(value), + std::forward(predicate)); +} + +template +decltype(auto) equal_range(Range &&range, Type &&value) { + return std::equal_range( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(value)); +} + +template +decltype(auto) equal_range(Range &&range, Type &&value, Predicate &&predicate) { + return std::equal_range( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(value), + std::forward(predicate)); +} + +template +decltype(auto) sort(Range &&range) { + return std::sort( + std::begin(std::forward(range)), + std::end(std::forward(range))); +} + +template +decltype(auto) sort(Range &&range, Predicate &&predicate) { + return std::sort( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(predicate)); +} + +template +decltype(auto) stable_partition(Range &&range, Predicate &&predicate) { + return std::stable_partition( + std::begin(std::forward(range)), + std::end(std::forward(range)), + std::forward(predicate)); +} + } // namespace base diff --git a/Telegram/SourceFiles/base/enum_mask.h b/Telegram/SourceFiles/base/enum_mask.h new file mode 100644 index 000000000..489f3fb1c --- /dev/null +++ b/Telegram/SourceFiles/base/enum_mask.h @@ -0,0 +1,60 @@ +/* +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 + +namespace base { + +template +class enum_mask { + using Type = std::uint32_t; + +public: + static_assert(static_cast(Enum::kCount) <= 32, "We have only 32 bit."); + + enum_mask() = default; + enum_mask(Enum value) : _value(ToBit(value)) { + } + + enum_mask added(enum_mask other) const { + auto result = *this; + result.set(other); + return result; + } + void set(enum_mask other) { + _value |= other._value; + } + bool test(Enum value) const { + return _value & ToBit(value); + } + + explicit operator bool() const { + return _value != 0; + } + +private: + inline static Type ToBit(Enum value) { + return 1 << static_cast(value); + } + Type _value = 0; + +}; + +} // namespace base diff --git a/Telegram/SourceFiles/base/flat_map.h b/Telegram/SourceFiles/base/flat_map.h index d1dc28fd3..86553d61a 100644 --- a/Telegram/SourceFiles/base/flat_map.h +++ b/Telegram/SourceFiles/base/flat_map.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include "base/optional.h" +#include "base/algorithm.h" namespace base { @@ -31,10 +32,20 @@ class flat_map; template class flat_multi_map; -template +template < + typename Key, + typename Type, + typename iterator_impl, + typename pointer_impl, + typename reference_impl> class flat_multi_map_iterator_base_impl; -template +template < + typename Key, + typename Type, + typename iterator_impl, + typename pointer_impl, + typename reference_impl> class flat_multi_map_iterator_base_impl { public: using iterator_category = typename iterator_impl::iterator_category; @@ -46,7 +57,8 @@ public: using reference = reference_impl; using const_reference = typename flat_multi_map::const_reference; - flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl()) : _impl(impl) { + flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl()) + : _impl(impl) { } reference operator*() { @@ -134,7 +146,9 @@ class flat_multi_map { friend inline bool operator<(const key_const_wrap &a, const Key &b) { return ((const Key&)a) < b; } - friend inline bool operator<(const key_const_wrap &a, const key_const_wrap &b) { + friend inline bool operator<( + const key_const_wrap &a, + const key_const_wrap &b) { return ((const Key&)a) < ((const Key&)b); } @@ -146,10 +160,30 @@ class flat_multi_map { using pair_type = std::pair; using impl = std::deque; - using iterator_base = flat_multi_map_iterator_base_impl; - using const_iterator_base = flat_multi_map_iterator_base_impl; - using reverse_iterator_base = flat_multi_map_iterator_base_impl; - using const_reverse_iterator_base = flat_multi_map_iterator_base_impl; + using iterator_base = flat_multi_map_iterator_base_impl< + Key, + Type, + typename impl::iterator, + pair_type*, + pair_type&>; + using const_iterator_base = flat_multi_map_iterator_base_impl< + Key, + Type, + typename impl::const_iterator, + const pair_type*, + const pair_type&>; + using reverse_iterator_base = flat_multi_map_iterator_base_impl< + Key, + Type, + typename impl::reverse_iterator, + pair_type*, + pair_type&>; + using const_reverse_iterator_base = flat_multi_map_iterator_base_impl< + Key, + Type, + typename impl::const_reverse_iterator, + const pair_type*, + const pair_type&>; public: using value_type = pair_type; @@ -354,36 +388,66 @@ private: } }; typename impl::iterator getLowerBound(const Key &key) { - return std::lower_bound(_impl.begin(), _impl.end(), key, Comparator()); + return base::lower_bound(_impl, key, Comparator()); } typename impl::const_iterator getLowerBound(const Key &key) const { - return std::lower_bound(_impl.begin(), _impl.end(), key, Comparator()); + return base::lower_bound(_impl, key, Comparator()); } typename impl::iterator getUpperBound(const Key &key) { - return std::upper_bound(_impl.begin(), _impl.end(), key, Comparator()); + return base::upper_bound(_impl, key, Comparator()); } typename impl::const_iterator getUpperBound(const Key &key) const { - return std::upper_bound(_impl.begin(), _impl.end(), key, Comparator()); + return base::upper_bound(_impl, key, Comparator()); } - std::pair getEqualRange(const Key &key) { - return std::equal_range(_impl.begin(), _impl.end(), key, Comparator()); + std::pair< + typename impl::iterator, + typename impl::iterator + > getEqualRange(const Key &key) { + return base::equal_range(_impl, key, Comparator()); } - std::pair getEqualRange(const Key &key) const { - return std::equal_range(_impl.begin(), _impl.end(), key, Comparator()); + std::pair< + typename impl::const_iterator, + typename impl::const_iterator + > getEqualRange(const Key &key) const { + return base::equal_range(_impl, key, Comparator()); } }; template -class flat_map : public flat_multi_map { +class flat_map : private flat_multi_map { using parent = flat_multi_map; using pair_type = typename parent::pair_type; public: - using parent::parent; + using value_type = typename parent::value_type; + using size_type = typename parent::size_type; + using difference_type = typename parent::difference_type; + using pointer = typename parent::pointer; + using const_pointer = typename parent::const_pointer; + using reference = typename parent::reference; + using const_reference = typename parent::const_reference; using iterator = typename parent::iterator; using const_iterator = typename parent::const_iterator; - using value_type = typename parent::value_type; + using reverse_iterator = typename parent::reverse_iterator; + using const_reverse_iterator = typename parent::const_reverse_iterator; + + using parent::parent; + using parent::size; + using parent::empty; + using parent::clear; + using parent::begin; + using parent::end; + using parent::cbegin; + using parent::cend; + using parent::rbegin; + using parent::rend; + using parent::crbegin; + using parent::crend; + using parent::front; + using parent::back; + using parent::erase; + using parent::contains; iterator insert(const value_type &value) { if (this->empty() || (value.first < this->front().first)) { diff --git a/Telegram/SourceFiles/base/flat_set.h b/Telegram/SourceFiles/base/flat_set.h index 7e578d334..8dd2b1012 100644 --- a/Telegram/SourceFiles/base/flat_set.h +++ b/Telegram/SourceFiles/base/flat_set.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include +#include "base/algorithm.h" namespace base { @@ -43,7 +44,8 @@ public: using pointer = typename flat_multi_set::pointer; using reference = typename flat_multi_set::reference; - flat_multi_set_iterator_base_impl(iterator_impl impl = iterator_impl()) : _impl(impl) { + flat_multi_set_iterator_base_impl(iterator_impl impl = iterator_impl()) + : _impl(impl) { } reference operator*() const { @@ -100,6 +102,11 @@ public: private: iterator_impl _impl; friend class flat_multi_set; + friend class flat_set; + + Type &wrapped() { + return _impl->wrapped(); + } }; @@ -115,6 +122,9 @@ class flat_multi_set { inline operator const Type&() const { return _value; } + Type &wrapped() { + return _value; + } friend inline bool operator<(const Type &a, const const_wrap &b) { return a < ((const Type&)b); @@ -133,10 +143,18 @@ class flat_multi_set { using impl = std::deque; - using iterator_base = flat_multi_set_iterator_base_impl; - using const_iterator_base = flat_multi_set_iterator_base_impl; - using reverse_iterator_base = flat_multi_set_iterator_base_impl; - using const_reverse_iterator_base = flat_multi_set_iterator_base_impl; + using iterator_base = flat_multi_set_iterator_base_impl< + Type, + typename impl::iterator>; + using const_iterator_base = flat_multi_set_iterator_base_impl< + Type, + typename impl::const_iterator>; + using reverse_iterator_base = flat_multi_set_iterator_base_impl< + Type, + typename impl::reverse_iterator>; + using const_reverse_iterator_base = flat_multi_set_iterator_base_impl< + Type, + typename impl::const_reverse_iterator>; public: using value_type = Type; @@ -167,7 +185,8 @@ public: class reverse_iterator : public reverse_iterator_base { public: using reverse_iterator_base::reverse_iterator_base; - reverse_iterator(reverse_iterator_base other) : reverse_iterator_base(other) { + reverse_iterator(reverse_iterator_base other) + : reverse_iterator_base(other) { } friend class const_reverse_iterator; @@ -175,18 +194,26 @@ public: class const_reverse_iterator : public const_reverse_iterator_base { public: using const_reverse_iterator_base::const_reverse_iterator_base; - const_reverse_iterator(const_reverse_iterator_base other) : const_reverse_iterator_base(other) { + const_reverse_iterator(const_reverse_iterator_base other) + : const_reverse_iterator_base(other) { } - const_reverse_iterator(const reverse_iterator &other) : const_reverse_iterator_base(other._impl) { + const_reverse_iterator(const reverse_iterator &other) + : const_reverse_iterator_base(other._impl) { } }; flat_multi_set() = default; - template ::iterator_category> + template < + typename Iterator, + typename = typename std::iterator_traits::iterator_category> flat_multi_set(Iterator first, Iterator last) : _impl(first, last) { - std::sort(_impl.begin(), _impl.end()); + base::sort(_impl); + } + + flat_multi_set(std::initializer_list iter) + : flat_multi_set(iter.begin(), iter.end()) { } size_type size() const { @@ -327,49 +354,120 @@ public: return (range.second - range.first); } + template + auto modify(iterator which, Action action) { + auto result = action(which.wrapped()); + for (auto i = which + 1, e = end(); i != e; ++i) { + if (*i < *which) { + std::swap(i.wrapped(), which.wrapped()); + } else { + break; + } + } + for (auto i = which, b = begin(); i != b;) { + --i; + if (*which < *i) { + std::swap(i.wrapped(), which.wrapped()); + } else { + break; + } + } + return result; + } + + template < + typename Iterator, + typename = typename std::iterator_traits::iterator_category> + void merge(Iterator first, Iterator last) { + _impl.insert(_impl.end(), first, last); + base::sort(_impl); + } + + void merge(const flat_multi_set &other) { + merge(other.begin(), other.end()); + } + + void merge(std::initializer_list list) { + merge(list.begin(), list.end()); + } + private: impl _impl; friend class flat_set; typename impl::iterator getLowerBound(const Type &value) { - return std::lower_bound(_impl.begin(), _impl.end(), value); + return base::lower_bound(_impl, value); } typename impl::const_iterator getLowerBound(const Type &value) const { - return std::lower_bound(_impl.begin(), _impl.end(), value); + return base::lower_bound(_impl, value); } typename impl::iterator getUpperBound(const Type &value) { - return std::upper_bound(_impl.begin(), _impl.end(), value); + return base::upper_bound(_impl, value); } typename impl::const_iterator getUpperBound(const Type &value) const { - return std::upper_bound(_impl.begin(), _impl.end(), value); + return base::upper_bound(_impl, value); } - std::pair getEqualRange(const Type &value) { - return std::equal_range(_impl.begin(), _impl.end(), value); + std::pair< + typename impl::iterator, + typename impl::iterator + > getEqualRange(const Type &value) { + return base::equal_range(_impl, value); } - std::pair getEqualRange(const Type &value) const { - return std::equal_range(_impl.begin(), _impl.end(), value); + std::pair< + typename impl::const_iterator, + typename impl::const_iterator + > getEqualRange(const Type &value) const { + return base::equal_range(_impl, value); } }; template -class flat_set : public flat_multi_set { +class flat_set : private flat_multi_set { using parent = flat_multi_set; public: - using parent::parent; using iterator = typename parent::iterator; using const_iterator = typename parent::const_iterator; + using reverse_iterator = typename parent::reverse_iterator; + using const_reverse_iterator = typename parent::const_reverse_iterator; + using value_type = typename parent::value_type; + using size_type = typename parent::size_type; + using difference_type = typename parent::difference_type; + using pointer = typename parent::pointer; + using reference = typename parent::reference; flat_set() = default; - template ::iterator_category> + template < + typename Iterator, + typename = typename std::iterator_traits::iterator_category + > flat_set(Iterator first, Iterator last) : parent(first, last) { - this->_impl.erase(std::unique(this->_impl.begin(), this->_impl.end(), [](auto &&a, auto &&b) { - return !(a < b); - }), this->_impl.end()); + finalize(); } + flat_set(std::initializer_list iter) : parent(iter.begin(), iter.end()) { + finalize(); + } + + using parent::parent; + using parent::size; + using parent::empty; + using parent::clear; + using parent::begin; + using parent::end; + using parent::cbegin; + using parent::cend; + using parent::rbegin; + using parent::rend; + using parent::crbegin; + using parent::crend; + using parent::front; + using parent::back; + using parent::contains; + using parent::erase; + iterator insert(const Type &value) { if (this->empty() || (value < this->front())) { this->_impl.push_front(value); @@ -414,6 +512,58 @@ public: return this->findFirst(value); } + template + void modify(iterator which, Action action) { + action(which.wrapped()); + for (auto i = iterator(which + 1), e = end(); i != e; ++i) { + if (*i < *which) { + std::swap(i.wrapped(), which.wrapped()); + } else if (!(*which < *i)) { + erase(which); + return; + } else{ + break; + } + } + for (auto i = which, b = begin(); i != b;) { + --i; + if (*which < *i) { + std::swap(i.wrapped(), which.wrapped()); + } else if (!(*i < *which)) { + erase(which); + return; + } else { + break; + } + } + } + + template < + typename Iterator, + typename = typename std::iterator_traits::iterator_category> + void merge(Iterator first, Iterator last) { + parent::merge(first, last); + finalize(); + } + + void merge(const flat_multi_set &other) { + merge(other.begin(), other.end()); + } + + void merge(std::initializer_list list) { + merge(list.begin(), list.end()); + } + +private: + void finalize() { + this->_impl.erase( + std::unique( + this->_impl.begin(), + this->_impl.end(), + [](auto &&a, auto &&b) { return !(a < b); }), + this->_impl.end()); + } + }; } // namespace base diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 49c11281f..f3cf017e6 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -636,8 +636,8 @@ void EditChatAdminsBoxController::rebuildRows() { auto sortByName = [](auto a, auto b) { return (a->name.compare(b->name, Qt::CaseInsensitive) < 0); }; - std::sort(admins.begin(), admins.end(), sortByName); - std::sort(others.begin(), others.end(), sortByName); + base::sort(admins, sortByName); + base::sort(others, sortByName); auto addOne = [this](not_null user) { if (auto row = createRow(user)) { diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 380e03ef4..a1fc87aa4 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -52,7 +52,7 @@ public: void addItem(HistoryItem *item) { Expects(canAddItem(item)); _items.push_back(item); - std::sort(_items.begin(), _items.end(), [](HistoryItem *a, HistoryItem *b) { + base::sort(_items, [](HistoryItem *a, HistoryItem *b) { return (a->id > b->id); }); refreshStatus(); diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index e6cdbfa48..a6b142a99 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -27,8 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "base/build_config.h" -template -using not_null = gsl::not_null; +using gsl::not_null; // Custom libc++ build used for old OS X versions already has this. #ifndef OS_MAC_OLD diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 0867d6221..2bfc72eac 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -36,6 +36,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "window/notifications_manager.h" #include "calls/calls_instance.h" +#include "storage/storage_facade.h" +#include "storage/storage_shared_media.h" namespace { @@ -64,6 +66,14 @@ HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, QString(), text); } +Storage::SharedMediaType ConvertSharedMediaType(MediaOverviewType type) { + return static_cast(type); +} + +MediaOverviewType ConvertSharedMediaType(Storage::SharedMediaType type) { + return static_cast(type); +} + } // namespace void HistoryInit() { @@ -436,7 +446,7 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { return _joinedMessage; } - UserData *inviter = (peer->asChannel()->inviter > 0) ? App::userLoaded(peer->asChannel()->inviter) : nullptr; + auto inviter = (peer->asChannel()->inviter > 0) ? App::userLoaded(peer->asChannel()->inviter) : nullptr; if (!inviter) return nullptr; MTPDmessage::Flags flags = 0; @@ -446,7 +456,7 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { // flags |= MTPDmessage::Flag::f_unread; } - QDateTime inviteDate = peer->asChannel()->inviteDate; + auto inviteDate = peer->asChannel()->inviteDate; if (unread) _maxReadMessageDate = inviteDate; if (isEmpty()) { _joinedMessage = HistoryJoined::create(this, inviteDate, inviter, flags); @@ -1255,6 +1265,24 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { } adding->addToOverview(AddToOverviewNew); + if (IsServerMsgId(adding->id)) { + if (auto sharedMediaTypes = adding->sharedMediaTypes()) { + if (newMsg) { + Auth().storage().add(Storage::SharedMediaAddNew( + peer->id, + sharedMediaTypes, + adding->id)); + } else { + auto from = loadedAtTop() ? 0 : minMsgId(); + auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId(); + Auth().storage().add(Storage::SharedMediaAddExisting( + peer->id, + sharedMediaTypes, + adding->id, + { from, till })); + } + } + } if (adding->from()->id) { if (auto user = adding->from()->asUser()) { auto getLastAuthors = [this]() -> QList>* { @@ -1417,6 +1445,22 @@ void History::addItemToBlock(HistoryItem *item) { } } +template +void History::addToSharedMedia(std::vector (&medias)[kSharedMediaTypeCount], bool force) { + auto from = loadedAtTop() ? 0 : minMsgId(); + auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId(); + for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) { + if (force || !medias[i].empty()) { + auto type = static_cast(i); + Auth().storage().add(Storage::SharedMediaAddSlice( + peer->id, + type, + std::move(medias[i]), + { from, till })); + } + } +} + void History::addOlderSlice(const QVector &slice) { if (slice.isEmpty()) { oldLoaded = true; @@ -1525,6 +1569,7 @@ void History::addOlderSlice(const QVector &slice) { Notify::peerUpdatedDelayed(update); } } + addBlockToSharedMedia(block); if (isChannel()) { asChannelHistory()->checkJoinedMessage(); @@ -1545,7 +1590,8 @@ void History::addNewerSlice(const QVector &slice) { Assert(!isBuildingFrontBlock()); if (!slice.isEmpty()) { - bool atLeastOneAdded = false; + std::vector medias[Storage::kSharedMediaTypeCount]; + auto atLeastOneAdded = false; for (auto i = slice.cend(), e = slice.cbegin(); i != e;) { --i; auto adding = createItem(*i, false, true); @@ -1553,12 +1599,24 @@ void History::addNewerSlice(const QVector &slice) { addItemToBlock(adding); atLeastOneAdded = true; + if (auto types = adding->sharedMediaTypes()) { + for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) { + auto type = static_cast(i); + if (types.test(type)) { + if (medias[i].empty()) { + medias[i].reserve(slice.size()); + } + medias[i].push_back(adding->id); + } + } + } } if (!atLeastOneAdded) { newLoaded = true; setLastMessage(lastAvailableMessage()); } + addToSharedMedia(medias, wasLoadedAtBottom != loadedAtBottom()); } if (!wasLoadedAtBottom) { @@ -1599,6 +1657,26 @@ void History::checkAddAllToOverview() { } } +void History::addBlockToSharedMedia(HistoryBlock *block) { + std::vector medias[Storage::kSharedMediaTypeCount]; + if (block) { + for (auto item : block->items) { + if (auto types = item->sharedMediaTypes()) { + for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) { + auto type = static_cast(i); + if (types.test(type)) { + if (medias[i].empty()) { + medias[i].reserve(block->items.size()); + } + medias[i].push_back(item->id); + } + } + } + } + } + addToSharedMedia(medias, !block); +} + int History::countUnread(MsgId upTo) { int result = 0; for (auto i = blocks.cend(), e = blocks.cbegin(); i != e;) { @@ -2044,9 +2122,9 @@ void History::fixLastMessage(bool wasAtBottom) { } MsgId History::minMsgId() const { - for_const (const HistoryBlock *block, blocks) { - for_const (const HistoryItem *item, block->items) { - if (item->id > 0) { + for (auto block : std::as_const(blocks)) { + for (auto item : std::as_const(block->items)) { + if (IsServerMsgId(item->id)) { return item->id; } } @@ -2055,12 +2133,10 @@ MsgId History::minMsgId() const { } MsgId History::maxMsgId() const { - for (auto i = blocks.cend(), e = blocks.cbegin(); i != e;) { - --i; - for (auto j = (*i)->items.cend(), en = (*i)->items.cbegin(); j != en;) { - --j; - if ((*j)->id > 0) { - return (*j)->id; + for (auto block : base::reversed(std::as_const(blocks))) { + for (auto item : base::reversed(std::as_const(block->items))) { + if (IsServerMsgId(item->id)) { + return item->id; } } } @@ -2142,8 +2218,6 @@ void History::clear(bool leaveItems) { ++i; } } - } - if (!leaveItems) { for (auto i = 0; i != OverviewCount; ++i) { if (!_overview[i].isEmpty()) { _overviewCountData[i] = -1; // not loaded yet @@ -2153,6 +2227,7 @@ void History::clear(bool leaveItems) { } } } + Auth().storage().remove(Storage::SharedMediaRemoveAll(peer->id)); } clearBlocks(leaveItems); if (leaveItems) { @@ -2277,27 +2352,33 @@ void History::setPinnedIndex(int pinnedIndex) { } } -void History::overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages &result, bool onlyCounts) { +void History::overviewSliceDone( + int32 overviewIndex, + MsgId startMessageId, + const MTPmessages_Messages &result, + bool onlyCounts) { + auto fullCount = 0; const QVector *v = 0; switch (result.type()) { case mtpc_messages_messages: { - auto &d(result.c_messages_messages()); + auto &d = result.c_messages_messages(); App::feedUsers(d.vusers); App::feedChats(d.vchats); v = &d.vmessages.v; + fullCount = v->size(); _overviewCountData[overviewIndex] = 0; } break; case mtpc_messages_messagesSlice: { - auto &d(result.c_messages_messagesSlice()); + auto &d = result.c_messages_messagesSlice(); App::feedUsers(d.vusers); App::feedChats(d.vchats); - _overviewCountData[overviewIndex] = d.vcount.v; + fullCount = _overviewCountData[overviewIndex] = d.vcount.v; v = &d.vmessages.v; } break; case mtpc_messages_channelMessages: { - auto &d(result.c_messages_channelMessages()); + auto &d = result.c_messages_channelMessages(); if (peer->isChannel()) { peer->asChannel()->ptsReceived(d.vpts.v); } else { @@ -2305,7 +2386,7 @@ void History::overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages } App::feedUsers(d.vusers); App::feedChats(d.vchats); - _overviewCountData[overviewIndex] = d.vcount.v; + fullCount = _overviewCountData[overviewIndex] = d.vcount.v; v = &d.vmessages.v; } break; @@ -2316,11 +2397,29 @@ void History::overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages _overviewCountData[overviewIndex] = 0; } + auto noSkipRange = MsgRange { startMessageId, startMessageId }; + auto sharedMediaType = ConvertSharedMediaType( + static_cast(overviewIndex)); + auto slice = std::vector(); + slice.reserve(v->size()); for (auto i = v->cbegin(), e = v->cend(); i != e; ++i) { if (auto item = App::histories().addNewMessage(*i, NewMessageExisting)) { - _overview[overviewIndex].insert(item->id); + auto itemId = item->id; + _overview[overviewIndex].insert(itemId); + if (item->sharedMediaTypes().test(sharedMediaType)) { + slice.push_back(itemId); + accumulate_min(noSkipRange.from, itemId); + accumulate_max(noSkipRange.till, itemId); + } } } + Auth().storage().add(Storage::SharedMediaAddSlice( + peer->id, + sharedMediaType, + std::move(slice), + noSkipRange, + fullCount + )); } void History::changeMsgId(MsgId oldId, MsgId newId) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 24db52f0d..cc07761cd 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -491,7 +491,11 @@ public: MsgId overviewMinId(int32 overviewIndex) const { return _overview[overviewIndex].empty() ? 0 : *_overview[overviewIndex].begin(); } - void overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages &result, bool onlyCounts = false); + void overviewSliceDone( + int32 overviewIndex, + MsgId startMessageId, + const MTPmessages_Messages &result, + bool onlyCounts = false); bool overviewHasMsgId(int32 overviewIndex, MsgId msgId) const { return _overview[overviewIndex].contains(msgId); } @@ -545,6 +549,10 @@ private: // Add all items to the media overview if we were not loaded at bottom and now are. void checkAddAllToOverview(); + template + void addToSharedMedia(std::vector (&medias)[kSharedMediaTypeCount], bool force); + void addBlockToSharedMedia(HistoryBlock *block); + enum class Flag { f_has_pending_resized_items = (1 << 0), f_pending_resize = (1 << 1), diff --git a/Telegram/SourceFiles/history/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/history_admin_log_inner.cpp index 85bd2b2be..f338d8335 100644 --- a/Telegram/SourceFiles/history/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_inner.cpp @@ -1250,9 +1250,11 @@ void InnerWidget::updateSelected() { auto itemPoint = QPoint(); auto begin = std::rbegin(_items), end = std::rend(_items); - auto from = (point.y() >= _itemsTop && point.y() < _itemsTop + _itemsHeight) ? std::lower_bound(begin, end, point.y(), [this](auto &elem, int top) { - return this->itemTop(elem) + elem->height() <= top; - }) : end; + auto from = (point.y() >= _itemsTop && point.y() < _itemsTop + _itemsHeight) + ? std::lower_bound(begin, end, point.y(), [this](auto &elem, int top) { + return this->itemTop(elem) + elem->height() <= top; + }) + : end; auto item = (from != end) ? from->get() : nullptr; if (item) { App::mousedItem(item); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 98d3de397..e9d36ac2d 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -30,6 +30,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_history.h" #include "ui/effects/ripple_animation.h" #include "storage/file_upload.h" +#include "storage/storage_facade.h" +#include "storage/storage_shared_media.h" #include "auth_session.h" #include "media/media_audio.h" #include "messenger.h" @@ -708,6 +710,14 @@ void HistoryItem::destroy() { } else { // All this must be done for all items manually in History::clear(false)! eraseFromOverview(); + if (IsServerMsgId(id)) { + if (auto types = sharedMediaTypes()) { + Auth().storage().remove(Storage::SharedMediaRemoveOne( + history()->peer->id, + types, + id)); + } + } auto wasAtBottom = history()->loadedAtBottom(); _history->removeNotification(this); @@ -748,6 +758,10 @@ void HistoryItem::detachFast() { _indexInBlock = -1; } +Storage::SharedMediaTypesMask HistoryItem::sharedMediaTypes() const { + return {}; +} + void HistoryItem::previousItemChanged() { Expects(!isLogEntry()); recountDisplayDate(); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 5e3d6ef22..8f3b661e0 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -23,6 +23,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "base/runtime_composer.h" #include "base/flags.h" +namespace base { +template +class enum_mask; +} // namespace base + +namespace Storage { +enum class SharedMediaType : char; +using SharedMediaTypesMask = base::enum_mask; +} // namespace Storage + namespace Ui { class RippleAnimation; } // namespace Ui @@ -669,11 +679,14 @@ public: } virtual void updateReplyMarkup(const MTPReplyMarkup *markup) { } + virtual int32 addToOverview(AddToOverviewMethod method) { return 0; } virtual void eraseFromOverview() { } + virtual Storage::SharedMediaTypesMask sharedMediaTypes() const; + virtual bool hasBubble() const { return false; } diff --git a/Telegram/SourceFiles/history/history_media.cpp b/Telegram/SourceFiles/history/history_media.cpp new file mode 100644 index 000000000..292ace504 --- /dev/null +++ b/Telegram/SourceFiles/history/history_media.cpp @@ -0,0 +1,27 @@ +/* +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_media.h" + +#include "storage/storage_shared_media.h" + +Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const { + return {}; +} diff --git a/Telegram/SourceFiles/history/history_media.h b/Telegram/SourceFiles/history/history_media.h index c8580ebb3..b106c016a 100644 --- a/Telegram/SourceFiles/history/history_media.h +++ b/Telegram/SourceFiles/history/history_media.h @@ -20,6 +20,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +namespace base { +template +class enum_mask; +} // namespace base + +namespace Storage { +enum class SharedMediaType : char; +using SharedMediaTypesMask = base::enum_mask; +} // namespace Storage + enum class MediaInBubbleState { None, Top, @@ -79,6 +89,7 @@ public: } virtual void eraseFromOverview() { } + virtual Storage::SharedMediaTypesMask sharedMediaTypes() const; // if we are in selecting items mode perhaps we want to // toggle selection instead of activating the pressed link diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 0a350680e..ad69e985e 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "storage/localstorage.h" +#include "storage/storage_shared_media.h" #include "media/media_audio.h" #include "media/media_clip_reader.h" #include "media/player/media_player_instance.h" @@ -653,6 +654,13 @@ void HistoryPhoto::eraseFromOverview() { } } +Storage::SharedMediaTypesMask HistoryPhoto::sharedMediaTypes() const { + if (_parent->toHistoryMessage()) { + return Storage::SharedMediaType::Photo; + } + return Storage::SharedMediaType::ChatPhoto; +} + ImagePtr HistoryPhoto::replyPreview() { return _data->makeReplyPreview(); } @@ -959,6 +967,10 @@ void HistoryVideo::eraseFromOverview() { eraseFromOneOverview(OverviewVideos); } +Storage::SharedMediaTypesMask HistoryVideo::sharedMediaTypes() const { + return Storage::SharedMediaType::Video; +} + void HistoryVideo::updateStatusText() const { bool showPause = false; int32 statusSize = 0, realDuration = 0; @@ -1601,6 +1613,20 @@ void HistoryDocument::eraseFromOverview() { } } +Storage::SharedMediaTypesMask HistoryDocument::sharedMediaTypes() const { + using Type = Storage::SharedMediaType; + if (_data->voice()) { + using Mask = Storage::SharedMediaTypesMask; + return Mask {}.added(Type::VoiceFile).added(Type::RoundVoiceFile); + } else if (_data->song()) { + if (_data->isMusic()) { + return Type::MusicFile; + } + return {}; + } + return Type::File; +} + template void HistoryDocument::buildStringRepresentation(Callback callback) const { const Text emptyCaption; @@ -2427,6 +2453,16 @@ int32 HistoryGif::addToOverview(AddToOverviewMethod method) { return result; } +Storage::SharedMediaTypesMask HistoryGif::sharedMediaTypes() const { + using Type = Storage::SharedMediaType; + if (_data->isRoundVideo()) { + return Type::RoundVoiceFile; + } else if (_data->isGifv()) { + return Type::GIF; + } + return Type::File; +} + void HistoryGif::eraseFromOverview() { if (_data->isRoundVideo()) { eraseFromOneOverview(OverviewRoundVoiceFiles); diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 5a77b7f42..5866cfdca 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -156,6 +156,7 @@ public: int32 addToOverview(AddToOverviewMethod method) override; void eraseFromOverview() override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; PhotoData *photo() const { return _data; @@ -241,6 +242,7 @@ public: int32 addToOverview(AddToOverviewMethod method) override; void eraseFromOverview() override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; DocumentData *getDocument() override { return _data; @@ -398,6 +400,7 @@ public: int32 addToOverview(AddToOverviewMethod method) override; void eraseFromOverview() override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; bool uploading() const override { return _data->uploading(); @@ -504,6 +507,7 @@ public: int32 addToOverview(AddToOverviewMethod method) override; void eraseFromOverview() override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; bool uploading() const override { return _data->uploading(); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index ca4f9289c..d424acac9 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -38,6 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_history.h" #include "window/notifications_manager.h" #include "observer_peer.h" +#include "storage/storage_shared_media.h" namespace { @@ -1297,6 +1298,17 @@ void HistoryMessage::eraseFromOverview() { } } +Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const { + auto result = Storage::SharedMediaTypesMask {}; + if (auto media = getMedia()) { + result.set(media->sharedMediaTypes()); + } + if (hasTextLinks()) { + result.set(Storage::SharedMediaType::Link); + } + return result; +} + TextWithEntities HistoryMessage::selectedText(TextSelection selection) const { TextWithEntities logEntryOriginalResult; auto textResult = _text.originalTextWithEntities((selection == FullSelection) ? AllTextSelection : selection, ExpandLinksAll); diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 39fafd0bd..85dbb2503 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -100,8 +100,10 @@ public: void updateReplyMarkup(const MTPReplyMarkup *markup) override { setReplyMarkup(markup); } + int32 addToOverview(AddToOverviewMethod method) override; void eraseFromOverview() override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; TextWithEntities selectedText(TextSelection selection) const override; void setText(const TextWithEntities &textWithEntities) override; diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 4d9e1c9e5..7f81739aa 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "history/history_message.h" #include "auth_session.h" #include "window/notifications_manager.h" +#include "storage/storage_shared_media.h" namespace { @@ -731,6 +732,13 @@ int32 HistoryService::addToOverview(AddToOverviewMethod method) { return result; } +Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const { + if (auto media = getMedia()) { + return media->sharedMediaTypes(); + } + return {}; +} + void HistoryService::eraseFromOverview() { if (auto media = getMedia()) { media->eraseFromOverview(); diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index 109a2b2d8..a1a6bbb80 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -100,6 +100,7 @@ public: int32 addToOverview(AddToOverviewMethod method) override; void eraseFromOverview() override; + Storage::SharedMediaTypesMask sharedMediaTypes() const override; bool needCheck() const override { return false; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index bcb621c6d..df9699de9 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -74,6 +74,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/window_controller.h" #include "calls/calls_instance.h" #include "calls/calls_top_bar.h" +#include "auth_session.h" +#include "storage/storage_facade.h" +#include "storage/storage_shared_media.h" namespace { @@ -1587,7 +1590,8 @@ void MainWidget::overviewPreloaded(PeerData *peer, const MTPmessages_Messages &r if (type == OverviewCount) return; - App::history(peer->id)->overviewSliceDone(type, result, true); + auto startMessageId = MsgId(0); + App::history(peer->id)->overviewSliceDone(type, startMessageId, result, true); Notify::mediaOverviewUpdated(peer, type); } @@ -1634,7 +1638,7 @@ void MainWidget::loadMediaBack(PeerData *peer, MediaOverviewType type, bool many return; } - _overviewLoad[type].insert(peer, MTP::send(MTPmessages_Search(MTP_flags(0), peer->input, MTPstring(), MTP_inputUserEmpty(), filter, MTP_int(0), MTP_int(0), MTP_int(minId), MTP_int(0), MTP_int(limit), MTP_int(0), MTP_int(0)), rpcDone(&MainWidget::overviewLoaded, history))); + _overviewLoad[type].insert(peer, MTP::send(MTPmessages_Search(MTP_flags(0), peer->input, MTPstring(), MTP_inputUserEmpty(), filter, MTP_int(0), MTP_int(0), MTP_int(minId), MTP_int(0), MTP_int(limit), MTP_int(0), MTP_int(0)), rpcDone(&MainWidget::overviewLoaded, { history, minId }))); } void MainWidget::checkLastUpdate(bool afterSleep) { @@ -1645,7 +1649,11 @@ void MainWidget::checkLastUpdate(bool afterSleep) { } } -void MainWidget::overviewLoaded(not_null history, const MTPmessages_Messages &result, mtpRequestId req) { +void MainWidget::overviewLoaded( + std::pair,MsgId> historyAndStartMsgId, + const MTPmessages_Messages &result, + mtpRequestId req) { + auto history = historyAndStartMsgId.first; OverviewsPreload::iterator it; MediaOverviewType type = OverviewCount; for (int32 i = 0; i < OverviewCount; ++i) { @@ -1658,7 +1666,7 @@ void MainWidget::overviewLoaded(not_null history, const MTPmessages_Me } if (type == OverviewCount) return; - history->overviewSliceDone(type, result); + history->overviewSliceDone(type, historyAndStartMsgId.second, result); Notify::mediaOverviewUpdated(history->peer, type); } @@ -4717,11 +4725,14 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { case mtpc_updateShortSentMessage: { auto &d = updates.c_updateShortSentMessage(); - if (randomId) { + if (!IsServerMsgId(d.vid.v)) { + LOG(("API Error: Bad msgId got from server: %1").arg(d.vid.v)); + } else if (randomId) { PeerId peerId = 0; QString text; App::histSentDataByItem(randomId, peerId, text); + auto wasAlready = peerId && (App::histItemById(peerToChannel(peerId), d.vid.v) != nullptr); feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date if (peerId) { if (auto item = App::histItemById(peerToChannel(peerId), d.vid.v)) { @@ -4732,6 +4743,14 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { item->setText({ text, entities }); item->updateMedia(d.has_media() ? (&d.vmedia) : nullptr); item->addToOverview(AddToOverviewNew); + if (!wasAlready) { + if (auto sharedMediaTypes = item->sharedMediaTypes()) { + Auth().storage().add(Storage::SharedMediaAddNew( + peerId, + sharedMediaTypes, + item->id)); + } + } } } } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index af50b0f2e..5bc2d5052 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -515,7 +515,10 @@ private: void readRequestDone(PeerData *peer); void messagesAffected(PeerData *peer, const MTPmessages_AffectedMessages &result); - void overviewLoaded(not_null history, const MTPmessages_Messages &result, mtpRequestId req); + void overviewLoaded( + std::pair, MsgId> historyAndStartMsgId, + const MTPmessages_Messages &result, + mtpRequestId req); void mediaOverviewUpdated(const Notify::PeerUpdate &update); Window::SectionSlideParams prepareShowAnimation(bool willHaveTopBarShadow, bool willHaveTabbedSection); diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp index b872cc7d9..f5b0ca0ec 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.cpp +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -341,7 +341,7 @@ DcOptions::Ids DcOptions::configEnumDcIds() const { } } } - std::sort(result.begin(), result.end()); + base::sort(result); return result; } diff --git a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp index bf0806351..9cabfb96e 100644 --- a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp +++ b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp @@ -181,7 +181,7 @@ bool UnsafeShowOpenWithDropdown(const QString &filepath, QPoint menuPosition) { if (!handlers.empty()) { HMENU menu = CreatePopupMenu(); - std::sort(handlers.begin(), handlers.end(), [](const OpenWithApp &a, const OpenWithApp &b) { + base::sort(handlers, [](const OpenWithApp &a, const OpenWithApp &b) { return a.name() < b.name(); }); for (int32 i = 0, l = handlers.size(); i < l; ++i) { diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index 9c1d5f5e3..d4e504eab 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -75,6 +75,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "base/optional.h" #include "base/algorithm.h" +#include "base/flat_set.h" +#include "base/flat_map.h" + #include "core/basic_types.h" #include "logs.h" #include "core/utils.h" diff --git a/Telegram/SourceFiles/storage/storage_facade.cpp b/Telegram/SourceFiles/storage/storage_facade.cpp new file mode 100644 index 000000000..201accd64 --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_facade.cpp @@ -0,0 +1,101 @@ +/* +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_facade.h" + +#include "storage/storage_shared_media.h" + +namespace Storage { + +class Facade::Impl { +public: + void add(SharedMediaAddNew &&query); + void add(SharedMediaAddExisting &&query); + void add(SharedMediaAddSlice &&query); + void remove(SharedMediaRemoveOne &&query); + void remove(SharedMediaRemoveAll &&query); + void query( + SharedMediaQuery &&query, + base::lambda_once &&callback); + +private: + SharedMedia _sharedMedia; + +}; + +void Facade::Impl::add(SharedMediaAddNew &&query) { + _sharedMedia.add(std::move(query)); +} + +void Facade::Impl::add(SharedMediaAddExisting &&query) { + _sharedMedia.add(std::move(query)); +} + +void Facade::Impl::add(SharedMediaAddSlice &&query) { + _sharedMedia.add(std::move(query)); +} + +void Facade::Impl::remove(SharedMediaRemoveOne &&query) { + _sharedMedia.remove(std::move(query)); +} + +void Facade::Impl::remove(SharedMediaRemoveAll &&query) { + _sharedMedia.remove(std::move(query)); +} + +void Facade::Impl::query( + SharedMediaQuery &&query, + base::lambda_once &&callback) { + _sharedMedia.query(query, std::move(callback)); +} + + +Facade::Facade() : _impl(std::make_unique()) { +} + +void Facade::add(SharedMediaAddNew &&query) { + _impl->add(std::move(query)); +} + +void Facade::add(SharedMediaAddExisting &&query) { + _impl->add(std::move(query)); +} + +void Facade::add(SharedMediaAddSlice &&query) { + _impl->add(std::move(query)); +} + +void Facade::remove(SharedMediaRemoveOne &&query) { + _impl->remove(std::move(query)); +} + +void Facade::remove(SharedMediaRemoveAll &&query) { + _impl->remove(std::move(query)); +} + +void Facade::query( + SharedMediaQuery &&query, + base::lambda_once &&callback) { + _impl->query(std::move(query), std::move(callback)); +} + +Facade::~Facade() = default; + +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_facade.h b/Telegram/SourceFiles/storage/storage_facade.h new file mode 100644 index 000000000..52e9d3476 --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_facade.h @@ -0,0 +1,56 @@ +/* +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 "base/enum_mask.h" + +namespace Storage { + +struct SharedMediaAddNew; +struct SharedMediaAddExisting; +struct SharedMediaAddSlice; +struct SharedMediaRemoveOne; +struct SharedMediaRemoveAll; +struct SharedMediaQuery; +struct SharedMediaResult; + +class Facade { +public: + Facade(); + + void add(SharedMediaAddNew &&query); + void add(SharedMediaAddExisting &&query); + void add(SharedMediaAddSlice &&query); + void remove(SharedMediaRemoveOne &&query); + void remove(SharedMediaRemoveAll &&query); + void query( + SharedMediaQuery &&query, + base::lambda_once &&callback); + + ~Facade(); + +private: + class Impl; + const std::unique_ptr _impl; + +}; + +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_shared_media.cpp b/Telegram/SourceFiles/storage/storage_shared_media.cpp new file mode 100644 index 000000000..e80e0bcc7 --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_shared_media.cpp @@ -0,0 +1,293 @@ +/* +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_shared_media.h" + +#include "base/task_queue.h" + +namespace Storage { + +SharedMedia::List::Slice::Slice( + base::flat_set &&messages, + MsgRange range) + : messages(std::move(messages)) + , range(range) { +} + +template +void SharedMedia::List::Slice::merge( + const Range &moreMessages, + MsgRange moreNoSkipRange) { + Expects(moreNoSkipRange.from <= range.till); + Expects(range.from <= moreNoSkipRange.till); + + messages.merge(std::begin(moreMessages), std::end(moreMessages)); + range = { + qMin(range.from, moreNoSkipRange.from), + qMax(range.till, moreNoSkipRange.till) + }; +} + +template +int SharedMedia::List::uniteAndAdd( + base::flat_set::iterator uniteFrom, + base::flat_set::iterator uniteTill, + const Range &messages, + MsgRange noSkipRange) { + 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) { + _slices.modify(uniteFrom, [&](Slice &slice) { + slice.merge(it->messages, it->range); + }); + } + _slices.erase(firstToErase, uniteTill); + } + return result; +} + +template +int SharedMedia::List::addRangeItemsAndCount( + const Range &messages, + MsgRange noSkipRange, + base::optional 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; + } + + auto uniteFrom = base::lower_bound( + _slices, + noSkipRange.from, + [](const Slice &slice, MsgId from) { return slice.range.till < from; }); + auto uniteTill = base::upper_bound( + _slices, + noSkipRange.till, + [](MsgId till, const Slice &slice) { return till < slice.range.from; }); + if (uniteFrom < uniteTill) { + return uniteAndAdd(uniteFrom, uniteTill, messages, noSkipRange); + } + + auto sliceMessages = base::flat_set { + std::begin(messages), + std::end(messages) }; + auto slice = _slices.emplace( + std::move(sliceMessages), + noSkipRange); + return slice->messages.size(); +} + +template +int SharedMedia::List::addRange( + const Range &messages, + MsgRange noSkipRange, + base::optional count) { + auto result = addRangeItemsAndCount(messages, noSkipRange, count); + if (_slices.size() == 1) { + if (_slices.front().range == MsgRange { 0, ServerMaxMsgId }) { + _count = _slices.front().messages.size(); + } + } + return result; +} + +void SharedMedia::List::addNew(MsgId messageId) { + auto range = { messageId }; + auto added = addRange(range, { messageId, ServerMaxMsgId }, base::none); + if (added > 0 && _count) { + *_count += added; + } +} + +void SharedMedia::List::addExisting( + MsgId messageId, + MsgRange noSkipRange) { + auto range = { messageId }; + addRange(range, noSkipRange, base::none); +} + +void SharedMedia::List::addSlice( + std::vector &&messageIds, + MsgRange noSkipRange, + base::optional count) { + addRange(messageIds, noSkipRange, count); +} + +void SharedMedia::List::removeOne(MsgId messageId) { + auto slice = base::lower_bound( + _slices, + messageId, + [](const Slice &slice, MsgId from) { return slice.range.till < from; }); + if (slice != _slices.end() && slice->range.from <= messageId) { + _slices.modify(slice, [messageId](Slice &slice) { + return slice.messages.remove(messageId); + }); + } + if (_count) { + --*_count; + } +} + +void SharedMedia::List::removeAll() { + _slices.clear(); + _slices.emplace(base::flat_set{}, MsgRange { 0, ServerMaxMsgId }); + _count = 0; +} + +void SharedMedia::List::query( + const SharedMediaQuery &query, + base::lambda_once &&callback) { + auto result = SharedMediaResult {}; + result.count = _count; + + auto slice = base::lower_bound( + _slices, + query.messageId, + [](const Slice &slice, MsgId id) { return slice.range.till < id; }); + if (slice != _slices.end() && slice->range.from <= query.messageId) { + result = queryFromSlice(query, *slice); + } else { + result.count = _count; + } + base::TaskQueue::Main().Put( + [ + callback = std::move(callback), + result = std::move(result) + ]() mutable { + callback(std::move(result)); + }); +} + +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 before = qMin(haveBefore, query.limitBefore); + auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1); + result.messageIds.reserve(before + equalOrAfter); + for ( + auto from = position - before, till = position + equalOrAfter; + from != till; + ++from) { + result.messageIds.push_back(*from); + } + if (slice.range.from == 0) { + result.skippedBefore = haveBefore - before; + } + if (slice.range.till == ServerMaxMsgId) { + result.skippedAfter = haveEqualOrAfter - equalOrAfter; + } + if (_count) { + result.count = _count; + if (!result.skippedBefore && result.skippedAfter) { + result.skippedBefore = *result.count + - *result.skippedAfter + - result.messageIds.size(); + } else if (!result.skippedAfter && result.skippedBefore) { + result.skippedAfter = *result.count + - *result.skippedBefore + - 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; + } + for (auto index = 0; index != kSharedMediaTypeCount; ++index) { + auto type = static_cast(index); + if (query.types.test(type)) { + peerIt->second[index].addNew(query.messageId); + } + } +} + +void SharedMedia::add(SharedMediaAddExisting &&query) { + auto peerIt = _lists.find(query.peerId); + if (peerIt == _lists.end()) { + peerIt = _lists.emplace(query.peerId, Lists {}).first; + } + for (auto index = 0; index != kSharedMediaTypeCount; ++index) { + auto type = static_cast(index); + if (query.types.test(type)) { + peerIt->second[index].addExisting(query.messageId, query.noSkipRange); + } + } +} + +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 index = static_cast(query.type); + peerIt->second[index].addSlice(std::move(query.messageIds), query.noSkipRange, query.count); +} + +void SharedMedia::remove(SharedMediaRemoveOne &&query) { + auto peerIt = _lists.find(query.peerId); + if (peerIt != _lists.end()) { + for (auto index = 0; index != kSharedMediaTypeCount; ++index) { + auto type = static_cast(index); + if (query.types.test(type)) { + peerIt->second[index].removeOne(query.messageId); + } + } + } +} + +void SharedMedia::remove(SharedMediaRemoveAll &&query) { + auto peerIt = _lists.find(query.peerId); + if (peerIt != _lists.end()) { + for (auto index = 0; index != kSharedMediaTypeCount; ++index) { + peerIt->second[index].removeAll(); + } + } +} + +void SharedMedia::query( + const SharedMediaQuery &query, + base::lambda_once &&callback) { + Expects(IsValidSharedMediaType(query.type)); + auto peerIt = _lists.find(query.peerId); + if (peerIt != _lists.end()) { + auto index = static_cast(query.type); + peerIt->second[index].query(query, std::move(callback)); + } +} + +} // namespace Storage \ No newline at end of file diff --git a/Telegram/SourceFiles/storage/storage_shared_media.h b/Telegram/SourceFiles/storage/storage_shared_media.h new file mode 100644 index 000000000..1a40daac4 --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_shared_media.h @@ -0,0 +1,229 @@ +/* +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 { + +// Allow forward declarations. +enum class SharedMediaType : char { + Photo = 0, + Video = 1, + MusicFile = 2, + File = 3, + VoiceFile = 4, + Link = 5, + ChatPhoto = 6, + RoundVoiceFile = 7, + GIF = 8, + + kCount = 9, +}; +constexpr auto kSharedMediaTypeCount = static_cast(SharedMediaType::kCount); +constexpr bool IsValidSharedMediaType(SharedMediaType type) { + return (static_cast(type) >= 0) + && (static_cast(type) < kSharedMediaTypeCount); +} + +using SharedMediaTypesMask = base::enum_mask; + +struct SharedMediaAddNew { + SharedMediaAddNew(PeerId peerId, SharedMediaTypesMask types, MsgId messageId) + : peerId(peerId), messageId(messageId), types(types) { + } + + PeerId peerId = 0; + MsgId messageId = 0; + SharedMediaTypesMask types; + +}; + +struct SharedMediaAddExisting { + SharedMediaAddExisting( + PeerId peerId, + SharedMediaTypesMask types, + MsgId messageId, + MsgRange noSkipRange) + : peerId(peerId) + , messageId(messageId) + , noSkipRange(noSkipRange) + , types(types) { + } + + PeerId peerId = 0; + MsgId messageId = 0; + MsgRange noSkipRange; + SharedMediaTypesMask types; + +}; + +struct SharedMediaAddSlice { + SharedMediaAddSlice( + PeerId peerId, + SharedMediaType type, + std::vector &&messageIds, + MsgRange noSkipRange, + base::optional count = base::none) + : peerId(peerId) + , messageIds(std::move(messageIds)) + , noSkipRange(noSkipRange) + , type(type) + , count(count) { + } + + PeerId peerId = 0; + std::vector messageIds; + MsgRange noSkipRange; + SharedMediaType type = SharedMediaType::kCount; + base::optional count; + +}; + +struct SharedMediaRemoveOne { + SharedMediaRemoveOne( + PeerId peerId, + SharedMediaTypesMask types, + MsgId messageId) + : peerId(peerId) + , messageId(messageId) + , types(types) { + } + + PeerId peerId = 0; + MsgId messageId = 0; + SharedMediaTypesMask types; + +}; + +struct SharedMediaRemoveAll { + SharedMediaRemoveAll(PeerId peerId) : peerId(peerId) { + } + + PeerId peerId = 0; + +}; + +struct SharedMediaQuery { + SharedMediaQuery( + PeerId peerId, + SharedMediaType type, + MsgId messageId, + int limitBefore, + int limitAfter) + : peerId(peerId) + , messageId(messageId) + , limitBefore(limitBefore) + , limitAfter(limitAfter) + , type(type) { + } + + PeerId peerId = 0; + MsgId messageId = 0; + int limitBefore = 0; + int limitAfter = 0; + SharedMediaType type = SharedMediaType::kCount; + +}; + +struct SharedMediaResult { + base::optional count; + base::optional skippedBefore; + base::optional skippedAfter; + std::vector messageIds; +}; + +class SharedMedia { +public: + using Type = SharedMediaType; + + void add(SharedMediaAddNew &&query); + void add(SharedMediaAddExisting &&query); + void add(SharedMediaAddSlice &&query); + void remove(SharedMediaRemoveOne &&query); + void remove(SharedMediaRemoveAll &&query); + void query( + const SharedMediaQuery &query, + base::lambda_once &&callback); + +private: + class List { + public: + void addNew(MsgId messageId); + void addExisting(MsgId messageId, MsgRange noSkipRange); + void addSlice( + std::vector &&messageIds, + MsgRange noSkipRange, + base::optional count); + void removeOne(MsgId messageId); + void removeAll(); + void query( + const SharedMediaQuery &query, + base::lambda_once &&callback); + + private: + struct Slice { + Slice(base::flat_set &&messages, MsgRange range); + + template + void merge(const Range &moreMessages, MsgRange moreNoSkipRange); + + base::flat_set messages; + MsgRange range; + + inline bool operator<(const Slice &other) const { + return range.from < other.range.from; + } + + }; + + template + int uniteAndAdd( + base::flat_set::iterator uniteFrom, + base::flat_set::iterator uniteTill, + const Range &messages, + MsgRange noSkipRange); + template + int addRangeItemsAndCount( + const Range &messages, + MsgRange noSkipRange, + base::optional count); + template + int addRange( + const Range &messages, + MsgRange noSkipRange, + base::optional count); + + SharedMediaResult queryFromSlice( + const SharedMediaQuery &query, + const Slice &slice); + + base::optional _count; + base::flat_set _slices; + + }; + using Lists = std::array; + + std::map _lists; + +}; + +} // namespace Storage diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index c9ee2856e..e8d2b0254 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -48,12 +48,43 @@ inline StorageKey mediaKey(const MTPDfileLocation &location) { return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v); } -typedef int32 UserId; -typedef int32 ChatId; -typedef int32 ChannelId; -static const ChannelId NoChannel = 0; +using UserId = int32; +using ChatId = int32; +using ChannelId = int32; +constexpr auto NoChannel = ChannelId(0); + +using MsgId = int32; +constexpr auto StartClientMsgId = MsgId(-0x7FFFFFFF); +constexpr auto EndClientMsgId = MsgId(-0x40000000); +constexpr auto ShowAtTheEndMsgId = MsgId(-0x40000000); +constexpr auto SwitchAtTopMsgId = MsgId(-0x3FFFFFFF); +constexpr auto ShowAtProfileMsgId = MsgId(-0x3FFFFFFE); +constexpr auto ShowAndStartBotMsgId = MsgId(-0x3FFFFFD); +constexpr auto ShowAtGameShareMsgId = MsgId(-0x3FFFFFC); +constexpr auto ServerMaxMsgId = MsgId(0x3FFFFFFF); +constexpr auto ShowAtUnreadMsgId = MsgId(0); +constexpr inline bool IsClientMsgId(MsgId id) { + return (id >= StartClientMsgId && id < EndClientMsgId); +} +constexpr inline bool IsServerMsgId(MsgId id) { + return (id > 0 && id < ServerMaxMsgId); +} + +struct MsgRange { + MsgRange() = default; + MsgRange(MsgId from, MsgId till) : from(from), till(till) { + } + + MsgId from = 0; + MsgId till = 0; +}; +inline bool operator==(const MsgRange &a, const MsgRange &b) { + return (a.from == b.from) && (a.till == b.till); +} +inline bool operator!=(const MsgRange &a, const MsgRange &b) { + return !(a == b); +} -typedef int32 MsgId; struct FullMsgId { FullMsgId() = default; FullMsgId(ChannelId channel, MsgId msg) : channel(channel), msg(msg) { @@ -61,13 +92,24 @@ struct FullMsgId { ChannelId channel = NoChannel; MsgId msg = 0; }; +inline bool operator==(const FullMsgId &a, const FullMsgId &b) { + return (a.channel == b.channel) && (a.msg == b.msg); +} +inline bool operator!=(const FullMsgId &a, const FullMsgId &b) { + return !(a == b); +} +inline bool operator<(const FullMsgId &a, const FullMsgId &b) { + if (a.msg < b.msg) return true; + if (a.msg > b.msg) return false; + return a.channel < b.channel; +} -typedef uint64 PeerId; -static const uint64 PeerIdMask = 0xFFFFFFFFULL; -static const uint64 PeerIdTypeMask = 0x300000000ULL; -static const uint64 PeerIdUserShift = 0x000000000ULL; -static const uint64 PeerIdChatShift = 0x100000000ULL; -static const uint64 PeerIdChannelShift = 0x200000000ULL; +using PeerId = uint64; +constexpr auto PeerIdMask = PeerId(0xFFFFFFFFULL); +constexpr auto PeerIdTypeMask = PeerId(0x300000000ULL); +constexpr auto PeerIdUserShift = PeerId(0x000000000ULL); +constexpr auto PeerIdChatShift = PeerId(0x100000000ULL); +constexpr auto PeerIdChannelShift = PeerId(0x200000000ULL); inline bool peerIsUser(const PeerId &id) { return (id & PeerIdTypeMask) == PeerIdUserShift; } @@ -170,32 +212,7 @@ using AudioId = uint64; using DocumentId = uint64; using WebPageId = uint64; using GameId = uint64; -static const WebPageId CancelledWebPageId = 0xFFFFFFFFFFFFFFFFULL; - -inline bool operator==(const FullMsgId &a, const FullMsgId &b) { - return (a.channel == b.channel) && (a.msg == b.msg); -} -inline bool operator!=(const FullMsgId &a, const FullMsgId &b) { - return !(a == b); -} -inline bool operator<(const FullMsgId &a, const FullMsgId &b) { - if (a.msg < b.msg) return true; - if (a.msg > b.msg) return false; - return a.channel < b.channel; -} - -constexpr const MsgId StartClientMsgId = -0x7FFFFFFF; -constexpr const MsgId EndClientMsgId = -0x40000000; -inline constexpr bool isClientMsgId(MsgId id) { - return id >= StartClientMsgId && id < EndClientMsgId; -} -constexpr const MsgId ShowAtTheEndMsgId = -0x40000000; -constexpr const MsgId SwitchAtTopMsgId = -0x3FFFFFFF; -constexpr const MsgId ShowAtProfileMsgId = -0x3FFFFFFE; -constexpr const MsgId ShowAndStartBotMsgId = -0x3FFFFFD; -constexpr const MsgId ShowAtGameShareMsgId = -0x3FFFFFC; -constexpr const MsgId ServerMaxMsgId = 0x3FFFFFFF; -constexpr const MsgId ShowAtUnreadMsgId = 0; +constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL); struct NotifySettings { NotifySettings() : flags(MTPDpeerNotifySettings::Flag::f_show_previews), sound(qsl("default")) { diff --git a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp b/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp index ebe23cbd5..00ff10e00 100644 --- a/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp +++ b/Telegram/ThirdParty/emoji_suggestions/emoji_suggestions.cpp @@ -386,33 +386,36 @@ int Completer::findEqualCharsCount(int position, const utf16string *word) { std::vector Completer::prepareResult() { auto firstCharOfQuery = _query[0]; - std::stable_partition(_result.begin(), _result.end(), [firstCharOfQuery](Result &result) { + base::stable_partition(_result, [firstCharOfQuery](Result &result) { auto firstCharAfterColon = result.replacement->replacement[1]; return (firstCharAfterColon == firstCharOfQuery); }); - std::stable_partition(_result.begin(), _result.end(), [](Result &result) { + base::stable_partition(_result, [](Result &result) { return (result.wordsUsed < 2); }); - std::stable_partition(_result.begin(), _result.end(), [](Result &result) { + base::stable_partition(_result, [](Result &result) { return (result.wordsUsed < 3); }); - std::stable_partition(_result.begin(), _result.end(), [this](Result &result) { + base::stable_partition(_result, [this](Result &result) { return isExactMatch(result.replacement->replacement); }); auto result = std::vector(); result.reserve(_result.size()); for (auto &item : _result) { - result.emplace_back(item.replacement->emoji, item.replacement->replacement, item.replacement->replacement); + result.emplace_back( + item.replacement->emoji, + item.replacement->replacement, + item.replacement->replacement); } return result; } string_span Completer::findWordsStartingWith(utf16char ch) { - auto begin = std::lower_bound(_currentItemWords.begin(), _currentItemWords.end(), ch, [](utf16string word, utf16char ch) { + auto begin = base::lower_bound(_currentItemWords, ch, [](utf16string word, utf16char ch) { return word[0] < ch; }); - auto end = std::upper_bound(_currentItemWords.begin(), _currentItemWords.end(), ch, [](utf16char ch, utf16string word) { + auto end = base::upper_bound(_currentItemWords, ch, [](utf16char ch, utf16string word) { return ch < word[0]; }); return _currentItemWords.subspan(begin - _currentItemWords.begin(), end - begin); diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 50d373a1a..ef644e171 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -2,6 +2,7 @@ <(src_loc)/base/assertion.h <(src_loc)/base/build_config.h <(src_loc)/base/flags.h +<(src_loc)/base/enum_mask.h <(src_loc)/base/flat_map.h <(src_loc)/base/flat_set.h <(src_loc)/base/lambda.h @@ -176,6 +177,7 @@ <(src_loc)/history/history_location_manager.cpp <(src_loc)/history/history_location_manager.h <(src_loc)/history/history_media.h +<(src_loc)/history/history_media.cpp <(src_loc)/history/history_media_types.cpp <(src_loc)/history/history_media_types.h <(src_loc)/history/history_message.cpp @@ -440,6 +442,10 @@ <(src_loc)/storage/serialize_common.h <(src_loc)/storage/serialize_document.cpp <(src_loc)/storage/serialize_document.h +<(src_loc)/storage/storage_facade.cpp +<(src_loc)/storage/storage_facade.h +<(src_loc)/storage/storage_shared_media.cpp +<(src_loc)/storage/storage_shared_media.h <(src_loc)/ui/effects/cross_animation.cpp <(src_loc)/ui/effects/cross_animation.h <(src_loc)/ui/effects/panel_animation.cpp