mirror of
https://github.com/vale981/tdesktop
synced 2025-03-11 21:46:41 -04:00

That way audio files and voice/video messages will play in context (one after another with ability to go to next or previous in player) almost always, no matter at what part of message history we are.
236 lines
6.4 KiB
C++
236 lines
6.4 KiB
C++
/*
|
|
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 Storage {
|
|
struct SparseIdsListResult;
|
|
struct SparseIdsSliceUpdate;
|
|
} // namespace Storage
|
|
|
|
enum class SparseIdsLoadDirection {
|
|
Around,
|
|
Before,
|
|
After,
|
|
};
|
|
|
|
class SparseIdsSlice {
|
|
public:
|
|
using Key = MsgId;
|
|
|
|
SparseIdsSlice() = default;
|
|
SparseIdsSlice(
|
|
const base::flat_set<MsgId> &ids,
|
|
MsgRange range,
|
|
base::optional<int> fullCount,
|
|
base::optional<int> skippedBefore,
|
|
base::optional<int> skippedAfter);
|
|
|
|
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;
|
|
int size() const { return _ids.size(); }
|
|
MsgId operator[](int index) const;
|
|
base::optional<int> distance(MsgId a, MsgId b) const;
|
|
base::optional<MsgId> nearest(MsgId msgId) const;
|
|
|
|
private:
|
|
base::flat_set<MsgId> _ids;
|
|
MsgRange _range;
|
|
base::optional<int> _fullCount;
|
|
base::optional<int> _skippedBefore;
|
|
base::optional<int> _skippedAfter;
|
|
|
|
};
|
|
|
|
class SparseIdsMergedSlice {
|
|
public:
|
|
using UniversalMsgId = MsgId;
|
|
struct Key {
|
|
Key(
|
|
PeerId peerId,
|
|
PeerId migratedPeerId,
|
|
UniversalMsgId universalId)
|
|
: peerId(peerId)
|
|
, migratedPeerId(migratedPeerId)
|
|
, universalId(universalId) {
|
|
}
|
|
|
|
bool operator==(const Key &other) const {
|
|
return (peerId == other.peerId)
|
|
&& (migratedPeerId == other.migratedPeerId)
|
|
&& (universalId == other.universalId);
|
|
}
|
|
bool operator!=(const Key &other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
PeerId peerId = 0;
|
|
PeerId migratedPeerId = 0;
|
|
UniversalMsgId universalId = 0;
|
|
|
|
};
|
|
|
|
SparseIdsMergedSlice(Key key);
|
|
SparseIdsMergedSlice(
|
|
Key key,
|
|
SparseIdsSlice part,
|
|
base::optional<SparseIdsSlice> migrated);
|
|
|
|
base::optional<int> fullCount() const;
|
|
base::optional<int> skippedBefore() const;
|
|
base::optional<int> skippedAfter() const;
|
|
base::optional<int> indexOf(FullMsgId fullId) const;
|
|
int size() const;
|
|
FullMsgId operator[](int index) const;
|
|
base::optional<int> distance(const Key &a, const Key &b) const;
|
|
base::optional<FullMsgId> nearest(UniversalMsgId id) const;
|
|
|
|
using SimpleViewerFunction = rpl::producer<SparseIdsSlice>(
|
|
PeerId peerId,
|
|
SparseIdsSlice::Key simpleKey,
|
|
int limitBefore,
|
|
int limitAfter);
|
|
static rpl::producer<SparseIdsMergedSlice> CreateViewer(
|
|
SparseIdsMergedSlice::Key key,
|
|
int limitBefore,
|
|
int limitAfter,
|
|
base::lambda<SimpleViewerFunction> simpleViewer);
|
|
|
|
private:
|
|
static SparseIdsSlice::Key PartKey(const Key &key) {
|
|
return (key.universalId < 0) ? 1 : key.universalId;
|
|
}
|
|
static SparseIdsSlice::Key MigratedKey(const Key &key) {
|
|
return (key.universalId < 0)
|
|
? (ServerMaxMsgId + key.universalId)
|
|
: (key.universalId > 0) ? (ServerMaxMsgId - 1) : 0;
|
|
}
|
|
static base::optional<SparseIdsSlice> MigratedSlice(const Key &key) {
|
|
return key.migratedPeerId
|
|
? base::make_optional(SparseIdsSlice())
|
|
: base::none;
|
|
}
|
|
|
|
static bool IsFromSlice(PeerId peerId, FullMsgId fullId) {
|
|
return peerIsChannel(peerId)
|
|
? (peerId == peerFromChannel(fullId.channel))
|
|
: !fullId.channel;
|
|
}
|
|
static FullMsgId ComputeId(PeerId peerId, MsgId msgId) {
|
|
return FullMsgId(
|
|
peerIsChannel(peerId) ? peerToBareInt(peerId) : 0,
|
|
msgId);
|
|
}
|
|
static FullMsgId ComputeId(const Key &key) {
|
|
return (key.universalId >= 0)
|
|
? ComputeId(key.peerId, key.universalId)
|
|
: ComputeId(key.migratedPeerId, ServerMaxMsgId + key.universalId);
|
|
}
|
|
static base::optional<int> Add(
|
|
const base::optional<int> &a,
|
|
const base::optional<int> &b) {
|
|
return (a && b) ? base::make_optional(*a + *b) : base::none;
|
|
}
|
|
|
|
bool isFromPart(FullMsgId fullId) const {
|
|
return IsFromSlice(_key.peerId, fullId);
|
|
}
|
|
bool isFromMigrated(FullMsgId fullId) const {
|
|
return _migrated
|
|
? IsFromSlice(_key.migratedPeerId, 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(ServerMaxMsgId + _key.universalId)
|
|
&& (_migrated->skippedAfter() != 0);
|
|
}
|
|
|
|
Key _key;
|
|
SparseIdsSlice _part;
|
|
base::optional<SparseIdsSlice> _migrated;
|
|
|
|
};
|
|
|
|
class SparseIdsSliceBuilder {
|
|
public:
|
|
using Key = SparseIdsSlice::Key;
|
|
|
|
SparseIdsSliceBuilder(Key key, int limitBefore, int limitAfter);
|
|
|
|
bool applyInitial(const Storage::SparseIdsListResult &result);
|
|
bool applyUpdate(const Storage::SparseIdsSliceUpdate &update);
|
|
bool removeOne(MsgId messageId);
|
|
bool removeAll();
|
|
|
|
void checkInsufficient();
|
|
struct AroundData {
|
|
MsgId aroundId = 0;
|
|
SparseIdsLoadDirection direction
|
|
= SparseIdsLoadDirection::Around;
|
|
|
|
inline bool operator<(const AroundData &other) const {
|
|
return (aroundId < other.aroundId)
|
|
|| ((aroundId == other.aroundId)
|
|
&& (direction < other.direction));
|
|
}
|
|
};
|
|
auto insufficientAround() const {
|
|
return _insufficientAround.events();
|
|
}
|
|
|
|
SparseIdsSlice snapshot() const;
|
|
|
|
private:
|
|
enum class RequestDirection {
|
|
Before,
|
|
After,
|
|
};
|
|
void requestMessages(RequestDirection direction);
|
|
void requestMessagesCount();
|
|
void fillSkippedAndSliceToLimits();
|
|
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;
|
|
base::flat_set<MsgId> _ids;
|
|
MsgRange _range;
|
|
base::optional<int> _fullCount;
|
|
base::optional<int> _skippedBefore;
|
|
base::optional<int> _skippedAfter;
|
|
int _limitBefore = 0;
|
|
int _limitAfter = 0;
|
|
|
|
rpl::event_stream<AroundData> _insufficientAround;
|
|
|
|
};
|