mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Add short-polling of stories.
This commit is contained in:
parent
12fe0a836a
commit
5ccb97668c
14 changed files with 308 additions and 52 deletions
|
@ -38,6 +38,8 @@ constexpr auto kSavedPerPage = 100;
|
||||||
constexpr auto kMaxPreloadSources = 10;
|
constexpr auto kMaxPreloadSources = 10;
|
||||||
constexpr auto kStillPreloadFromFirst = 3;
|
constexpr auto kStillPreloadFromFirst = 3;
|
||||||
constexpr auto kMaxSegmentsCount = 180;
|
constexpr auto kMaxSegmentsCount = 180;
|
||||||
|
constexpr auto kPollingIntervalChat = 5 * TimeId(60);
|
||||||
|
constexpr auto kPollingIntervalViewer = 1 * TimeId(60);
|
||||||
|
|
||||||
using UpdateFlag = StoryUpdate::Flag;
|
using UpdateFlag = StoryUpdate::Flag;
|
||||||
|
|
||||||
|
@ -98,10 +100,12 @@ Stories::Stories(not_null<Session*> owner)
|
||||||
: _owner(owner)
|
: _owner(owner)
|
||||||
, _expireTimer([=] { processExpired(); })
|
, _expireTimer([=] { processExpired(); })
|
||||||
, _markReadTimer([=] { sendMarkAsReadRequests(); })
|
, _markReadTimer([=] { sendMarkAsReadRequests(); })
|
||||||
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); }) {
|
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); })
|
||||||
|
, _pollingTimer([=] { sendPollingRequests(); }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Stories::~Stories() {
|
Stories::~Stories() {
|
||||||
|
Expects(_pollingSettings.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
Session &Stories::owner() const {
|
Session &Stories::owner() const {
|
||||||
|
@ -348,7 +352,7 @@ Story *Stories::parseAndApply(
|
||||||
const auto result = i->second.get();
|
const auto result = i->second.get();
|
||||||
const auto pinned = result->pinned();
|
const auto pinned = result->pinned();
|
||||||
const auto mediaChanged = (result->media() != *media);
|
const auto mediaChanged = (result->media() != *media);
|
||||||
if (result->applyChanges(*media, data)) {
|
if (result->applyChanges(*media, data, now)) {
|
||||||
if (result->pinned() != pinned) {
|
if (result->pinned() != pinned) {
|
||||||
savedStateUpdated(result);
|
savedStateUpdated(result);
|
||||||
}
|
}
|
||||||
|
@ -358,6 +362,11 @@ Story *Stories::parseAndApply(
|
||||||
if (const auto item = lookupItem(result)) {
|
if (const auto item = lookupItem(result)) {
|
||||||
item->applyChanges(result);
|
item->applyChanges(result);
|
||||||
}
|
}
|
||||||
|
_owner->refreshStoryItemViews(fullId);
|
||||||
|
}
|
||||||
|
const auto j = _pollingSettings.find(result);
|
||||||
|
if (j != end(_pollingSettings)) {
|
||||||
|
maybeSchedulePolling(result, j->second, now);
|
||||||
}
|
}
|
||||||
if (mediaChanged) {
|
if (mediaChanged) {
|
||||||
_preloaded.remove(fullId);
|
_preloaded.remove(fullId);
|
||||||
|
@ -378,7 +387,7 @@ Story *Stories::parseAndApply(
|
||||||
StoryMedia{ *media },
|
StoryMedia{ *media },
|
||||||
data.vdate().v,
|
data.vdate().v,
|
||||||
data.vexpire_date().v)).first->second.get();
|
data.vexpire_date().v)).first->second.get();
|
||||||
result->applyChanges(*media, data);
|
result->applyChanges(*media, data, now);
|
||||||
if (result->pinned()) {
|
if (result->pinned()) {
|
||||||
savedStateUpdated(result);
|
savedStateUpdated(result);
|
||||||
}
|
}
|
||||||
|
@ -656,6 +665,7 @@ void Stories::applyDeleted(FullStoryId id) {
|
||||||
preloadFinished(id);
|
preloadFinished(id);
|
||||||
}
|
}
|
||||||
_owner->refreshStoryItemViews(id);
|
_owner->refreshStoryItemViews(id);
|
||||||
|
Assert(!_pollingSettings.contains(story.get()));
|
||||||
if (i->second.empty()) {
|
if (i->second.empty()) {
|
||||||
_stories.erase(i);
|
_stories.erase(i);
|
||||||
}
|
}
|
||||||
|
@ -818,13 +828,15 @@ base::expected<not_null<Story*>, NoStory> Stories::lookup(
|
||||||
_deleted.contains(id) ? NoStory::Deleted : NoStory::Unknown);
|
_deleted.contains(id) ? NoStory::Deleted : NoStory::Unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::resolve(FullStoryId id, Fn<void()> done) {
|
void Stories::resolve(FullStoryId id, Fn<void()> done, bool force) {
|
||||||
const auto already = lookup(id);
|
if (!force) {
|
||||||
if (already.has_value() || already.error() != NoStory::Unknown) {
|
const auto already = lookup(id);
|
||||||
if (done) {
|
if (already.has_value() || already.error() != NoStory::Unknown) {
|
||||||
done();
|
if (done) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (const auto i = _resolveSent.find(id.peer); i != end(_resolveSent)) {
|
if (const auto i = _resolveSent.find(id.peer); i != end(_resolveSent)) {
|
||||||
if (const auto j = i->second.find(id.story); j != end(i->second)) {
|
if (const auto j = i->second.find(id.story); j != end(i->second)) {
|
||||||
|
@ -1493,6 +1505,84 @@ bool Stories::isUnread(not_null<Story*> story) {
|
||||||
return (story->id() > readTill);
|
return (story->id() > readTill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Stories::registerPolling(not_null<Story*> story, Polling polling) {
|
||||||
|
auto &settings = _pollingSettings[story];
|
||||||
|
switch (polling) {
|
||||||
|
case Polling::Chat: ++settings.chat; break;
|
||||||
|
case Polling::Viewer: ++settings.viewer; break;
|
||||||
|
}
|
||||||
|
maybeSchedulePolling(story, settings, base::unixtime::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::unregisterPolling(not_null<Story*> story, Polling polling) {
|
||||||
|
const auto i = _pollingSettings.find(story);
|
||||||
|
Assert(i != end(_pollingSettings));
|
||||||
|
|
||||||
|
switch (polling) {
|
||||||
|
case Polling::Chat:
|
||||||
|
Assert(i->second.chat > 0);
|
||||||
|
--i->second.chat;
|
||||||
|
break;
|
||||||
|
case Polling::Viewer:
|
||||||
|
Assert(i->second.viewer > 0);
|
||||||
|
--i->second.viewer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!i->second.chat && !i->second.viewer) {
|
||||||
|
_pollingSettings.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Stories::registerPolling(FullStoryId id, Polling polling) {
|
||||||
|
if (const auto maybeStory = lookup(id)) {
|
||||||
|
registerPolling(*maybeStory, polling);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::unregisterPolling(FullStoryId id, Polling polling) {
|
||||||
|
const auto maybeStory = lookup(id);
|
||||||
|
Assert(maybeStory.has_value());
|
||||||
|
unregisterPolling(*maybeStory, polling);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Stories::pollingInterval(const PollingSettings &settings) const {
|
||||||
|
return settings.viewer ? kPollingIntervalViewer : kPollingIntervalChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::maybeSchedulePolling(
|
||||||
|
not_null<Story*> story,
|
||||||
|
const PollingSettings &settings,
|
||||||
|
TimeId now) {
|
||||||
|
const auto last = story->lastUpdateTime();
|
||||||
|
const auto next = last + pollingInterval(settings);
|
||||||
|
const auto left = std::max(next - now, 0) * crl::time(1000) + 1;
|
||||||
|
if (!_pollingTimer.isActive() || _pollingTimer.remainingTime() > left) {
|
||||||
|
_pollingTimer.callOnce(left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::sendPollingRequests() {
|
||||||
|
auto min = 0;
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
for (const auto &[story, settings] : _pollingSettings) {
|
||||||
|
const auto last = story->lastUpdateTime();
|
||||||
|
const auto next = last + pollingInterval(settings);
|
||||||
|
if (now >= next) {
|
||||||
|
resolve(story->fullId(), nullptr, true);
|
||||||
|
} else {
|
||||||
|
const auto left = (next - now) * crl::time(1000) + 1;
|
||||||
|
if (!min || left < min) {
|
||||||
|
min = left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min > 0) {
|
||||||
|
_pollingTimer.callOnce(min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Stories::updateUserStoriesState(not_null<PeerData*> peer) {
|
void Stories::updateUserStoriesState(not_null<PeerData*> peer) {
|
||||||
const auto till = _readTill.find(peer->id);
|
const auto till = _readTill.find(peer->id);
|
||||||
const auto readTill = (till != end(_readTill)) ? till->second : 0;
|
const auto readTill = (till != end(_readTill)) ? till->second : 0;
|
||||||
|
|
|
@ -153,7 +153,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
|
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
|
||||||
FullStoryId id) const;
|
FullStoryId id) const;
|
||||||
void resolve(FullStoryId id, Fn<void()> done);
|
void resolve(FullStoryId id, Fn<void()> done, bool force = false);
|
||||||
[[nodiscard]] std::shared_ptr<HistoryItem> resolveItem(FullStoryId id);
|
[[nodiscard]] std::shared_ptr<HistoryItem> resolveItem(FullStoryId id);
|
||||||
[[nodiscard]] std::shared_ptr<HistoryItem> resolveItem(
|
[[nodiscard]] std::shared_ptr<HistoryItem> resolveItem(
|
||||||
not_null<Story*> story);
|
not_null<Story*> story);
|
||||||
|
@ -209,6 +209,16 @@ public:
|
||||||
StoryId storyMaxId);
|
StoryId storyMaxId);
|
||||||
[[nodiscard]] bool isUnread(not_null<Story*> story);
|
[[nodiscard]] bool isUnread(not_null<Story*> story);
|
||||||
|
|
||||||
|
enum class Polling {
|
||||||
|
Chat,
|
||||||
|
Viewer,
|
||||||
|
};
|
||||||
|
void registerPolling(not_null<Story*> story, Polling polling);
|
||||||
|
void unregisterPolling(not_null<Story*> story, Polling polling);
|
||||||
|
|
||||||
|
bool registerPolling(FullStoryId id, Polling polling);
|
||||||
|
void unregisterPolling(FullStoryId id, Polling polling);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Saved {
|
struct Saved {
|
||||||
StoriesIds ids;
|
StoriesIds ids;
|
||||||
|
@ -217,6 +227,10 @@ private:
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
mtpRequestId requestId = 0;
|
mtpRequestId requestId = 0;
|
||||||
};
|
};
|
||||||
|
struct PollingSettings {
|
||||||
|
int chat = 0;
|
||||||
|
int viewer = 0;
|
||||||
|
};
|
||||||
|
|
||||||
void parseAndApply(const MTPUserStories &stories);
|
void parseAndApply(const MTPUserStories &stories);
|
||||||
[[nodiscard]] Story *parseAndApply(
|
[[nodiscard]] Story *parseAndApply(
|
||||||
|
@ -265,6 +279,14 @@ private:
|
||||||
void startPreloading(not_null<Story*> story);
|
void startPreloading(not_null<Story*> story);
|
||||||
void preloadFinished(FullStoryId id, bool markAsPreloaded = false);
|
void preloadFinished(FullStoryId id, bool markAsPreloaded = false);
|
||||||
|
|
||||||
|
[[nodiscard]] int pollingInterval(
|
||||||
|
const PollingSettings &settings) const;
|
||||||
|
void maybeSchedulePolling(
|
||||||
|
not_null<Story*> story,
|
||||||
|
const PollingSettings &settings,
|
||||||
|
TimeId now);
|
||||||
|
void sendPollingRequests();
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
std::unordered_map<
|
std::unordered_map<
|
||||||
PeerId,
|
PeerId,
|
||||||
|
@ -336,6 +358,9 @@ private:
|
||||||
mtpRequestId _readTillsRequestId = 0;
|
mtpRequestId _readTillsRequestId = 0;
|
||||||
bool _readTillReceived = false;
|
bool _readTillReceived = false;
|
||||||
|
|
||||||
|
base::flat_map<not_null<Story*>, PollingSettings> _pollingSettings;
|
||||||
|
base::Timer _pollingTimer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -371,7 +371,12 @@ void Story::applyViewsSlice(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
|
bool Story::applyChanges(
|
||||||
|
StoryMedia media,
|
||||||
|
const MTPDstoryItem &data,
|
||||||
|
TimeId now) {
|
||||||
|
_lastUpdateTime = now;
|
||||||
|
|
||||||
const auto pinned = data.is_pinned();
|
const auto pinned = data.is_pinned();
|
||||||
const auto edited = data.is_edited();
|
const auto edited = data.is_edited();
|
||||||
const auto isPublic = data.is_public();
|
const auto isPublic = data.is_public();
|
||||||
|
@ -424,6 +429,10 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeId Story::lastUpdateTime() const {
|
||||||
|
return _lastUpdateTime;
|
||||||
|
}
|
||||||
|
|
||||||
StoryPreload::StoryPreload(not_null<Story*> story, Fn<void()> done)
|
StoryPreload::StoryPreload(not_null<Story*> story, Fn<void()> done)
|
||||||
: _story(story)
|
: _story(story)
|
||||||
, _done(std::move(done)) {
|
, _done(std::move(done)) {
|
||||||
|
|
|
@ -113,7 +113,11 @@ public:
|
||||||
const std::vector<StoryView> &slice,
|
const std::vector<StoryView> &slice,
|
||||||
int total);
|
int total);
|
||||||
|
|
||||||
bool applyChanges(StoryMedia media, const MTPDstoryItem &data);
|
bool applyChanges(
|
||||||
|
StoryMedia media,
|
||||||
|
const MTPDstoryItem &data,
|
||||||
|
TimeId now);
|
||||||
|
[[nodiscard]] TimeId lastUpdateTime() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const StoryId _id = 0;
|
const StoryId _id = 0;
|
||||||
|
@ -125,6 +129,7 @@ private:
|
||||||
int _views = 0;
|
int _views = 0;
|
||||||
const TimeId _date = 0;
|
const TimeId _date = 0;
|
||||||
const TimeId _expires = 0;
|
const TimeId _expires = 0;
|
||||||
|
TimeId _lastUpdateTime = 0;
|
||||||
bool _pinned : 1 = false;
|
bool _pinned : 1 = false;
|
||||||
bool _isPublic : 1 = false;
|
bool _isPublic : 1 = false;
|
||||||
bool _closeFriends : 1 = false;
|
bool _closeFriends : 1 = false;
|
||||||
|
|
|
@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
#include "ui/effects/spoiler_mess.h"
|
#include "ui/effects/spoiler_mess.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/data_streaming.h"
|
#include "data/data_streaming.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_file_click_handler.h"
|
#include "data/data_file_click_handler.h"
|
||||||
|
@ -134,6 +135,7 @@ Gif::~Gif() {
|
||||||
_parent->checkHeavyPart();
|
_parent->checkHeavyPart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
togglePollingStory(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Gif::CanPlayInline(not_null<DocumentData*> document) {
|
bool Gif::CanPlayInline(not_null<DocumentData*> document) {
|
||||||
|
@ -1432,6 +1434,22 @@ void Gif::dataMediaCreated() const {
|
||||||
_dataMedia->videoThumbnailWanted(_realParent->fullId());
|
_dataMedia->videoThumbnailWanted(_realParent->fullId());
|
||||||
}
|
}
|
||||||
history()->owner().registerHeavyViewPart(_parent);
|
history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
togglePollingStory(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gif::togglePollingStory(bool enabled) const {
|
||||||
|
if (!_story || _pollingStory == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto polling = Data::Stories::Polling::Chat;
|
||||||
|
const auto media = _parent->data()->media();
|
||||||
|
const auto id = media ? media->storyId() : FullStoryId();
|
||||||
|
if (!enabled) {
|
||||||
|
_data->owner().stories().unregisterPolling(id, polling);
|
||||||
|
} else if (!_data->owner().stories().registerPolling(id, polling)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pollingStory = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Gif::uploading() const {
|
bool Gif::uploading() const {
|
||||||
|
@ -1686,6 +1704,7 @@ void Gif::unloadHeavyPart() {
|
||||||
_thumbCache = QImage();
|
_thumbCache = QImage();
|
||||||
_videoThumbnailFrame = nullptr;
|
_videoThumbnailFrame = nullptr;
|
||||||
_caption.unloadPersistentAnimation();
|
_caption.unloadPersistentAnimation();
|
||||||
|
togglePollingStory(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gif::refreshParentId(not_null<HistoryItem*> realParent) {
|
void Gif::refreshParentId(not_null<HistoryItem*> realParent) {
|
||||||
|
@ -1815,6 +1834,7 @@ void Gif::setStreamed(std::unique_ptr<Streamed> value) {
|
||||||
_streamed = std::move(value);
|
_streamed = std::move(value);
|
||||||
if (set) {
|
if (set) {
|
||||||
history()->owner().registerHeavyViewPart(_parent);
|
history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
togglePollingStory(true);
|
||||||
} else if (removed) {
|
} else if (removed) {
|
||||||
_parent->checkHeavyPart();
|
_parent->checkHeavyPart();
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,6 +208,8 @@ private:
|
||||||
StateRequest request,
|
StateRequest request,
|
||||||
QPoint position) const;
|
QPoint position) const;
|
||||||
|
|
||||||
|
void togglePollingStory(bool enabled) const;
|
||||||
|
|
||||||
const not_null<DocumentData*> _data;
|
const not_null<DocumentData*> _data;
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
std::unique_ptr<Streamed> _streamed;
|
std::unique_ptr<Streamed> _streamed;
|
||||||
|
@ -222,6 +224,7 @@ private:
|
||||||
mutable bool _thumbCacheBlurred : 1 = false;
|
mutable bool _thumbCacheBlurred : 1 = false;
|
||||||
mutable bool _thumbIsEllipse : 1 = false;
|
mutable bool _thumbIsEllipse : 1 = false;
|
||||||
mutable bool _story : 1 = false;
|
mutable bool _story : 1 = false;
|
||||||
|
mutable bool _pollingStory : 1 = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/power_saving.h"
|
#include "ui/power_saving.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/data_streaming.h"
|
#include "data/data_streaming.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_photo_media.h"
|
#include "data/data_photo_media.h"
|
||||||
|
@ -101,6 +102,7 @@ Photo::~Photo() {
|
||||||
_parent->checkHeavyPart();
|
_parent->checkHeavyPart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
togglePollingStory(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::create(FullMsgId contextId, PeerData *chat) {
|
void Photo::create(FullMsgId contextId, PeerData *chat) {
|
||||||
|
@ -145,6 +147,7 @@ void Photo::dataMediaCreated() const {
|
||||||
_dataMedia->wanted(PhotoSize::Small, _realParent->fullId());
|
_dataMedia->wanted(PhotoSize::Small, _realParent->fullId());
|
||||||
}
|
}
|
||||||
history()->owner().registerHeavyViewPart(_parent);
|
history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
togglePollingStory(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Photo::hasHeavyPart() const {
|
bool Photo::hasHeavyPart() const {
|
||||||
|
@ -160,6 +163,23 @@ void Photo::unloadHeavyPart() {
|
||||||
}
|
}
|
||||||
_imageCache = QImage();
|
_imageCache = QImage();
|
||||||
_caption.unloadPersistentAnimation();
|
_caption.unloadPersistentAnimation();
|
||||||
|
togglePollingStory(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Photo::togglePollingStory(bool enabled) const {
|
||||||
|
const auto pollingStory = (enabled ? 1 : 0);
|
||||||
|
if (!_story || _pollingStory == pollingStory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto polling = Data::Stories::Polling::Chat;
|
||||||
|
const auto media = _parent->data()->media();
|
||||||
|
const auto id = media ? media->storyId() : FullStoryId();
|
||||||
|
if (!enabled) {
|
||||||
|
_data->owner().stories().unregisterPolling(id, polling);
|
||||||
|
} else if (!_data->owner().stories().registerPolling(id, polling)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pollingStory = pollingStory;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Photo::countOptimalSize() {
|
QSize Photo::countOptimalSize() {
|
||||||
|
@ -926,6 +946,7 @@ void Photo::setStreamed(std::unique_ptr<Streamed> value) {
|
||||||
_streamed = std::move(value);
|
_streamed = std::move(value);
|
||||||
if (set) {
|
if (set) {
|
||||||
history()->owner().registerHeavyViewPart(_parent);
|
history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
togglePollingStory(true);
|
||||||
} else if (removed) {
|
} else if (removed) {
|
||||||
_parent->checkHeavyPart();
|
_parent->checkHeavyPart();
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,8 @@ private:
|
||||||
|
|
||||||
[[nodiscard]] QSize photoSize() const;
|
[[nodiscard]] QSize photoSize() const;
|
||||||
|
|
||||||
|
void togglePollingStory(bool enabled) const;
|
||||||
|
|
||||||
const not_null<PhotoData*> _data;
|
const not_null<PhotoData*> _data;
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
||||||
|
@ -167,10 +169,11 @@ private:
|
||||||
const std::unique_ptr<MediaSpoiler> _spoiler;
|
const std::unique_ptr<MediaSpoiler> _spoiler;
|
||||||
mutable QImage _imageCache;
|
mutable QImage _imageCache;
|
||||||
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
|
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
|
||||||
uint32 _serviceWidth : 29 = 0;
|
uint32 _serviceWidth : 28 = 0;
|
||||||
mutable uint32 _imageCacheForum : 1 = 0;
|
mutable uint32 _imageCacheForum : 1 = 0;
|
||||||
mutable uint32 _imageCacheBlurred : 1 = 0;
|
mutable uint32 _imageCacheBlurred : 1 = 0;
|
||||||
mutable uint32 _story : 1 = 0;
|
mutable uint32 _story : 1 = 0;
|
||||||
|
mutable uint32 _pollingStory : 1 = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,9 @@ StoryMention::StoryMention(
|
||||||
, _unread(story->owner().stories().isUnread(story) ? 1 : 0) {
|
, _unread(story->owner().stories().isUnread(story) ? 1 : 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
StoryMention::~StoryMention() = default;
|
StoryMention::~StoryMention() {
|
||||||
|
changeSubscribedTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
int StoryMention::top() {
|
int StoryMention::top() {
|
||||||
return st::msgServiceGiftBoxButtonMargins.top();
|
return st::msgServiceGiftBoxButtonMargins.top();
|
||||||
|
@ -105,13 +107,12 @@ void StoryMention::draw(
|
||||||
? history->session().user()
|
? history->session().user()
|
||||||
: history->peer);
|
: history->peer);
|
||||||
_thumbnailFromStory = showStory;
|
_thumbnailFromStory = showStory;
|
||||||
_subscribed = 0;
|
changeSubscribedTo(0);
|
||||||
}
|
}
|
||||||
if (!_subscribed) {
|
if (changeSubscribedTo(1)) {
|
||||||
_thumbnail->subscribeToUpdates([=] {
|
_thumbnail->subscribeToUpdates([=] {
|
||||||
_parent->data()->history()->owner().requestViewRepaint(_parent);
|
_parent->data()->history()->owner().requestViewRepaint(_parent);
|
||||||
});
|
});
|
||||||
_subscribed = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto padding = (geometry.width() - st::storyMentionSize) / 2;
|
const auto padding = (geometry.width() - st::storyMentionSize) / 2;
|
||||||
|
@ -164,10 +165,26 @@ bool StoryMention::hasHeavyPart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StoryMention::unloadHeavyPart() {
|
void StoryMention::unloadHeavyPart() {
|
||||||
if (_subscribed) {
|
if (changeSubscribedTo(0)) {
|
||||||
_subscribed = 0;
|
|
||||||
_thumbnail->subscribeToUpdates(nullptr);
|
_thumbnail->subscribeToUpdates(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StoryMention::changeSubscribedTo(uint32 value) {
|
||||||
|
Expects(value == 0 || value == 1);
|
||||||
|
|
||||||
|
if (_subscribed == value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_subscribed = value;
|
||||||
|
const auto stories = &_parent->history()->owner().stories();
|
||||||
|
if (value) {
|
||||||
|
_parent->history()->owner().registerHeavyViewPart(_parent);
|
||||||
|
stories->registerPolling(_story, Data::Stories::Polling::Chat);
|
||||||
|
} else {
|
||||||
|
stories->unregisterPolling(_story, Data::Stories::Polling::Chat);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -55,6 +55,8 @@ public:
|
||||||
private:
|
private:
|
||||||
using Thumbnail = Dialogs::Stories::Thumbnail;
|
using Thumbnail = Dialogs::Stories::Thumbnail;
|
||||||
|
|
||||||
|
bool changeSubscribedTo(uint32 value);
|
||||||
|
|
||||||
const not_null<Element*> _parent;
|
const not_null<Element*> _parent;
|
||||||
const not_null<Data::Story*> _story;
|
const not_null<Data::Story*> _story;
|
||||||
std::shared_ptr<Thumbnail> _thumbnail;
|
std::shared_ptr<Thumbnail> _thumbnail;
|
||||||
|
|
|
@ -56,6 +56,10 @@ Provider::Provider(not_null<AbstractController*> controller)
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Provider::~Provider() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
Type Provider::type() {
|
Type Provider::type() {
|
||||||
return Type::PhotoVideo;
|
return Type::PhotoVideo;
|
||||||
}
|
}
|
||||||
|
@ -90,11 +94,20 @@ std::optional<int> Provider::fullCount() {
|
||||||
return _slice.fullCount();
|
return _slice.fullCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Provider::restart() {
|
void Provider::clear() {
|
||||||
|
for (const auto &[storyId, _] : _layouts) {
|
||||||
|
_peer->owner().stories().unregisterPolling(
|
||||||
|
{ _peer->id, storyId },
|
||||||
|
Data::Stories::Polling::Chat);
|
||||||
|
}
|
||||||
_layouts.clear();
|
_layouts.clear();
|
||||||
_aroundId = kDefaultAroundId;
|
_aroundId = kDefaultAroundId;
|
||||||
_idsLimit = kMinimalIdsLimit;
|
_idsLimit = kMinimalIdsLimit;
|
||||||
_slice = Data::StoriesIdsSlice();
|
_slice = Data::StoriesIdsSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Provider::restart() {
|
||||||
|
clear();
|
||||||
refreshViewer();
|
refreshViewer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +223,9 @@ void Provider::markLayoutsStale() {
|
||||||
void Provider::clearStaleLayouts() {
|
void Provider::clearStaleLayouts() {
|
||||||
for (auto i = _layouts.begin(); i != _layouts.end();) {
|
for (auto i = _layouts.begin(); i != _layouts.end();) {
|
||||||
if (i->second.stale) {
|
if (i->second.stale) {
|
||||||
|
_peer->owner().stories().unregisterPolling(
|
||||||
|
{ _peer->id, i->first },
|
||||||
|
Data::Stories::Polling::Chat);
|
||||||
_layoutRemoved.fire(i->second.item.get());
|
_layoutRemoved.fire(i->second.item.get());
|
||||||
const auto taken = _items.take(i->first);
|
const auto taken = _items.take(i->first);
|
||||||
i = _layouts.erase(i);
|
i = _layouts.erase(i);
|
||||||
|
@ -240,6 +256,9 @@ bool Provider::isAfter(
|
||||||
void Provider::itemRemoved(not_null<const HistoryItem*> item) {
|
void Provider::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
const auto id = StoryIdFromMsgId(item->id);
|
const auto id = StoryIdFromMsgId(item->id);
|
||||||
if (const auto i = _layouts.find(id); i != end(_layouts)) {
|
if (const auto i = _layouts.find(id); i != end(_layouts)) {
|
||||||
|
_peer->owner().stories().unregisterPolling(
|
||||||
|
{ _peer->id, id },
|
||||||
|
Data::Stories::Polling::Chat);
|
||||||
_layoutRemoved.fire(i->second.item.get());
|
_layoutRemoved.fire(i->second.item.get());
|
||||||
_layouts.erase(i);
|
_layouts.erase(i);
|
||||||
}
|
}
|
||||||
|
@ -253,6 +272,9 @@ BaseLayout *Provider::getLayout(
|
||||||
if (auto layout = createLayout(id, delegate)) {
|
if (auto layout = createLayout(id, delegate)) {
|
||||||
layout->initDimensions();
|
layout->initDimensions();
|
||||||
it = _layouts.emplace(id, std::move(layout)).first;
|
it = _layouts.emplace(id, std::move(layout)).first;
|
||||||
|
_peer->owner().stories().registerPolling(
|
||||||
|
{ _peer->id, id },
|
||||||
|
Data::Stories::Polling::Chat);
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ class Provider final
|
||||||
, public base::has_weak_ptr {
|
, public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
explicit Provider(not_null<AbstractController*> controller);
|
explicit Provider(not_null<AbstractController*> controller);
|
||||||
|
~Provider();
|
||||||
|
|
||||||
Media::Type type() override;
|
Media::Type type() override;
|
||||||
bool hasSelectRestriction() override;
|
bool hasSelectRestriction() override;
|
||||||
|
@ -99,6 +100,7 @@ private:
|
||||||
void itemRemoved(not_null<const HistoryItem*> item);
|
void itemRemoved(not_null<const HistoryItem*> item);
|
||||||
void markLayoutsStale();
|
void markLayoutsStale();
|
||||||
void clearStaleLayouts();
|
void clearStaleLayouts();
|
||||||
|
void clear();
|
||||||
|
|
||||||
[[nodiscard]] HistoryItem *ensureItem(StoryId id);
|
[[nodiscard]] HistoryItem *ensureItem(StoryId id);
|
||||||
[[nodiscard]] Media::BaseLayout *getLayout(
|
[[nodiscard]] Media::BaseLayout *getLayout(
|
||||||
|
|
|
@ -302,7 +302,9 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
_contentFadeAnimation.stop();
|
_contentFadeAnimation.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::~Controller() = default;
|
Controller::~Controller() {
|
||||||
|
changeShown(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::updateContentFaded() {
|
void Controller::updateContentFaded() {
|
||||||
if (_contentFaded == _replyActive) {
|
if (_contentFaded == _replyActive) {
|
||||||
|
@ -480,6 +482,9 @@ void Controller::initLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::Story *Controller::story() const {
|
Data::Story *Controller::story() const {
|
||||||
|
if (!_session) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
const auto maybeStory = _session->data().stories().lookup(_shown);
|
const auto maybeStory = _session->data().stories().lookup(_shown);
|
||||||
return maybeStory ? maybeStory->get() : nullptr;
|
return maybeStory ? maybeStory->get() : nullptr;
|
||||||
}
|
}
|
||||||
|
@ -748,10 +753,9 @@ void Controller::show(
|
||||||
.date = story->date(),
|
.date = story->date(),
|
||||||
.edited = story->edited(),
|
.edited = story->edited(),
|
||||||
});
|
});
|
||||||
if (_shown == storyId && _session == &story->session()) {
|
if (!changeShown(story)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_shown = storyId;
|
|
||||||
_viewed = false;
|
_viewed = false;
|
||||||
invalidate_weak_ptrs(&_viewsLoadGuard);
|
invalidate_weak_ptrs(&_viewsLoadGuard);
|
||||||
_reactions->hide();
|
_reactions->hide();
|
||||||
|
@ -769,41 +773,72 @@ void Controller::show(
|
||||||
.valid = user->isSelf(),
|
.valid = user->isSelf(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto session = &story->session();
|
|
||||||
if (_session != session) {
|
|
||||||
_session = session;
|
|
||||||
_sessionLifetime = session->changes().storyUpdates(
|
|
||||||
Data::StoryUpdate::Flag::Destroyed
|
|
||||||
) | rpl::start_with_next([=](Data::StoryUpdate update) {
|
|
||||||
if (update.story->fullId() == _shown) {
|
|
||||||
_delegate->storiesClose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
session->data().stories().itemsChanged(
|
|
||||||
) | rpl::start_with_next([=](PeerId peerId) {
|
|
||||||
if (_waitingForId.peer == peerId) {
|
|
||||||
checkWaitingFor();
|
|
||||||
}
|
|
||||||
}, _sessionLifetime);
|
|
||||||
session->changes().storyUpdates(
|
|
||||||
Data::StoryUpdate::Flag::Edited
|
|
||||||
) | rpl::filter([=](const Data::StoryUpdate &update) {
|
|
||||||
return (update.story == this->story());
|
|
||||||
}) | rpl::start_with_next([=](const Data::StoryUpdate &update) {
|
|
||||||
show(update.story, _context);
|
|
||||||
_delegate->storiesRedisplay(update.story);
|
|
||||||
}, _sessionLifetime);
|
|
||||||
_sessionLifetime.add([=] {
|
|
||||||
session->data().stories().setPreloadingInViewer({});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stories.loadAround(storyId, context);
|
stories.loadAround(storyId, context);
|
||||||
|
|
||||||
updatePlayingAllowed();
|
updatePlayingAllowed();
|
||||||
user->updateFull();
|
user->updateFull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Controller::changeShown(Data::Story *story) {
|
||||||
|
const auto id = story ? story->fullId() : FullStoryId();
|
||||||
|
const auto session = story ? &story->session() : nullptr;
|
||||||
|
const auto sessionChanged = (_session != session);
|
||||||
|
if (_shown == id && !sessionChanged) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (const auto now = this->story()) {
|
||||||
|
now->owner().stories().unregisterPolling(
|
||||||
|
now,
|
||||||
|
Data::Stories::Polling::Viewer);
|
||||||
|
}
|
||||||
|
if (sessionChanged) {
|
||||||
|
_sessionLifetime.destroy();
|
||||||
|
}
|
||||||
|
_shown = id;
|
||||||
|
_session = session;
|
||||||
|
if (sessionChanged) {
|
||||||
|
subscribeToSession();
|
||||||
|
}
|
||||||
|
if (story) {
|
||||||
|
story->owner().stories().registerPolling(
|
||||||
|
story,
|
||||||
|
Data::Stories::Polling::Viewer);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::subscribeToSession() {
|
||||||
|
Expects(!_sessionLifetime);
|
||||||
|
|
||||||
|
if (!_session) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_session->changes().storyUpdates(
|
||||||
|
Data::StoryUpdate::Flag::Destroyed
|
||||||
|
) | rpl::start_with_next([=](Data::StoryUpdate update) {
|
||||||
|
if (update.story->fullId() == _shown) {
|
||||||
|
_delegate->storiesClose();
|
||||||
|
}
|
||||||
|
}, _sessionLifetime);
|
||||||
|
_session->data().stories().itemsChanged(
|
||||||
|
) | rpl::start_with_next([=](PeerId peerId) {
|
||||||
|
if (_waitingForId.peer == peerId) {
|
||||||
|
checkWaitingFor();
|
||||||
|
}
|
||||||
|
}, _sessionLifetime);
|
||||||
|
_session->changes().storyUpdates(
|
||||||
|
Data::StoryUpdate::Flag::Edited
|
||||||
|
) | rpl::filter([=](const Data::StoryUpdate &update) {
|
||||||
|
return (update.story == this->story());
|
||||||
|
}) | rpl::start_with_next([=](const Data::StoryUpdate &update) {
|
||||||
|
show(update.story, _context);
|
||||||
|
_delegate->storiesRedisplay(update.story);
|
||||||
|
}, _sessionLifetime);
|
||||||
|
_sessionLifetime.add([=] {
|
||||||
|
_session->data().stories().setPreloadingInViewer({});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::updatePlayingAllowed() {
|
void Controller::updatePlayingAllowed() {
|
||||||
if (!_shown) {
|
if (!_shown) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -164,6 +164,8 @@ private:
|
||||||
class Unsupported;
|
class Unsupported;
|
||||||
|
|
||||||
void initLayout();
|
void initLayout();
|
||||||
|
bool changeShown(Data::Story *story);
|
||||||
|
void subscribeToSession();
|
||||||
void updatePhotoPlayback(const Player::TrackState &state);
|
void updatePhotoPlayback(const Player::TrackState &state);
|
||||||
void updatePlayback(const Player::TrackState &state);
|
void updatePlayback(const Player::TrackState &state);
|
||||||
void updatePowerSaveBlocker(const Player::TrackState &state);
|
void updatePowerSaveBlocker(const Player::TrackState &state);
|
||||||
|
|
Loading…
Add table
Reference in a new issue