Add SharedMediaSlice to observe shared media.

Start testing / using it in MediaView.
This commit is contained in:
John Preston 2017-08-18 22:14:31 +03:00
parent 41ed2d1b84
commit 2363a6bd44
15 changed files with 1548 additions and 450 deletions

View File

@ -35,6 +35,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/notifications_manager.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h"
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
namespace {
@ -46,6 +48,7 @@ constexpr auto kStickersUpdateTimeout = 3600000; // update not more than once in
constexpr auto kUnreadMentionsPreloadIfLess = 5;
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
constexpr auto kUnreadMentionsNextRequestLimit = 100;
constexpr auto kSharedMediaLimit = 10;
} // namespace
@ -1911,4 +1914,154 @@ void ApiWrap::sendSaveChatAdminsRequests(not_null<ChatData*> chat) {
requestSendDelayed();
}
void ApiWrap::requestSharedMedia(
not_null<PeerData*> peer,
SharedMediaType type,
MsgId messageId,
SliceType slice) {
auto key = std::make_tuple(peer, type, messageId, slice);
if (_sharedMediaRequests.contains(key)) {
return;
}
auto filter = [&] {
using Type = SharedMediaType;
switch (type) {
case Type::Photo: return MTP_inputMessagesFilterPhotos();
case Type::Video: return MTP_inputMessagesFilterVideo();
case Type::MusicFile: return MTP_inputMessagesFilterMusic();
case Type::File: return MTP_inputMessagesFilterDocument();
case Type::VoiceFile: return MTP_inputMessagesFilterVoice();
case Type::RoundVoiceFile: return MTP_inputMessagesFilterRoundVoice();
case Type::GIF: return MTP_inputMessagesFilterGif();
case Type::Link: return MTP_inputMessagesFilterUrl();
case Type::ChatPhoto: return MTP_inputMessagesFilterChatPhotos();
}
return MTP_inputMessagesFilterEmpty();
}();
if (filter.type() == mtpc_inputMessagesFilterEmpty) {
return;
}
auto minId = 0;
auto maxId = 0;
auto limit = messageId ? kSharedMediaLimit : 0;
auto offsetId = [&] {
switch (slice) {
case SliceType::Before:
case SliceType::Around: return messageId;
case SliceType::After: return messageId + 1;
}
Unexpected("Slice type in ApiWrap::requestSharedMedia");
}();
auto addOffset = [&] {
switch (slice) {
case SliceType::Before: return 0;
case SliceType::Around: return -limit / 2;
case SliceType::After: return -limit;
}
Unexpected("Slice type in ApiWrap::requestSharedMedia");
}();
LOG(("REQUESTING SHARED MEDIA: %1, %2, %3").arg(static_cast<int>(type)).arg(messageId).arg(static_cast<int>(slice)));
auto requestId = request(MTPmessages_Search(
MTP_flags(0),
peer->input,
MTPstring(),
MTP_inputUserEmpty(),
filter,
MTP_int(0),
MTP_int(0),
MTP_int(offsetId),
MTP_int(addOffset),
MTP_int(limit),
MTP_int(maxId),
MTP_int(minId)
)).done([this, peer, type, messageId, slice](const MTPmessages_Messages &result) {
_sharedMediaRequests.remove(std::make_tuple(peer, type, messageId, slice));
sharedMediaDone(peer, type, messageId, slice, result);
}).fail([this, key](const RPCError &error) {
_sharedMediaRequests.remove(key);
}).send();
_sharedMediaRequests.emplace(key, requestId);
}
void ApiWrap::sharedMediaDone(
not_null<PeerData*> peer,
SharedMediaType type,
MsgId messageId,
SliceType slice,
const MTPmessages_Messages &result) {
auto fullCount = 0;
auto &messages = *[&] {
switch (result.type()) {
case mtpc_messages_messages: {
auto &d = result.c_messages_messages();
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
fullCount = d.vmessages.v.size();
return &d.vmessages.v;
} break;
case mtpc_messages_messagesSlice: {
auto &d = result.c_messages_messagesSlice();
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
fullCount = d.vcount.v;
return &d.vmessages.v;
} break;
case mtpc_messages_channelMessages: {
auto &d = result.c_messages_channelMessages();
if (auto channel = peer->asChannel()) {
channel->ptsReceived(d.vpts.v);
} else {
LOG(("API Error: received messages.channelMessages when no channel was passed! (ApiWrap::sharedMediaDone)"));
}
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
fullCount = d.vcount.v;
return &d.vmessages.v;
} break;
}
Unexpected("messages.Messages type in sharedMediaDone()");
}();
auto noSkipRange = MsgRange { messageId, messageId };
auto messageIds = std::vector<MsgId>();
auto addType = NewMessageExisting;
messageIds.reserve(messages.size());
for (auto &message : messages) {
if (auto item = App::histories().addNewMessage(message, addType)) {
if (item->sharedMediaTypes().test(type)) {
auto itemId = item->id;
messageIds.push_back(itemId);
accumulate_min(noSkipRange.from, itemId);
accumulate_max(noSkipRange.till, itemId);
}
}
}
if (messageId && messageIds.empty()) {
noSkipRange = [&]() -> MsgRange {
switch (slice) {
case SliceType::Before: // All old loaded.
return { 0, noSkipRange.till };
case SliceType::Around: // All loaded.
return { 0, ServerMaxMsgId };
case SliceType::After: // All new loaded.
return { noSkipRange.from, ServerMaxMsgId };
}
Unexpected("Slice type in ApiWrap::sharedMediaDone");
}();
}
Auth().storage().add(Storage::SharedMediaAddSlice(
peer->id,
type,
std::move(messageIds),
noSkipRange,
fullCount
));
}
ApiWrap::~ApiWrap() = default;

View File

@ -28,6 +28,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
class AuthSession;
namespace Storage {
enum class SharedMediaType : char;
} // namespace Storage
namespace Api {
inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) {
@ -108,6 +112,22 @@ public:
bool adminsEnabled,
base::flat_set<not_null<UserData*>> &&admins);
enum class SliceType {
Around,
Before,
After,
};
void requestSharedMedia(
not_null<PeerData*> peer,
Storage::SharedMediaType type,
MsgId messageId,
SliceType slice);
void requestSharedMediaCount(
not_null<PeerData*> peer,
Storage::SharedMediaType type) {
requestSharedMedia(peer, type, 0, SliceType::Before);
}
~ApiWrap();
private:
@ -117,6 +137,7 @@ private:
Callbacks callbacks;
};
using MessageDataRequests = QMap<MsgId, MessageDataRequest>;
using SharedMediaType = Storage::SharedMediaType;
void requestAppChangelogs();
void addLocalChangelogs(int oldAppVersion);
@ -153,6 +174,13 @@ private:
void saveChatAdmins(not_null<ChatData*> chat);
void sendSaveChatAdminsRequests(not_null<ChatData*> chat);
void sharedMediaDone(
not_null<PeerData*> peer,
SharedMediaType type,
MsgId messageId,
SliceType slice,
const MTPmessages_Messages &result);
not_null<AuthSession*> _session;
mtpRequestId _changelogSubscription = 0;
@ -205,9 +233,21 @@ private:
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;
base::flat_map<not_null<ChatData*>, mtpRequestId> _chatAdminsEnabledRequests;
base::flat_map<not_null<ChatData*>, base::flat_set<not_null<UserData*>>> _chatAdminsToSave;
base::flat_map<not_null<ChatData*>, base::flat_set<mtpRequestId>> _chatAdminsSaveRequests;
base::flat_map<
not_null<ChatData*>,
mtpRequestId> _chatAdminsEnabledRequests;
base::flat_map<
not_null<ChatData*>,
base::flat_set<not_null<UserData*>>> _chatAdminsToSave;
base::flat_map<
not_null<ChatData*>,
base::flat_set<mtpRequestId>> _chatAdminsSaveRequests;
base::flat_map<std::tuple<
not_null<PeerData*>,
SharedMediaType,
MsgId,
SliceType>, mtpRequestId> _sharedMediaRequests;
base::Observable<PeerData*> _fullPeerUpdated;

View File

@ -227,7 +227,7 @@ public:
void checkAutoLockIn(TimeMs time);
base::Observable<DocumentData*> documentUpdated;
base::Observable<std::pair<HistoryItem*, MsgId>> messageIdChanging;
base::Observable<std::pair<not_null<HistoryItem*>, MsgId>> messageIdChanging;
~AuthSession();

View File

@ -366,9 +366,9 @@ class Observable : public internal::BaseObservable<EventType, Handler, base::typ
public:
Observable() = default;
Observable(const Observable &other) = delete;
Observable(Observable &&other) = delete;
Observable(Observable &&other) = default;
Observable &operator=(const Observable &other) = delete;
Observable &operator=(Observable &&other) = delete;
Observable &operator=(Observable &&other) = default;
};

View File

@ -87,7 +87,6 @@ enum {
SearchManyPerPage = 100,
LinksOverviewPerPage = 12,
MediaOverviewStartPerPage = 5,
MediaOverviewPreloadCount = 4,
AudioVoiceMsgMaxLength = 100 * 60, // 100 minutes
AudioVoiceMsgUpdateView = 100, // 100ms

View File

@ -0,0 +1,409 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "history/history_shared_media.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
namespace {
using Type = SharedMediaViewer::Type;
inline MediaOverviewType SharedMediaTypeToOverview(Type type) {
switch (type) {
case Type::Photo: return OverviewPhotos;
case Type::Video: return OverviewVideos;
case Type::MusicFile: return OverviewMusicFiles;
case Type::File: return OverviewFiles;
case Type::VoiceFile: return OverviewVoiceFiles;
case Type::Link: return OverviewLinks;
default: break;
}
return OverviewCount;
}
not_null<History*> GetActualHistory(not_null<History*> history) {
if (auto to = history->peer->migrateTo()) {
return App::history(to);
}
return history;
}
History *GetMigratedHistory(
not_null<History*> passedHistory,
not_null<History*> actualHistory) {
if (actualHistory != passedHistory) {
return passedHistory;
} else if (auto from = actualHistory->peer->migrateFrom()) {
return App::history(from);
}
return nullptr;
}
} // namespace
SharedMediaViewer::SharedMediaViewer(
Key key,
int limitBefore,
int limitAfter)
: _key(key)
, _limitBefore(limitBefore)
, _limitAfter(limitAfter)
, _data(_key) {
}
base::optional<Storage::SharedMediaType> SharedMediaOverviewType(
Storage::SharedMediaType type) {
if (SharedMediaTypeToOverview(type) != OverviewCount) {
return type;
}
return base::none;
}
void SharedMediaShowOverview(
Storage::SharedMediaType type,
not_null<History*> history) {
if (SharedMediaOverviewType(type)) {
Ui::showPeerOverview(history, SharedMediaTypeToOverview(type));
}
}
void SharedMediaViewer::start() {
auto applyUpdateCallback = [this](auto &update) {
this->applyUpdate(update);
};
subscribe(Auth().storage().sharedMediaSliceUpdated(), applyUpdateCallback);
subscribe(Auth().storage().sharedMediaOneRemoved(), applyUpdateCallback);
subscribe(Auth().storage().sharedMediaAllRemoved(), applyUpdateCallback);
loadInitial();
}
void SharedMediaViewer::loadInitial() {
auto weak = base::make_weak_unique(this);
Auth().storage().query(Storage::SharedMediaQuery(
_key,
_limitBefore,
_limitAfter), [weak](Storage::SharedMediaResult &&result) {
if (weak) {
weak->applyStoredResult(std::move(result));
}
});
}
void SharedMediaViewer::applyStoredResult(Storage::SharedMediaResult &&result) {
mergeSliceData(
result.count,
result.messageIds,
result.skippedBefore,
result.skippedAfter);
}
void SharedMediaViewer::mergeSliceData(
base::optional<int> count,
const base::flat_set<MsgId> &messageIds,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter) {
if (messageIds.empty()) {
if (count && *_data._fullCount != *count) {
_data._fullCount = count;
updated.notify(_data);
}
return;
}
if (count) {
_data._fullCount = count;
}
auto wasMinId = _data._ids.empty() ? -1 : _data._ids.front();
auto wasMaxId = _data._ids.empty() ? -1 : _data._ids.back();
_data._ids.merge(messageIds.begin(), messageIds.end());
auto adjustSkippedBefore = [&](MsgId oldId, int oldSkippedBefore) {
auto it = _data._ids.find(oldId);
Assert(it != _data._ids.end());
_data._skippedBefore = oldSkippedBefore - (it - _data._ids.begin());
accumulate_max(*_data._skippedBefore, 0);
};
if (skippedBefore) {
adjustSkippedBefore(messageIds.front(), *skippedBefore);
} else if (wasMinId >= 0 && _data._skippedBefore) {
adjustSkippedBefore(wasMinId, *_data._skippedBefore);
} else {
_data._skippedBefore = base::none;
}
auto adjustSkippedAfter = [&](MsgId oldId, int oldSkippedAfter) {
auto it = _data._ids.find(oldId);
Assert(it != _data._ids.end());
_data._skippedAfter = oldSkippedAfter - (_data._ids.end() - it - 1);
accumulate_max(*_data._skippedAfter, 0);
};
if (skippedAfter) {
adjustSkippedAfter(messageIds.back(), *skippedAfter);
} else if (wasMaxId >= 0 && _data._skippedAfter) {
adjustSkippedAfter(wasMaxId, *_data._skippedAfter);
} else {
_data._skippedAfter = base::none;
}
if (_data._fullCount) {
if (_data._skippedBefore && !_data._skippedAfter) {
_data._skippedAfter = *_data._fullCount
- *_data._skippedBefore
- int(_data._ids.size());
} else if (_data._skippedAfter && !_data._skippedBefore) {
_data._skippedBefore = *_data._fullCount
- *_data._skippedAfter
- int(_data._ids.size());
}
}
sliceToLimits();
updated.notify(_data);
}
void SharedMediaViewer::applyUpdate(const SliceUpdate &update) {
if (update.peerId != _key.peerId || update.type != _key.type) {
return;
}
auto intersects = [](MsgRange range1, MsgRange range2) {
return (range1.from <= range2.till) && (range2.from <= range1.till);
};
if (!intersects(update.range, {
_data._ids.empty() ? _key.messageId : _data._ids.front(),
_data._ids.empty() ? _key.messageId : _data._ids.back() })) {
return;
}
auto skippedBefore = (update.range.from == 0)
? 0
: base::optional<int> {};
auto skippedAfter = (update.range.till == ServerMaxMsgId)
? 0
: base::optional<int> {};
mergeSliceData(
update.count,
update.messages ? *update.messages : base::flat_set<MsgId> {},
skippedBefore,
skippedAfter);
}
void SharedMediaViewer::applyUpdate(const OneRemoved &update) {
if (update.peerId != _key.peerId || !update.types.test(_key.type)) {
return;
}
auto changed = false;
if (_data._fullCount && *_data._fullCount > 0) {
--*_data._fullCount;
changed = true;
}
if (_data._ids.contains(update.messageId)) {
_data._ids.remove(update.messageId);
changed = true;
} else if (!_data._ids.empty()) {
if (_data._ids.front() > update.messageId
&& _data._skippedBefore
&& *_data._skippedBefore > 0) {
--*_data._skippedBefore;
changed = true;
} else if (_data._ids.back() < update.messageId
&& _data._skippedAfter
&& *_data._skippedAfter > 0) {
--*_data._skippedAfter;
changed = true;
}
}
if (changed) {
updated.notify(_data);
}
}
void SharedMediaViewer::applyUpdate(const AllRemoved &update) {
if (update.peerId != _key.peerId) {
return;
}
_data = SharedMediaSlice(_key, 0);
updated.notify(_data);
}
void SharedMediaViewer::sliceToLimits() {
auto aroundIt = base::lower_bound(_data._ids, _key.messageId);
auto removeFromBegin = (aroundIt - _data._ids.begin() - _limitBefore);
auto removeFromEnd = (_data._ids.end() - aroundIt - _limitAfter - 1);
if (removeFromBegin > 0) {
_data._ids.erase(_data._ids.begin(), _data._ids.begin() + removeFromBegin);
if (_data._skippedBefore) {
*_data._skippedBefore += removeFromBegin;
}
} else if (removeFromBegin < 0 && (!_data._skippedBefore || *_data._skippedBefore > 0)) {
requestMessages(RequestDirection::Before);
}
if (removeFromEnd > 0) {
_data._ids.erase(_data._ids.end() - removeFromEnd, _data._ids.end());
if (_data._skippedAfter) {
*_data._skippedAfter += removeFromEnd;
}
} else if (removeFromEnd < 0 && (!_data._skippedAfter || *_data._skippedAfter > 0)) {
requestMessages(RequestDirection::After);
}
}
void SharedMediaViewer::requestMessages(RequestDirection direction) {
using SliceType = ApiWrap::SliceType;
auto requestAroundData = [&]() -> std::pair<MsgId, SliceType> {
if (_data._ids.empty()) {
return { _key.messageId, SliceType::Around };
} else if (direction == RequestDirection::Before) {
return { _data._ids.front(), SliceType::Before };
}
return { _data._ids.back(), SliceType::After };
}();
Auth().api().requestSharedMedia(
App::peer(_key.peerId),
_key.type,
requestAroundData.first,
requestAroundData.second);
}
//
//base::optional<int> SharedMediaViewerMerged::Data::fullCount() const {
// if (_historyCount && _migratedCount) {
// return (*_historyCount + *_migratedCount);
// }
// return base::none;
//}
//base::optional<int> SharedMediaViewerMerged::Data::skippedBefore() const {
// if (_ids.empty()) {
// return base::none;
// } else if (!IsServerMsgId(_ids.front())) {
// return _migratedSkippedBefore;
// } else if (_historySkippedBefore && _migratedCount) {
// return *_historySkippedBefore + *_migratedCount;
// }
// return base::none;
//}
//
//base::optional<int> SharedMediaViewerMerged::Data::skippedAfter() const {
// if (_ids.empty()) {
// return base::none;
// } else if (IsServerMsgId(_ids.back())) {
// return _historySkippedAfter;
// } else if (_migratedSkippedAfter && _historyCount) {
// return *_migratedSkippedAfter + *_historyCount;
// }
// return base::none;
//}
//
//SharedMediaViewerMerged::SharedMediaViewerMerged(
// Type type,
// not_null<History*> history,
// MsgId aroundId,
// int limitBefore,
// int limitAfter)
//: _type(type)
//, _history(GetActualHistory(history))
//, _migrated(GetMigratedHistory(history, _history))
//, _universalAroundId((_history == _migrated) ? -aroundId : aroundId)
//, _limitBefore(limitBefore)
//, _limitAfter(limitAfter)
//, _data(_history, _migrated) {
//}
//
//bool SharedMediaViewerMerged::hasOverview() const {
// return SharedMediaTypeToOverview(_type) != OverviewCount;
//}
//
//void SharedMediaViewerMerged::showOverview() const {
// if (hasOverview()) {
// Ui::showPeerOverview(_history, SharedMediaTypeToOverview(_type));
// }
//}
//
//bool SharedMediaViewerMerged::moveTo(const SharedMediaViewerMerged &other) {
// if (_history != other._history || _type != other._type) {
// return false;
// }
// _universalAroundId = other._universalAroundId;
// if (!containsAroundId()) {
// clearAfterMove();
// }
// load();
// return true;
//}
//
//bool SharedMediaViewerMerged::containsAroundId() const {
// if (_data._ids.empty()) {
// return false;
// }
// auto min = _data._ids.front();
// auto max = _data._ids.back();
// if (IsServerMsgId(_universalAroundId)) {
// return (!IsServerMsgId(min) || min <= aroundId())
// && (IsServerMsgId(max) && max >= aroundId());
// }
// return (!IsServerMsgId(min) && -min <= aroundId())
// && (IsServerMsgId(max) || -max >= aroundId());
//}
//
//bool SharedMediaViewerMerged::amAroundMigrated() const {
// return !IsServerMsgId(_universalAroundId);
//}
//
//not_null<History*> SharedMediaViewerMerged::aroundHistory() const {
// return amAroundMigrated() ? _migrated : _history;
//}
//
//MsgId SharedMediaViewerMerged::aroundId() const {
// return amAroundMigrated() ? -_universalAroundId : _universalAroundId;
//}
//
//void SharedMediaViewerMerged::clearAfterMove() {
// _data = Data(_history, _migrated, _data._historyCount, _data._migratedCount);
//}
//
//void SharedMediaViewerMerged::load() {
// auto weak = base::make_weak_unique(this);
// auto peer = aroundHistory()->peer;
// Auth().storage().query(Storage::SharedMediaQuery(
// peer->id,
// _type,
// aroundId(),
// _limitBefore,
// _limitAfter), [weak, peer](Storage::SharedMediaResult &&result) {
// if (weak) {
// weak->applyStoredResult(peer, std::move(result));
// }
// });
//}
//
//void SharedMediaViewerMerged::applyStoredResult(
// not_null<PeerData*> peer,
// Storage::SharedMediaResult &&result) {
// if (aroundHistory()->peer != peer) {
// return;
// }
// auto aroundMigrated = amAroundMigrated();
// if (result.count) {
// (aroundMigrated ? _data._migratedCount : _data._historyCount) = result.count;
// }
//}

View File

@ -0,0 +1,316 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "storage/storage_shared_media.h"
#include "mtproto/sender.h"
#include "base/weak_unique_ptr.h"
base::optional<Storage::SharedMediaType> SharedMediaOverviewType(
Storage::SharedMediaType type);
void SharedMediaShowOverview(
Storage::SharedMediaType type,
not_null<History*> history);
class SharedMediaViewer;
class SharedMediaSlice {
public:
using Key = Storage::SharedMediaKey;
SharedMediaSlice(
Key key,
base::optional<int> fullCount = base::none)
: _key(key)
, _fullCount(fullCount) {
}
base::optional<int> fullCount() const {
return _fullCount;
}
base::optional<int> skippedBefore() const {
return _skippedBefore;
}
base::optional<int> skippedAfter() const {
return _skippedAfter;
}
base::optional<int> indexOf(MsgId msgId) const {
auto it = _ids.find(msgId);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return base::none;
}
int size() const {
return _ids.size();
}
using iterator = base::flat_set<MsgId>::const_iterator;
iterator begin() const {
return _ids.begin();
}
iterator end() const {
return _ids.end();
}
iterator cbegin() const {
return begin();
}
iterator cend() const {
return end();
}
base::optional<int> distance(const Key &a, const Key &b) const {
if (a.type != _key.type
|| b.type != _key.type
|| a.peerId != _key.peerId
|| b.peerId != _key.peerId) {
return base::none;
}
auto i = _ids.find(a.messageId);
auto j = _ids.find(b.messageId);
if (i == _ids.end() || j == _ids.end()) {
return base::none;
}
return j - i;
}
private:
Key _key;
base::flat_set<MsgId> _ids;
MsgRange _range;
base::optional<int> _fullCount;
base::optional<int> _skippedBefore;
base::optional<int> _skippedAfter;
friend class SharedMediaViewer;
};
class SharedMediaViewer :
private base::Subscriber,
public base::enable_weak_from_this {
public:
using Type = Storage::SharedMediaType;
using Key = Storage::SharedMediaKey;
SharedMediaViewer(
Key key,
int limitBefore,
int limitAfter);
void start();
base::Observable<SharedMediaSlice> updated;
private:
using InitialResult = Storage::SharedMediaResult;
using SliceUpdate = Storage::SharedMediaSliceUpdate;
using OneRemoved = Storage::SharedMediaRemoveOne;
using AllRemoved = Storage::SharedMediaRemoveAll;
void loadInitial();
enum class RequestDirection {
Before,
After,
};
void requestMessages(RequestDirection direction);
void applyStoredResult(InitialResult &&result);
void applyUpdate(const SliceUpdate &update);
void applyUpdate(const OneRemoved &update);
void applyUpdate(const AllRemoved &update);
void sliceToLimits();
void mergeSliceData(
base::optional<int> count,
const base::flat_set<MsgId> &messageIds,
base::optional<int> skippedBefore = base::none,
base::optional<int> skippedAfter = base::none);
Key _key;
int _limitBefore = 0;
int _limitAfter = 0;
mtpRequestId _beforeRequestId = 0;
mtpRequestId _afterRequestId = 0;
SharedMediaSlice _data;
};
//
//class SharedMediaSliceMerged :
// private MTP::Sender,
// private base::Subscriber,
// public base::enable_weak_from_this {
//public:
// class Data;
//
//private:
// friend class Data;
// using UniversalMsgId = MsgId;
//
//public:
// using Type = Storage::SharedMediaType;
//
// SharedMediaSliceMerged(
// Type type,
// not_null<History*> history,
// MsgId aroundId,
// int limitBefore,
// int limitAfter);
//
// bool hasOverview() const;
// void showOverview() const;
// bool moveTo(const SharedMediaSliceMerged &other);
//
// void load();
//
// class Data {
// public:
// base::optional<int> fullCount() const;
// base::optional<int> skippedBefore() const;
// base::optional<int> skippedAfter() const;
// int size() const {
// return _ids.size();
// }
//
// class iterator {
// public:
// FullMsgId operator*() const {
// auto id = _data->_ids[_index];
// Assert(IsServerMsgId(id)
// || (_data->_migrated != nullptr && IsServerMsgId(-id)));
// return IsServerMsgId(id)
// ? FullMsgId(_data->_history->channelId(), id)
// : FullMsgId(_data->_migrated->channelId(), -id);
// }
// iterator &operator--() {
// --_index;
// return *this;
// }
// iterator operator--(int) {
// auto result = *this;
// --*this;
// return result;
// }
// iterator &operator++() {
// ++_index;
// return *this;
// }
// iterator operator++(int) {
// auto result = *this;
// ++*this;
// return result;
// }
// iterator &operator+=(int offset) {
// _index += offset;
// return *this;
// }
// iterator operator+(int offset) const {
// auto result = *this;
// return result += offset;
// }
// bool operator==(iterator other) const {
// return (_data == other._data) && (_index == other._index);
// }
// bool operator!=(iterator other) const {
// return !(*this == other);
// }
// bool operator<(iterator other) const {
// return (_data < other._data)
// || (_data == other._data && _index < other._index);
// }
//
// private:
// friend class Data;
//
// iterator(not_null<const Data*> data, int index)
// : _data(data)
// , _index(index) {
// }
//
// not_null<const Data*> _data;
// int _index = 0;
//
// };
//
// iterator begin() const {
// return iterator(this, 0);
// }
// iterator end() const {
// iterator(this, _ids.size());
// }
// iterator cbegin() const {
// return begin();
// }
// iterator cend() const {
// return end();
// }
//
// private:
// friend class iterator;
// friend class SharedMediaSliceMerged;
//
// Data(
// not_null<History*> history,
// History *migrated,
// base::optional<int> historyCount = base::none,
// base::optional<int> migratedCount = base::none)
// : _history(history)
// , _migrated(migrated)
// , _historyCount(historyCount)
// , _migratedCount(migratedCount) {
// if (!_migrated) {
// _migratedCount = 0;
// }
// }
//
// not_null<History*> _history;
// History *_migrated = nullptr;
// std::vector<UniversalMsgId> _ids;
// base::optional<int> _historyCount;
// base::optional<int> _historySkippedBefore;
// base::optional<int> _historySkippedAfter;
// base::optional<int> _migratedCount;
// base::optional<int> _migratedSkippedBefore;
// base::optional<int> _migratedSkippedAfter;
//
// };
// base::Observable<Data> updated;
//
//private:
// bool amAroundMigrated() const;
// not_null<History*> aroundHistory() const;
// MsgId aroundId() const;
//
// void applyStoredResult(
// not_null<PeerData*> peer,
// Storage::SharedMediaResult &&result);
// bool containsAroundId() const;
// void clearAfterMove();
//
// Type _type = Type::kCount;
// not_null<History*> _history;
// History *_migrated = nullptr;
// UniversalMsgId _universalAroundId = 0;
// int _limitBefore = 0;
// int _limitAfter = 0;
// Data _data;
//
//};

View File

@ -44,6 +44,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace {
constexpr auto kPreloadCount = 4;
TextParseOptions _captionTextOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
@ -57,21 +59,24 @@ TextParseOptions _captionBotOptions = {
Qt::LayoutDirectionAuto, // dir
};
bool typeHasMediaOverview(MediaOverviewType type) {
switch (type) {
case OverviewPhotos:
case OverviewVideos:
case OverviewMusicFiles:
case OverviewFiles:
case OverviewVoiceFiles:
case OverviewLinks: return true;
default: break;
}
return false;
}
// Preload X message ids before and after current.
constexpr auto kIdsLimit = 32;
// Preload next messages if we went further from current than that.
constexpr auto kIdsPreloadAfter = 28;
} // namespace
struct MediaView::SharedMedia {
SharedMedia(SharedMediaViewer::Key key)
: key(key)
, slice(key, kIdsLimit, kIdsLimit) {
}
SharedMediaViewer::Key key;
SharedMediaViewer slice;
};
MediaView::MediaView() : TWidget(nullptr)
, _transparentBrush(style::transparentPlaceholderBrush())
, _animStarted(getms())
@ -111,9 +116,11 @@ MediaView::MediaView() : TWidget(nullptr)
documentUpdated(document);
}
});
subscribe(Auth().messageIdChanging, [this](std::pair<HistoryItem*, MsgId> update) {
subscribe(Auth().messageIdChanging, [this](std::pair<not_null<HistoryItem*>, MsgId> update) {
changingMsgId(update.first, update.second);
});
} else {
_sharedMedia = nullptr;
}
};
subscribe(Messenger::Instance().authSessionChanged(), [handleAuthSessionChange] {
@ -121,11 +128,6 @@ MediaView::MediaView() : TWidget(nullptr)
});
handleAuthSessionChange();
auto observeEvents = Notify::PeerUpdate::Flag::SharedMediaChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
mediaOverviewUpdated(update);
}));
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint);
moveToScreen();
setAttribute(Qt::WA_NoSystemBackground, true);
@ -190,54 +192,41 @@ void MediaView::moveToScreen() {
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
}
void MediaView::mediaOverviewUpdated(const Notify::PeerUpdate &update) {
if (isHidden() || (!_photo && !_doc)) return;
if (_photo && _overview == OverviewChatPhotos && _history && !_history->peer->isUser()) {
auto lastChatPhoto = computeLastOverviewChatPhoto();
if (_index < 0 && _photo == lastChatPhoto.photo && _photo == _additionalChatPhoto) {
auto firstOpened = _firstOpenedPeerPhoto;
showPhoto(_photo, lastChatPhoto.item);
_firstOpenedPeerPhoto = firstOpened;
void MediaView::handleSharedMediaUpdate(const SharedMediaSlice &update) {
if (isHidden() || (!_photo && !_doc) || !_sharedMedia) {
_index = _fullIndex = _fullCount = base::none;
return;
}
computeAdditionalChatPhoto(_history->peer, lastChatPhoto.photo);
}
//if (_photo && _overview == OverviewChatPhotos && _history && !_history->peer->isUser()) { // TODO chat
// auto lastChatPhoto = computeLastOverviewChatPhoto();
// if (_index < 0 && _photo == lastChatPhoto.photo && _photo == _additionalChatPhoto) {
// auto firstOpened = _firstOpenedPeerPhoto;
// showPhoto(_photo, lastChatPhoto.item);
// _firstOpenedPeerPhoto = firstOpened;
// return;
// }
// computeAdditionalChatPhoto(_history->peer, lastChatPhoto.photo);
//}
if (_history && (_history->peer == update.peer || (_migrated && _migrated->peer == update.peer)) && (update.mediaTypesMask & (1 << _overview)) && _msgid) {
_index = -1;
auto i = 0;
if (_msgmigrated) {
for_const (auto msgId, _migrated->overview(_overview)) {
if (msgId == _msgid) {
_index = i;
break;
}
++i;
}
} else {
for_const (auto msgId, _history->overview(_overview)) {
if (msgId == _msgid) {
_index = i;
break;
}
++i;
}
}
_sharedMediaData = update;
findCurrent();
updateControls();
preloadData(0);
} else 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);
}
//if (_user == update.peer && update.mediaTypesMask & (1 << OverviewCount)) {
// if (!_photo) return;
// _index = -1;
// for (int i = 0, l = _user->photos.size(); i < l; ++i) {
// if (_user->photos[i] == _photo) {
// _index = i;
// break;
// }
// }
// updateControls();
// preloadData(0);
//} // TODO user
}
bool MediaView::fileShown() const {
@ -285,17 +274,10 @@ void MediaView::documentUpdated(DocumentData *doc) {
}
}
void MediaView::changingMsgId(HistoryItem *row, MsgId newId) {
void MediaView::changingMsgId(not_null<HistoryItem*> row, MsgId newId) {
if (row->id == _msgid) {
_msgid = newId;
}
// Send a fake update.
if (!isHidden()) {
Notify::PeerUpdate update(row->history()->peer);
update.flags |= Notify::PeerUpdate::Flag::SharedMediaChanged;
update.mediaTypesMask |= (1 << _overview);
mediaOverviewUpdated(update);
validateSharedMedia();
}
}
@ -332,6 +314,17 @@ void MediaView::updateDocSize() {
}
}
void MediaView::refreshNavVisibility() {
if (_sharedMediaData) {
_leftNavVisible = _index && (*_index > 0);
_rightNavVisible = _index && (*_index + 1 < _sharedMediaData->size());
} else {
_leftNavVisible = false;
_rightNavVisible = false;
}
// TODO user
}
void MediaView::updateControls() {
if (_doc && fileBubbleShown()) {
if (_doc->loading()) {
@ -393,28 +386,7 @@ void MediaView::updateControls() {
_dateNav = myrtlrect(st::mediaviewTextLeft, height() - st::mediaviewTextTop, st::mediaviewFont->width(_dateText), st::mediaviewFont->height);
}
updateHeader();
if (_photo || (_history && _overview != OverviewCount)) {
_leftNavVisible = (_index > 0) || (_index == 0 && (
(!_msgmigrated && _history && _history->overview(_overview).size() < _history->overviewCount(_overview)) ||
(_msgmigrated && _migrated && _migrated->overview(_overview).size() < _migrated->overviewCount(_overview)) ||
(!_msgmigrated && _history && _migrated && (!_migrated->overview(_overview).isEmpty() || _migrated->overviewCount(_overview) > 0)))) ||
(_index < 0 && _photo == _additionalChatPhoto &&
((_history && _history->overviewCount(_overview) > 0) ||
(_migrated && _history->overviewLoaded(_overview) && _migrated->overviewCount(_overview) > 0))
);
_rightNavVisible = (_index >= 0) && (
(!_msgmigrated && _history && _index + 1 < _history->overview(_overview).size()) ||
(_msgmigrated && _migrated && _index + 1 < _migrated->overview(_overview).size()) ||
(_msgmigrated && _migrated && _history && (!_history->overview(_overview).isEmpty() || _history->overviewCount(_overview) > 0)) ||
(!_msgmigrated && _history && _index + 1 == _history->overview(_overview).size() && _additionalChatPhoto) ||
(_msgmigrated && _migrated && _index + 1 == _migrated->overview(_overview).size() && _history->overviewCount(_overview) == 0 && _additionalChatPhoto) ||
(!_history && _user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount)));
if (_msgmigrated && !_history->overviewLoaded(_overview)) {
_leftNavVisible = _rightNavVisible = false;
}
} else {
_leftNavVisible = _rightNavVisible = false;
}
refreshNavVisibility();
if (!_caption.isEmpty()) {
int32 skipw = qMax(_dateNav.left() + _dateNav.width(), _headerNav.left() + _headerNav.width());
@ -453,7 +425,10 @@ void MediaView::updateActions() {
_actions.push_back({ lang(lng_mediaview_delete), SLOT(onDelete()) });
}
_actions.push_back({ lang(lng_mediaview_save_as), SLOT(onSaveAs()) });
if (_history && typeHasMediaOverview(_overview)) {
if (auto overviewType =
sharedMediaType()
| SharedMediaOverviewType) {
_actions.push_back({ lang(_doc ? lng_mediaview_files_all : lng_mediaview_photos_all), SLOT(onOverview()) });
}
}
@ -687,6 +662,7 @@ void MediaView::updateMixerVideoVolume() const {
}
void MediaView::close() {
_sharedMedia = nullptr;
if (_menu) _menu->hideMenu(true);
Messenger::Instance().hideMediaView();
}
@ -1002,12 +978,13 @@ void MediaView::onDelete() {
void MediaView::onOverview() {
if (_menu) _menu->hideMenu(true);
if (!_history || !typeHasMediaOverview(_overview)) {
update();
return;
}
if (auto overviewType =
sharedMediaType()
| SharedMediaOverviewType) {
close();
if (_history->peer) App::main()->showMediaOverview(_history->peer, _overview);
SharedMediaShowOverview(*overviewType, _history);
}
}
void MediaView::onCopy() {
@ -1025,7 +1002,81 @@ void MediaView::onCopy() {
}
}
void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
base::optional<MediaView::SharedMediaType> MediaView::sharedMediaType() const {
using Type = SharedMediaType;
auto channelId = _msgmigrated ? NoChannel : _channel;
if (auto item = App::histItemById(channelId, _msgid)) {
if (_photo) {
if (item->toHistoryMessage()) {
return Type::Photo;
}
return Type::ChatPhoto;
} else if (_doc) {
if (_doc->isGifv()) {
return Type::GIF;
} else if (_doc->isVideo()) {
return Type::Video;
}
return Type::File;
}
}
return base::none;
}
base::optional<MediaView::SharedMediaKey> MediaView::sharedMediaKey() const {
auto keyForType = [this](SharedMediaType type) -> SharedMediaKey {
return { (_msgmigrated ? _migrated : _history)->peer->id, type, _msgid };
};
return
sharedMediaType()
| keyForType;
}
bool MediaView::validSharedMedia() const {
if (auto key = sharedMediaKey()) {
if (!_sharedMedia) {
return false;
}
auto countDistanceInData = [](const auto &a, const auto &b) {
return [&](const SharedMediaSlice &data) {
return data.distance(a, b);
};
};
auto distance = (key == _sharedMedia->key) ? 0 :
_sharedMediaData
| countDistanceInData(*key, _sharedMedia->key)
| base::abs;
if (distance) {
return (*distance < kIdsPreloadAfter);
}
}
return (_sharedMedia == nullptr);
}
void MediaView::validateSharedMedia() {
if (auto key = sharedMediaKey()) {
_sharedMedia = std::make_unique<SharedMedia>(*key);
subscribe(_sharedMedia->slice.updated, [this](const SharedMediaSlice &data) {
handleSharedMediaUpdate(data);
});
_sharedMedia->slice.start();
} else {
_sharedMedia = nullptr;
_sharedMediaData = base::none;
}
}
void MediaView::refreshSharedMedia() {
if (!validSharedMedia()) {
validateSharedMedia();
}
findCurrent();
updateControls();
preloadData(0);
}
void MediaView::showPhoto(not_null<PhotoData*> photo, HistoryItem *context) {
_history = context ? context->history().get() : nullptr;
_migrated = nullptr;
if (_history) {
@ -1052,23 +1103,20 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
}
if (!_animOpacities.isEmpty()) _animOpacities.clear();
_index = -1;
_msgid = context ? context->id : 0;
_msgmigrated = context ? (context->history() == _migrated) : false;
_channel = _history ? _history->channelId() : NoChannel;
_canForward = context ? context->canForward() : false;
_canDelete = context ? context->canDelete() : false;
_photo = photo;
validateSharedMedia();
if (_history) {
if (context && !context->toHistoryMessage()) {
_overview = OverviewChatPhotos;
if (!_history->peer->isUser()) {
computeAdditionalChatPhoto(_history->peer, computeLastOverviewChatPhoto().photo);
}
} else {
_overview = OverviewPhotos;
}
findCurrent();
}
displayPhoto(photo, context);
@ -1076,7 +1124,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
activateControls();
}
void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
void MediaView::showPhoto(not_null<PhotoData*> photo, PeerData *context) {
_history = _migrated = nullptr;
_additionalChatPhoto = nullptr;
_firstOpenedPeerPhoto = true;
@ -1096,23 +1144,23 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
_msgmigrated = false;
_channel = NoChannel;
_canForward = _canDelete = false;
_index = -1;
_photo = photo;
_overview = OverviewCount;
if (_user) {
if (_user->photos.isEmpty() && _user->photosCount < 0 && _user->photoId && _user->photoId != UnknownPeerPhotoId) {
_index = 0;
}
for (int i = 0, l = _user->photos.size(); i < l; ++i) {
if (_user->photos.at(i) == photo) {
_index = i;
break;
}
}
if (_user->photosCount < 0) {
loadBack();
}
validateSharedMedia();
if (_user) {
//if (_user->photos.isEmpty() && _user->photosCount < 0 && _user->photoId && _user->photoId != UnknownPeerPhotoId) {
// _fullIndex = 0;
//}
//for (int i = 0, l = _user->photos.size(); i < l; ++i) {
// if (_user->photos.at(i) == photo) {
// _fullIndex = i;
// break;
// }
//}
//if (_user->photosCount < 0) {
// loadBack();
//} // TODO user
} else if ((_history = App::historyLoaded(_peer))) {
if (_history->peer->migrateFrom()) {
_migrated = App::history(_history->peer->migrateFrom()->id);
@ -1129,20 +1177,20 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
}
computeAdditionalChatPhoto(_history->peer, lastChatPhoto.photo);
if (_additionalChatPhoto == _photo) {
_overview = OverviewChatPhotos;
findCurrent();
} else {
//if (_additionalChatPhoto == _photo) { // TODO chat
// _overview = OverviewChatPhotos;
// findCurrent();
//} else {
_additionalChatPhoto = nullptr;
_history = _migrated = nullptr;
}
//}
}
displayPhoto(photo, 0);
preloadData(0);
activateControls();
}
void MediaView::showDocument(DocumentData *doc, HistoryItem *context) {
void MediaView::showDocument(not_null<DocumentData*> document, HistoryItem *context) {
_photo = 0;
_history = context ? context->history().get() : nullptr;
_migrated = nullptr;
@ -1169,25 +1217,21 @@ void MediaView::showDocument(DocumentData *doc, HistoryItem *context) {
}
if (!_animOpacities.isEmpty()) _animOpacities.clear();
_index = -1;
_msgid = context ? context->id : 0;
_msgmigrated = context ? (context->history() == _migrated) : false;
_channel = _history ? _history->channelId() : NoChannel;
_canForward = context ? context->canForward() : false;
_canDelete = context ? context->canDelete() : false;
if (_history) {
_overview = doc->isGifv() ? OverviewGIFs : doc->isVideo() ? OverviewVideos : OverviewFiles;
findCurrent();
if (document->isVideo() || document->isRoundVideo()) {
_autoplayVideoDocument = document;
}
if (doc->isVideo() || doc->isRoundVideo()) {
_autoplayVideoDocument = doc;
}
displayDocument(doc, context);
displayDocument(document, context);
preloadData(0);
activateControls();
}
void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) {
void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
stopGif();
destroyThemePreview();
_doc = nullptr;
@ -1195,6 +1239,8 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) {
_photo = photo;
_radial.stop();
validateSharedMedia();
_photoRadialRect = QRect(QPoint((width() - st::radialSize.width()) / 2, (height() - st::radialSize.height()) / 2), st::radialSize);
_zoom = 0;
@ -1268,6 +1314,8 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty
_photo = nullptr;
_radial.stop();
validateSharedMedia();
if (_autoplayVideoDocument && _doc != _autoplayVideoDocument) {
_autoplayVideoDocument = nullptr;
}
@ -2160,51 +2208,14 @@ void MediaView::setZoomLevel(int newZoom) {
}
bool MediaView::moveToNext(int32 delta) {
if (_index < 0) {
if (delta == -1 && _photo == _additionalChatPhoto) {
auto lastChatPhoto = computeLastOverviewChatPhoto();
if (lastChatPhoto.item) {
if (lastChatPhoto.item->history() == _history) {
_index = _history->overview(_overview).size() - 1;
_msgmigrated = false;
} else {
_index = _migrated->overview(_overview).size() - 1;
_msgmigrated = true;
}
_msgid = lastChatPhoto.item->id;
_channel = _history ? _history->channelId() : NoChannel;
_canForward = lastChatPhoto.item->canForward();
_canDelete = lastChatPhoto.item->canDelete();
displayPhoto(lastChatPhoto.photo, lastChatPhoto.item);
preloadData(delta);
return true;
} else if (_history && (_history->overviewCount(OverviewChatPhotos) != 0 || (
_migrated && _migrated->overviewCount(OverviewChatPhotos) != 0))) {
loadBack();
return true;
}
}
if (!_index) {
return false;
}
if (_overview == OverviewCount && (_history || !_user)) {
auto newIndex = *_index + delta;
if (newIndex < 0 || newIndex >= _sharedMediaData->size()) {
return false;
}
if (_msgmigrated && !_history->overviewLoaded(_overview)) {
return true;
}
int32 newIndex = _index + delta;
if (_history && _overview != OverviewCount) {
bool newMigrated = _msgmigrated;
if (!newMigrated && newIndex < 0 && _migrated) {
newIndex += _migrated->overview(_overview).size();
newMigrated = true;
} else if (newMigrated && newIndex >= _migrated->overview(_overview).size()) {
newIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size());
newMigrated = false;
}
if (newIndex >= 0 && newIndex < (newMigrated ? _migrated : _history)->overview(_overview).size()) {
if (auto item = App::histItemById(newMigrated ? 0 : _channel, getMsgIdFromOverview(newMigrated ? _migrated : _history, newIndex))) {
if (auto item = App::histItemById(_history->channelId(), *(_sharedMediaData->begin() + newIndex))) {
_index = newIndex;
_msgid = item->id;
_msgmigrated = (item->history() == _migrated);
@ -2224,58 +2235,110 @@ bool MediaView::moveToNext(int32 delta) {
displayDocument(nullptr, item);
preloadData(delta);
}
return true;
}
} 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 false;
//if (_index < 0) { // TODO chat
// if (delta == -1 && _photo == _additionalChatPhoto) {
// auto lastChatPhoto = computeLastOverviewChatPhoto();
// if (lastChatPhoto.item) {
// if (lastChatPhoto.item->history() == _history) {
// _index = _history->overview(_overview).size() - 1;
// _msgmigrated = false;
// } else {
// _index = _migrated->overview(_overview).size() - 1;
// _msgmigrated = true;
// }
// _msgid = lastChatPhoto.item->id;
// _channel = _history ? _history->channelId() : NoChannel;
// _canForward = lastChatPhoto.item->canForward();
// _canDelete = lastChatPhoto.item->canDelete();
// displayPhoto(lastChatPhoto.photo, lastChatPhoto.item);
// preloadData(delta);
// return true;
// } else if (_history && (_history->overviewCount(OverviewChatPhotos) != 0 || (
// _migrated && _migrated->overviewCount(OverviewChatPhotos) != 0))) {
// loadBack();
// return true;
// }
// }
// return false;
//}
//if (_overview == OverviewCount && (_history || !_user)) {
// return false;
//}
//if (_msgmigrated && !_history->overviewLoaded(_overview)) {
// return true;
//}
//int32 newIndex = _index + delta;
//if (_history && _overview != OverviewCount) {
// bool newMigrated = _msgmigrated;
// if (!newMigrated && newIndex < 0 && _migrated) {
// newIndex += _migrated->overview(_overview).size();
// newMigrated = true;
// } else if (newMigrated && newIndex >= _migrated->overview(_overview).size()) {
// newIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size());
// newMigrated = false;
// }
// if (newIndex >= 0 && newIndex < (newMigrated ? _migrated : _history)->overview(_overview).size()) {
// if (auto item = App::histItemById(newMigrated ? 0 : _channel, getMsgIdFromOverview(newMigrated ? _migrated : _history, newIndex))) {
// _index = newIndex;
// _msgid = item->id;
// _msgmigrated = (item->history() == _migrated);
// _channel = _history ? _history->channelId() : NoChannel;
// _canForward = item->canForward();
// _canDelete = item->canDelete();
// stopGif();
// if (auto media = item->getMedia()) {
// switch (media->type()) {
// case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break;
// case MediaTypeFile:
// case MediaTypeVideo:
// case MediaTypeGif:
// case MediaTypeSticker: displayDocument(media->getDocument(), item); preloadData(delta); break;
// }
// } else {
// displayDocument(nullptr, item);
// preloadData(delta);
// }
// }
// } else if (!newMigrated && newIndex == _history->overview(_overview).size() && _additionalChatPhoto) {
// _index = -1;
// _msgid = 0;
// _msgmigrated = false;
// _canForward = false;
// _canDelete = false;
// displayPhoto(_additionalChatPhoto, 0);
// }
// if (delta < 0 && _index < MediaOverviewStartPerPage) {
// loadBack();
// }
//} else if (_user) {
// if (newIndex >= 0 && newIndex < _user->photos.size()) {
// _index = newIndex;
// displayPhoto(_user->photos[_index], 0);
// preloadData(delta);
// }
// if (delta > 0 && _index > _user->photos.size() - MediaOverviewStartPerPage) {
// loadBack();
// }
//}
return true;
}
void MediaView::preloadData(int32 delta) {
int indexInOverview = _index;
bool indexOfMigratedItem = _msgmigrated;
if (_index < 0) {
if (_overview != OverviewChatPhotos || !_history) return;
indexInOverview = _history->overview(OverviewChatPhotos).size();
indexOfMigratedItem = false;
if (!_index) {
return;
}
if (!_user && _overview == OverviewCount) return;
auto from = *_index + (delta ? delta : -1);
auto till = *_index + (delta ? delta * kPreloadCount : 1);
if (from > till) std::swap(from, till);
auto from = indexInOverview + (delta ? delta : -1);
auto to = indexInOverview + (delta ? delta * MediaOverviewPreloadCount : 1);
if (from > to) qSwap(from, to);
if (_history && _overview != OverviewCount) {
auto forgetIndex = indexInOverview - delta * 2;
auto forgetHistory = indexOfMigratedItem ? _migrated : _history;
if (_migrated) {
if (indexOfMigratedItem && forgetIndex >= _migrated->overview(_overview).size()) {
forgetHistory = _history;
forgetIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size());
} else if (!indexOfMigratedItem && forgetIndex < 0) {
forgetHistory = _migrated;
forgetIndex += _migrated->overview(_overview).size();
}
}
if (forgetIndex >= 0 && forgetIndex < forgetHistory->overview(_overview).size() && (forgetHistory != (indexOfMigratedItem ? _migrated : _history) || forgetIndex != indexInOverview)) {
if (auto item = App::histItemById(forgetHistory->channelId(), getMsgIdFromOverview(forgetHistory, forgetIndex))) {
auto forgetIndex = *_index - delta * 2;
if (forgetIndex >= 0 && forgetIndex < _sharedMediaData->size()) {
if (auto item = App::histItemById(_history->channelId(), *(_sharedMediaData->begin() + forgetIndex))) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->forget(); break;
@ -2288,20 +2351,9 @@ void MediaView::preloadData(int32 delta) {
}
}
for (int32 i = from; i <= to; ++i) {
History *previewHistory = indexOfMigratedItem ? _migrated : _history;
int32 previewIndex = i;
if (_migrated) {
if (indexOfMigratedItem && previewIndex >= _migrated->overview(_overview).size()) {
previewHistory = _history;
previewIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size());
} else if (!indexOfMigratedItem && previewIndex < 0) {
previewHistory = _migrated;
previewIndex += _migrated->overview(_overview).size();
}
}
if (previewIndex >= 0 && previewIndex < previewHistory->overview(_overview).size() && (previewHistory != (indexOfMigratedItem ? _migrated : _history) || previewIndex != indexInOverview)) {
if (auto item = App::histItemById(previewHistory->channelId(), getMsgIdFromOverview(previewHistory, previewIndex))) {
for (auto index = from; index != till; ++index) {
if (index >= 0 && index < _sharedMediaData->size()) {
if (auto item = App::histItemById(_history->channelId(), *(_sharedMediaData->begin() + index))) {
if (auto media = item->getMedia()) {
switch (media->type()) {
case MediaTypePhoto: static_cast<HistoryPhoto*>(media)->photo()->download(); break;
@ -2318,22 +2370,23 @@ void MediaView::preloadData(int32 delta) {
}
}
}
} else if (_user) {
for (int32 i = from; i <= to; ++i) {
if (i >= 0 && i < _user->photos.size() && i != indexInOverview) {
_user->photos[i]->thumb->load();
}
}
for (int32 i = from; i <= to; ++i) {
if (i >= 0 && i < _user->photos.size() && i != indexInOverview) {
_user->photos[i]->download();
}
}
int32 forgetIndex = indexInOverview - delta * 2;
if (forgetIndex >= 0 && forgetIndex < _user->photos.size() && forgetIndex != indexInOverview) {
_user->photos[forgetIndex]->forget();
}
}
//} else if (_user) {
// for (int32 i = from; i <= to; ++i) {
// if (i >= 0 && i < _user->photos.size() && i != indexInOverview) {
// _user->photos[i]->thumb->load();
// }
// }
// for (int32 i = from; i <= to; ++i) {
// if (i >= 0 && i < _user->photos.size() && i != indexInOverview) {
// _user->photos[i]->download();
// }
// }
// int32 forgetIndex = indexInOverview - delta * 2;
// if (forgetIndex >= 0 && forgetIndex < _user->photos.size() && forgetIndex != indexInOverview) {
// _user->photos[forgetIndex]->forget();
// }
//} // TODO user
}
void MediaView::mousePressEvent(QMouseEvent *e) {
@ -2763,69 +2816,81 @@ void MediaView::updateImage() {
}
void MediaView::findCurrent() {
auto i = 0;
if (_msgmigrated) {
for (auto msgId : _migrated->overview(_overview)) {
if (msgId == _msgid) {
_index = i;
break;
}
++i;
}
if (!_history->overviewCountLoaded(_overview)) {
loadBack();
} else if (_history->overviewLoaded(_overview) && !_migrated->overviewLoaded(_overview)) { // all loaded
if (!_migrated->overviewCountLoaded(_overview) || (_index < 2 && _migrated->overviewCount(_overview) > 0)) {
loadBack();
}
if (!_sharedMediaData) {
_index = _fullIndex = _fullCount = base::none;
return;
}
_index = _sharedMediaData->indexOf(_msgid);
if (_index && _sharedMediaData->skippedBefore()) {
_fullIndex = (*_index + *_sharedMediaData->skippedBefore());
} 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);
}
_fullIndex = base::none;
}
_fullCount = _sharedMediaData->fullCount();
//auto i = 0;
//if (_msgmigrated) {
// for (auto msgId : _migrated->overview(_overview)) {
// if (msgId == _msgid) {
// _index = i;
// break;
// }
// ++i;
// }
// if (!_history->overviewCountLoaded(_overview)) {
// loadBack();
// } else if (_history->overviewLoaded(_overview) && !_migrated->overviewLoaded(_overview)) { // all loaded
// if (!_migrated->overviewCountLoaded(_overview) || (_index < 2 && _migrated->overviewCount(_overview) > 0)) {
// loadBack();
// }
// }
//} else {
// for (auto msgId : _history->overview(_overview)) {
// if (msgId == _msgid) {
// _index = i;
// break;
// }
// ++i;
// }
// if (!_history->overviewLoaded(_overview)) {
// if (!_history->overviewCountLoaded(_overview) || (_index < 2 && _history->overviewCount(_overview) > 0) || (_index < 1 && _migrated && !_migrated->overviewLoaded(_overview))) {
// loadBack();
// }
// } else if (_index < 1 && _migrated && !_migrated->overviewLoaded(_overview)) {
// loadBack();
// }
// if (_migrated && !_migrated->overviewCountLoaded(_overview)) {
// App::main()->preloadOverview(_migrated->peer, _overview);
// }
//} // TODO user
}
void MediaView::loadBack() {
if (_loadRequest || (_overview == OverviewCount && !_user)) {
return;
}
if (_index < 0 && (!_additionalChatPhoto || _photo != _additionalChatPhoto || !_history)) {
return;
}
//if (_loadRequest || (_overview == OverviewCount && !_user)) {
// return;
//}
//if (_index < 0 && (!_additionalChatPhoto || _photo != _additionalChatPhoto || !_history)) {
// return;
//}
if (_history && _overview != OverviewCount && (!_history->overviewLoaded(_overview) || (_migrated && !_migrated->overviewLoaded(_overview)))) {
if (App::main()) {
if (_msgmigrated || (_migrated && _index == 0 && _history->overviewLoaded(_overview))) {
App::main()->loadMediaBack(_migrated->peer, _overview);
} else {
App::main()->loadMediaBack(_history->peer, _overview);
if (_migrated && _index == 0 && (_migrated->overviewCount(_overview) < 0 || _migrated->overview(_overview).isEmpty()) && !_migrated->overviewLoaded(_overview)) {
App::main()->loadMediaBack(_migrated->peer, _overview);
}
}
if (_msgmigrated && !_history->overviewCountLoaded(_overview)) {
App::main()->preloadOverview(_history->peer, _overview);
}
}
} else if (_user && _user->photosCount != 0) {
int32 limit = (_index < MediaOverviewStartPerPage && _user->photos.size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage;
_loadRequest = MTP::send(MTPphotos_GetUserPhotos(_user->inputUser, MTP_int(_user->photos.size()), MTP_long(0), MTP_int(limit)), rpcDone(&MediaView::userPhotosLoaded, _user));
}
//if (_history && _overview != OverviewCount && (!_history->overviewLoaded(_overview) || (_migrated && !_migrated->overviewLoaded(_overview)))) {
// if (App::main()) {
// if (_msgmigrated || (_migrated && _index == 0 && _history->overviewLoaded(_overview))) {
// App::main()->loadMediaBack(_migrated->peer, _overview);
// } else {
// App::main()->loadMediaBack(_history->peer, _overview);
// if (_migrated && _index == 0 && (_migrated->overviewCount(_overview) < 0 || _migrated->overview(_overview).isEmpty()) && !_migrated->overviewLoaded(_overview)) {
// App::main()->loadMediaBack(_migrated->peer, _overview);
// }
// }
// if (_msgmigrated && !_history->overviewCountLoaded(_overview)) {
// App::main()->preloadOverview(_history->peer, _overview);
// }
// }
//} else if (_user && _user->photosCount != 0) {
// int32 limit = (_index < MediaOverviewStartPerPage && _user->photos.size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage;
// _loadRequest = MTP::send(MTPphotos_GetUserPhotos(_user->inputUser, MTP_int(_user->photos.size()), MTP_long(0), MTP_int(limit)), rpcDone(&MediaView::userPhotosLoaded, _user));
//} // TODO user
}
MediaView::LastChatPhoto MediaView::computeLastOverviewChatPhoto() {
@ -2903,34 +2968,11 @@ void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mt
}
void MediaView::updateHeader() {
int32 index = _index, count = 0, addcount = (_migrated && _overview != OverviewCount) ? _migrated->overviewCount(_overview) : 0;
auto index = _fullIndex ? *_fullIndex : -1;
auto count = _fullCount ? *_fullCount : -1;
if (_history) {
if (_overview != OverviewCount) {
bool lastOverviewPhotoLoaded = (!_history->overview(_overview).isEmpty() || (
_migrated && _history->overviewCount(_overview) == 0 && !_migrated->overview(_overview).isEmpty()));
count = _history->overviewCount(_overview);
if (addcount >= 0 && count >= 0) {
count += addcount;
}
if (index >= 0 && (_msgmigrated ? (count >= 0 && addcount >= 0 && _history->overviewLoaded(_overview)) : (count >= 0))) {
if (_msgmigrated) {
index += addcount - _migrated->overview(_overview).size();
} else {
index += count - _history->overview(_overview).size();
}
if (_additionalChatPhoto && lastOverviewPhotoLoaded) {
++count;
}
} else if (index < 0 && _additionalChatPhoto && _photo == _additionalChatPhoto && lastOverviewPhotoLoaded) {
// Additional chat photo (not in the list => place it at the end of the list).
index = count;
++count;
} else {
count = 0; // unknown yet
}
}
} else if (_user) {
count = _user->photosCount ? _user->photosCount : _user->photos.size();
count = _user->photosCount ? _user->photosCount : _user->photos.size(); // TODO user
}
if (index >= 0 && index < count && count > 1) {
if (_doc) {
@ -2951,8 +2993,10 @@ void MediaView::updateHeader() {
_headerText = lang(lng_mediaview_single_photo);
}
}
_headerHasLink = _history && typeHasMediaOverview(_overview);
int32 hwidth = st::mediaviewThickFont->width(_headerText);
_headerHasLink = (
sharedMediaType()
| SharedMediaOverviewType) != base::none;
auto hwidth = st::mediaviewThickFont->width(_headerText);
if (hwidth > width() / 3) {
hwidth = width() / 3;
_headerText = st::mediaviewThickFont->elided(_headerText, hwidth, Qt::ElideMiddle);
@ -2966,13 +3010,13 @@ float64 MediaView::overLevel(OverState control) const {
}
MsgId MediaView::getMsgIdFromOverview(not_null<History*> history, int index) const {
auto &overview = history->overview(_overview);
if (index >= 0 && index < overview.size()) {
auto it = overview.begin();
for (auto i = 0; i != index; ++i) {
++it;
}
return *it;
}
//auto &overview = history->overview(_overview);
//if (index >= 0 && index < overview.size()) {
// auto it = overview.begin();
// for (auto i = 0; i != index; ++i) {
// ++it;
// }
// return *it;
//}
return 0;
}

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/dropdown_menu.h"
#include "ui/effects/radial_animation.h"
#include "history/history_shared_media.h"
namespace Media {
namespace Player {
@ -58,9 +59,9 @@ public:
void updateOver(QPoint mpos);
void showPhoto(PhotoData *photo, HistoryItem *context);
void showPhoto(PhotoData *photo, PeerData *context);
void showDocument(DocumentData *doc, HistoryItem *context);
void showPhoto(not_null<PhotoData*> photo, HistoryItem *context);
void showPhoto(not_null<PhotoData*> photo, PeerData *context);
void showDocument(not_null<DocumentData*> document, HistoryItem *context);
void moveToScreen();
bool moveToNext(int32 delta);
void preloadData(int32 delta);
@ -154,13 +155,25 @@ private:
void showSaveMsgFile();
void updateMixerVideoVolume() const;
struct SharedMedia;
using SharedMediaType = SharedMediaViewer::Type;
using SharedMediaKey = SharedMediaViewer::Key;
base::optional<SharedMediaType> sharedMediaType() const;
base::optional<SharedMediaKey> sharedMediaKey() const;
void validateSharedMedia();
bool validSharedMedia() const;
std::unique_ptr<SharedMedia> createSharedMedia() const;
void refreshSharedMedia();
void handleSharedMediaUpdate(const SharedMediaSlice &update);
void refreshNavVisibility();
void dropdownHidden();
void updateDocSize();
void updateControls();
void updateActions();
void displayPhoto(PhotoData *photo, HistoryItem *item);
void displayDocument(DocumentData *doc, HistoryItem *item);
void displayPhoto(not_null<PhotoData*> photo, HistoryItem *item);
void displayDocument(DocumentData *document, HistoryItem *item);
void displayFinished();
void findCurrent();
void loadBack();
@ -184,7 +197,7 @@ private:
void updateThemePreviewGeometry();
void documentUpdated(DocumentData *doc);
void changingMsgId(HistoryItem *row, MsgId newId);
void changingMsgId(not_null<HistoryItem*> row, MsgId newId);
// Radial animation interface.
float64 radialProgress() const;
@ -230,7 +243,9 @@ private:
PhotoData *_photo = nullptr;
DocumentData *_doc = nullptr;
MediaOverviewType _overview = OverviewCount;
std::unique_ptr<SharedMedia> _sharedMedia;
base::optional<SharedMediaSlice> _sharedMediaData;
QRect _closeNav, _closeNavIcon;
QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon;
QRect _headerNav, _nameNav, _dateNav;
@ -308,7 +323,9 @@ private:
PeerData *_from = nullptr;
Text _fromName;
int _index = -1; // index in photos or files array, -1 if just photo
base::optional<int> _index; // Index in current _sharedMedia data.
base::optional<int> _fullIndex; // Index in full shared media.
base::optional<int> _fullCount;
MsgId _msgid = 0; // msgId of current photo or file
bool _msgmigrated = false; // msgId is from _migrated history
ChannelId _channel = NoChannel;

View File

@ -96,7 +96,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, Ui::ScrollArea *scroll, P
subscribe(App::wnd()->dragFinished(), [this] {
dragActionUpdate(QCursor::pos());
});
subscribe(Auth().messageIdChanging, [this](std::pair<HistoryItem*, MsgId> update) {
subscribe(Auth().messageIdChanging, [this](std::pair<not_null<HistoryItem*>, MsgId> update) {
changingMsgId(update.first, update.second);
});

View File

@ -35,6 +35,10 @@ public:
SharedMediaQuery &&query,
base::lambda_once<void(SharedMediaResult&&)> &&callback);
base::Observable<SharedMediaSliceUpdate> &sharedMediaSliceUpdated();
base::Observable<SharedMediaRemoveOne> &sharedMediaOneRemoved();
base::Observable<SharedMediaRemoveAll> &sharedMediaAllRemoved();
private:
SharedMedia _sharedMedia;
@ -66,6 +70,17 @@ void Facade::Impl::query(
_sharedMedia.query(query, std::move(callback));
}
base::Observable<SharedMediaSliceUpdate> &Facade::Impl::sharedMediaSliceUpdated() {
return _sharedMedia.sliceUpdated;
}
base::Observable<SharedMediaRemoveOne> &Facade::Impl::sharedMediaOneRemoved() {
return _sharedMedia.oneRemoved;
}
base::Observable<SharedMediaRemoveAll> &Facade::Impl::sharedMediaAllRemoved() {
return _sharedMedia.allRemoved;
}
Facade::Facade() : _impl(std::make_unique<Impl>()) {
}
@ -96,6 +111,18 @@ void Facade::query(
_impl->query(std::move(query), std::move(callback));
}
base::Observable<SharedMediaSliceUpdate> &Facade::sharedMediaSliceUpdated() {
return _impl->sharedMediaSliceUpdated();
}
base::Observable<SharedMediaRemoveOne> &Facade::sharedMediaOneRemoved() {
return _impl->sharedMediaOneRemoved();
}
base::Observable<SharedMediaRemoveAll> &Facade::sharedMediaAllRemoved() {
return _impl->sharedMediaAllRemoved();
}
Facade::~Facade() = default;
} // namespace Storage

View File

@ -31,6 +31,7 @@ struct SharedMediaRemoveOne;
struct SharedMediaRemoveAll;
struct SharedMediaQuery;
struct SharedMediaResult;
struct SharedMediaSliceUpdate;
class Facade {
public:
@ -45,6 +46,10 @@ public:
SharedMediaQuery &&query,
base::lambda_once<void(SharedMediaResult&&)> &&callback);
base::Observable<SharedMediaSliceUpdate> &sharedMediaSliceUpdated();
base::Observable<SharedMediaRemoveOne> &sharedMediaOneRemoved();
base::Observable<SharedMediaRemoveAll> &sharedMediaAllRemoved();
~Facade();
private:

View File

@ -47,15 +47,16 @@ void SharedMedia::List::Slice::merge(
template <typename Range>
int SharedMedia::List::uniteAndAdd(
SliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MsgRange noSkipRange) {
auto uniteFromIndex = uniteFrom - _slices.begin();
auto was = uniteFrom->messages.size();
_slices.modify(uniteFrom, [&](Slice &slice) {
slice.merge(messages, noSkipRange);
});
auto result = uniteFrom->messages.size() - was;
auto firstToErase = uniteFrom + 1;
if (firstToErase != uniteTill) {
for (auto it = firstToErase; it != uniteTill; ++it) {
@ -64,21 +65,21 @@ int SharedMedia::List::uniteAndAdd(
});
}
_slices.erase(firstToErase, uniteTill);
uniteFrom = _slices.begin() + uniteFromIndex;
}
return result;
update.messages = &uniteFrom->messages;
update.range = uniteFrom->range;
return uniteFrom->messages.size() - was;
}
template <typename Range>
int SharedMedia::List::addRangeItemsAndCount(
SliceUpdate &update,
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count) {
Expects((noSkipRange.from < noSkipRange.till)
|| (noSkipRange.from == noSkipRange.till && messages.begin() == messages.end()));
if (count) {
_count = count;
}
if (noSkipRange.from == noSkipRange.till) {
return 0;
}
@ -92,7 +93,7 @@ int SharedMedia::List::addRangeItemsAndCount(
noSkipRange.till,
[](MsgId till, const Slice &slice) { return till < slice.range.from; });
if (uniteFrom < uniteTill) {
return uniteAndAdd(uniteFrom, uniteTill, messages, noSkipRange);
return uniteAndAdd(update, uniteFrom, uniteTill, messages, noSkipRange);
}
auto sliceMessages = base::flat_set<MsgId> {
@ -101,29 +102,39 @@ int SharedMedia::List::addRangeItemsAndCount(
auto slice = _slices.emplace(
std::move(sliceMessages),
noSkipRange);
update.messages = &slice->messages;
update.range = slice->range;
return slice->messages.size();
}
template <typename Range>
int SharedMedia::List::addRange(
void SharedMedia::List::addRange(
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count) {
auto result = addRangeItemsAndCount(messages, noSkipRange, count);
base::optional<int> count,
bool incrementCount) {
Expects(!count || !incrementCount);
auto wasCount = _count;
auto update = SliceUpdate();
auto result = addRangeItemsAndCount(update, messages, noSkipRange, count);
if (count) {
_count = count;
} else if (incrementCount && _count && result > 0) {
*_count += result;
}
if (_slices.size() == 1) {
if (_slices.front().range == MsgRange { 0, ServerMaxMsgId }) {
_count = _slices.front().messages.size();
}
}
return result;
update.count = _count;
sliceUpdated.notify(update, true);
}
void SharedMedia::List::addNew(MsgId messageId) {
auto range = { messageId };
auto added = addRange(range, { messageId, ServerMaxMsgId }, base::none);
if (added > 0 && _count) {
*_count += added;
}
addRange(range, { messageId, ServerMaxMsgId }, base::none, true);
}
void SharedMedia::List::addExisting(
@ -169,9 +180,9 @@ void SharedMedia::List::query(
auto slice = base::lower_bound(
_slices,
query.messageId,
query.key.messageId,
[](const Slice &slice, MsgId id) { return slice.range.till < id; });
if (slice != _slices.end() && slice->range.from <= query.messageId) {
if (slice != _slices.end() && slice->range.from <= query.key.messageId) {
result = queryFromSlice(query, *slice);
} else {
result.count = _count;
@ -189,18 +200,20 @@ SharedMediaResult SharedMedia::List::queryFromSlice(
const SharedMediaQuery &query,
const Slice &slice) {
auto result = SharedMediaResult {};
auto position = base::lower_bound(slice.messages, query.messageId);
auto haveBefore = position - slice.messages.begin();
auto haveEqualOrAfter = slice.messages.end() - position;
auto position = base::lower_bound(slice.messages, query.key.messageId);
auto haveBefore = int(position - slice.messages.begin());
auto haveEqualOrAfter = int(slice.messages.end() - position);
auto before = qMin(haveBefore, query.limitBefore);
auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1);
result.messageIds.reserve(before + equalOrAfter);
auto ids = std::vector<MsgId>();
ids.reserve(before + equalOrAfter);
for (
auto from = position - before, till = position + equalOrAfter;
from != till;
++from) {
result.messageIds.push_back(*from);
ids.push_back(*from);
}
result.messageIds.merge(ids.begin(), ids.end());
if (slice.range.from == 0) {
result.skippedBefore = haveBefore - before;
}
@ -212,21 +225,41 @@ SharedMediaResult SharedMedia::List::queryFromSlice(
if (!result.skippedBefore && result.skippedAfter) {
result.skippedBefore = *result.count
- *result.skippedAfter
- result.messageIds.size();
- int(result.messageIds.size());
} else if (!result.skippedAfter && result.skippedBefore) {
result.skippedAfter = *result.count
- *result.skippedBefore
- result.messageIds.size();
- int(result.messageIds.size());
}
}
return result;
}
void SharedMedia::add(SharedMediaAddNew &&query) {
auto peerIt = _lists.find(query.peerId);
if (peerIt == _lists.end()) {
peerIt = _lists.emplace(query.peerId, Lists {}).first;
std::map<PeerId, SharedMedia::Lists>::iterator
SharedMedia::enforceLists(PeerId peer) {
auto result = _lists.find(peer);
if (result != _lists.end()) {
return result;
}
result = _lists.emplace(peer, Lists {}).first;
for (auto index = 0; index != kSharedMediaTypeCount; ++index) {
auto &list = result->second[index];
auto type = static_cast<SharedMediaType>(index);
subscribe(list.sliceUpdated, [this, type, peer](const SliceUpdate &update) {
sliceUpdated.notify(SharedMediaSliceUpdate(
peer,
type,
update.messages,
update.range,
update.count), true);
});
}
return result;
}
void SharedMedia::add(SharedMediaAddNew &&query) {
auto peer = query.peerId;
auto peerIt = enforceLists(peer);
for (auto index = 0; index != kSharedMediaTypeCount; ++index) {
auto type = static_cast<SharedMediaType>(index);
if (query.types.test(type)) {
@ -236,10 +269,7 @@ void SharedMedia::add(SharedMediaAddNew &&query) {
}
void SharedMedia::add(SharedMediaAddExisting &&query) {
auto peerIt = _lists.find(query.peerId);
if (peerIt == _lists.end()) {
peerIt = _lists.emplace(query.peerId, Lists {}).first;
}
auto peerIt = enforceLists(query.peerId);
for (auto index = 0; index != kSharedMediaTypeCount; ++index) {
auto type = static_cast<SharedMediaType>(index);
if (query.types.test(type)) {
@ -250,10 +280,7 @@ void SharedMedia::add(SharedMediaAddExisting &&query) {
void SharedMedia::add(SharedMediaAddSlice &&query) {
Expects(IsValidSharedMediaType(query.type));
auto peerIt = _lists.find(query.peerId);
if (peerIt == _lists.end()) {
peerIt = _lists.emplace(query.peerId, Lists {}).first;
}
auto peerIt = enforceLists(query.peerId);
auto index = static_cast<int>(query.type);
peerIt->second[index].addSlice(std::move(query.messageIds), query.noSkipRange, query.count);
}
@ -265,6 +292,7 @@ void SharedMedia::remove(SharedMediaRemoveOne &&query) {
auto type = static_cast<SharedMediaType>(index);
if (query.types.test(type)) {
peerIt->second[index].removeOne(query.messageId);
oneRemoved.notify(query, true);
}
}
}
@ -276,16 +304,17 @@ void SharedMedia::remove(SharedMediaRemoveAll &&query) {
for (auto index = 0; index != kSharedMediaTypeCount; ++index) {
peerIt->second[index].removeAll();
}
allRemoved.notify(query, true);
}
}
void SharedMedia::query(
const SharedMediaQuery &query,
base::lambda_once<void(SharedMediaResult&&)> &&callback) {
Expects(IsValidSharedMediaType(query.type));
auto peerIt = _lists.find(query.peerId);
Expects(IsValidSharedMediaType(query.key.type));
auto peerIt = _lists.find(query.key.peerId);
if (peerIt != _lists.end()) {
auto index = static_cast<int>(query.type);
auto index = static_cast<int>(query.key.type);
peerIt->second[index].query(query, std::move(callback));
}
}

View File

@ -122,25 +122,44 @@ struct SharedMediaRemoveAll {
};
struct SharedMediaQuery {
SharedMediaQuery(
struct SharedMediaKey {
SharedMediaKey(
PeerId peerId,
SharedMediaType type,
MsgId messageId,
int limitBefore,
int limitAfter)
MsgId messageId)
: peerId(peerId)
, messageId(messageId)
, limitBefore(limitBefore)
, limitAfter(limitAfter)
, type(type) {
, type(type)
, messageId(messageId) {
}
bool operator==(const SharedMediaKey &other) const {
return (peerId == other.peerId)
&& (type == other.type)
&& (messageId == other.messageId);
}
bool operator!=(const SharedMediaKey &other) const {
return !(*this == other);
}
PeerId peerId = 0;
SharedMediaType type = SharedMediaType::kCount;
MsgId messageId = 0;
};
struct SharedMediaQuery {
SharedMediaQuery(
SharedMediaKey key,
int limitBefore,
int limitAfter)
: key(key)
, limitBefore(limitBefore)
, limitAfter(limitAfter) {
}
SharedMediaKey key;
int limitBefore = 0;
int limitAfter = 0;
SharedMediaType type = SharedMediaType::kCount;
};
@ -148,10 +167,31 @@ struct SharedMediaResult {
base::optional<int> count;
base::optional<int> skippedBefore;
base::optional<int> skippedAfter;
std::vector<MsgId> messageIds;
base::flat_set<MsgId> messageIds;
};
class SharedMedia {
struct SharedMediaSliceUpdate {
SharedMediaSliceUpdate(
PeerId peerId,
SharedMediaType type,
const base::flat_set<MsgId> *messages,
MsgRange range,
base::optional<int> count)
: peerId(peerId)
, type(type)
, messages(messages)
, range(range)
, count(count) {
}
PeerId peerId = 0;
SharedMediaType type = SharedMediaType::kCount;
const base::flat_set<MsgId> *messages = nullptr;
MsgRange range;
base::optional<int> count;
};
class SharedMedia : private base::Subscriber {
public:
using Type = SharedMediaType;
@ -164,6 +204,10 @@ public:
const SharedMediaQuery &query,
base::lambda_once<void(SharedMediaResult&&)> &&callback);
base::Observable<SharedMediaSliceUpdate> sliceUpdated;
base::Observable<SharedMediaRemoveOne> oneRemoved;
base::Observable<SharedMediaRemoveAll> allRemoved;
private:
class List {
public:
@ -179,6 +223,13 @@ private:
const SharedMediaQuery &query,
base::lambda_once<void(SharedMediaResult&&)> &&callback);
struct SliceUpdate {
const base::flat_set<MsgId> *messages = nullptr;
MsgRange range;
base::optional<int> count;
};
base::Observable<SliceUpdate> sliceUpdated;
private:
struct Slice {
Slice(base::flat_set<MsgId> &&messages, MsgRange range);
@ -197,20 +248,23 @@ private:
template <typename Range>
int uniteAndAdd(
SliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MsgRange noSkipRange);
template <typename Range>
int addRangeItemsAndCount(
SliceUpdate &update,
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count);
template <typename Range>
int addRange(
void addRange(
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count);
base::optional<int> count,
bool incrementCount = false);
SharedMediaResult queryFromSlice(
const SharedMediaQuery &query,
@ -220,8 +274,11 @@ private:
base::flat_set<Slice> _slices;
};
using SliceUpdate = List::SliceUpdate;
using Lists = std::array<List, kSharedMediaTypeCount>;
std::map<PeerId, Lists>::iterator enforceLists(PeerId peerId);
std::map<PeerId, Lists> _lists;
};

View File

@ -186,6 +186,8 @@
<(src_loc)/history/history_service.h
<(src_loc)/history/history_service_layout.cpp
<(src_loc)/history/history_service_layout.h
<(src_loc)/history/history_shared_media.cpp
<(src_loc)/history/history_shared_media.h
<(src_loc)/history/history_widget.cpp
<(src_loc)/history/history_widget.h
<(src_loc)/inline_bots/inline_bot_layout_internal.cpp