/* 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 "base/weak_unique_ptr.h" base::optional SharedMediaOverviewType( Storage::SharedMediaType type); void SharedMediaShowOverview( Storage::SharedMediaType type, not_null history); class SharedMediaSlice { public: using Type = Storage::SharedMediaType; using Key = Storage::SharedMediaKey; SharedMediaSlice(Key key); SharedMediaSlice( Key key, const base::flat_set &ids, MsgRange range, base::optional fullCount, base::optional skippedBefore, base::optional skippedAfter); const Key &key() const { return _key; } base::optional fullCount() const { return _fullCount; } base::optional skippedBefore() const { return _skippedBefore; } base::optional skippedAfter() const { return _skippedAfter; } base::optional indexOf(MsgId msgId) const; int size() const { return _ids.size(); } MsgId operator[](int index) const; base::optional distance(const Key &a, const Key &b) const; QString debug() const; private: Key _key; base::flat_set _ids; MsgRange _range; base::optional _fullCount; base::optional _skippedBefore; base::optional _skippedAfter; class SharedMediaSliceBuilder; }; rpl::producer SharedMediaViewer( SharedMediaSlice::Key key, int limitBefore, int limitAfter); class SharedMediaMergedSlice { public: using Type = Storage::SharedMediaType; using UniversalMsgId = MsgId; struct Key { Key( PeerId peerId, PeerId migratedPeerId, Type type, UniversalMsgId universalId) : peerId(peerId) , migratedPeerId(migratedPeerId) , type(type) , universalId(universalId) { } bool operator==(const Key &other) const { return (peerId == other.peerId) && (migratedPeerId == other.migratedPeerId) && (type == other.type) && (universalId == other.universalId); } PeerId peerId = 0; PeerId migratedPeerId = 0; Type type = Type::kCount; UniversalMsgId universalId = 0; }; SharedMediaMergedSlice(Key key); SharedMediaMergedSlice( Key key, SharedMediaSlice part, base::optional migrated); const Key &key() const { return _key; } base::optional fullCount() const; base::optional skippedBefore() const; base::optional skippedAfter() const; base::optional indexOf(FullMsgId fullId) const; int size() const; FullMsgId operator[](int index) const; base::optional distance(const Key &a, const Key &b) const; QString debug() const; static SharedMediaSlice::Key PartKey(const Key &key) { return { key.peerId, key.type, (key.universalId < 0) ? 1 : key.universalId }; } static SharedMediaSlice::Key MigratedKey(const Key &key) { return { key.migratedPeerId, key.type, (key.universalId <= 0) ? (-key.universalId) : (ServerMaxMsgId - 1) }; } private: static base::optional MigratedSlice(const Key &key) { return key.migratedPeerId ? base::make_optional(SharedMediaSlice(MigratedKey(key))) : base::none; } static bool IsFromSlice(const SharedMediaSlice &slice, FullMsgId fullId) { auto peer = slice.key().peerId; return peerIsChannel(peer) ? (peer == peerFromChannel(fullId.channel)) : !fullId.channel; } static FullMsgId ComputeId(PeerId peerId, MsgId msgId) { return FullMsgId( peerIsChannel(peerId) ? peerToBareInt(peerId) : 0, msgId); } static FullMsgId ComputeId(const SharedMediaSlice &slice, int index) { return ComputeId(slice.key().peerId, slice[index]); }; static FullMsgId ComputeId(const Key &key) { return (key.universalId > 0) ? ComputeId(key.peerId, key.universalId) : ComputeId(key.migratedPeerId, -key.universalId); } static base::optional Add( const base::optional &a, const base::optional &b) { return (a && b) ? base::make_optional(*a + *b) : base::none; } bool isFromPart(FullMsgId fullId) const { return IsFromSlice(_part, fullId); } bool isFromMigrated(FullMsgId fullId) const { return _migrated ? IsFromSlice(*_migrated, fullId) : false; } int migratedSize() const { return isolatedInPart() ? 0 : _migrated->size(); } bool isolatedInPart() const { return IsServerMsgId(_key.universalId) && (!_migrated || _part.skippedBefore() != 0); } bool isolatedInMigrated() const { return IsServerMsgId(-_key.universalId) && (_migrated->skippedAfter() != 0); } Key _key; SharedMediaSlice _part; base::optional _migrated; friend class SharedMediaMergedSliceBuilder; }; rpl::producer SharedMediaMergedViewer( SharedMediaMergedSlice::Key key, int limitBefore, int limitAfter); class SharedMediaWithLastSlice { public: using Type = Storage::SharedMediaType; // base::none in those mean CurrentPeerPhoto. using Value = base::variant>; using MessageId = SharedMediaMergedSlice::UniversalMsgId; using UniversalMsgId = base::variant< MessageId, not_null>; struct Key { Key( PeerId peerId, PeerId migratedPeerId, Type type, UniversalMsgId universalId) : peerId(peerId) , migratedPeerId(migratedPeerId) , type(type) , universalId(universalId) { Expects(base::get_if(&universalId) != nullptr || type == Type::ChatPhoto); } bool operator==(const Key &other) const { return (peerId == other.peerId) && (migratedPeerId == other.migratedPeerId) && (type == other.type) && (universalId == other.universalId); } PeerId peerId = 0; PeerId migratedPeerId = 0; Type type = Type::kCount; UniversalMsgId universalId; }; SharedMediaWithLastSlice(Key key); SharedMediaWithLastSlice( Key key, SharedMediaMergedSlice slice, base::optional ending); base::optional fullCount() const; base::optional skippedBefore() const; base::optional skippedAfter() const; base::optional indexOf(Value fullId) const; int size() const; Value operator[](int index) const; base::optional distance(const Key &a, const Key &b) const; QString debug() const; static SharedMediaMergedSlice::Key ViewerKey(const Key &key) { return { key.peerId, key.migratedPeerId, key.type, base::get_if(&key.universalId) ? (*base::get_if(&key.universalId)) : ServerMaxMsgId - 1 }; } static SharedMediaMergedSlice::Key EndingKey(const Key &key) { return { key.peerId, key.migratedPeerId, key.type, ServerMaxMsgId - 1 }; } private: static base::optional EndingSlice(const Key &key) { return base::get_if(&key.universalId) ? base::make_optional(SharedMediaMergedSlice(EndingKey(key))) : base::none; } static PhotoId LastPeerPhotoId(PeerId peerId); static base::optional IsLastIsolated( const SharedMediaMergedSlice &slice, const base::optional &ending, PhotoId lastPeerPhotoId); static base::optional LastFullMsgId( const SharedMediaMergedSlice &slice); static base::optional Add( const base::optional &a, const base::optional &b) { return (a && b) ? base::make_optional(*a + *b) : base::none; } static Value ComputeId(PeerId peerId, MsgId msgId) { return FullMsgId( peerIsChannel(peerId) ? peerToBareInt(peerId) : 0, msgId); } static Value ComputeId(const Key &key) { if (auto messageId = base::get_if(&key.universalId)) { return (*messageId > 0) ? ComputeId(key.peerId, *messageId) : ComputeId(key.migratedPeerId, -*messageId); } return *base::get_if>(&key.universalId); } bool isolatedInSlice() const { return (_slice.skippedAfter() != 0); } base::optional lastPhotoSkip() const { return _isolatedLastPhoto | [](bool isolated) { return isolated ? 1 : 0; }; } Key _key; SharedMediaMergedSlice _slice; base::optional _ending; PhotoId _lastPhotoId = 0; base::optional _isolatedLastPhoto; friend class SharedMediaWithLastSliceBuilder; }; rpl::producer SharedMediaWithLastViewer( SharedMediaWithLastSlice::Key key, int limitBefore, int limitAfter);