mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 06:35:14 +00:00
Add short-polling of stories.
This commit is contained in:
@@ -38,6 +38,8 @@ constexpr auto kSavedPerPage = 100;
|
||||
constexpr auto kMaxPreloadSources = 10;
|
||||
constexpr auto kStillPreloadFromFirst = 3;
|
||||
constexpr auto kMaxSegmentsCount = 180;
|
||||
constexpr auto kPollingIntervalChat = 5 * TimeId(60);
|
||||
constexpr auto kPollingIntervalViewer = 1 * TimeId(60);
|
||||
|
||||
using UpdateFlag = StoryUpdate::Flag;
|
||||
|
||||
@@ -98,10 +100,12 @@ Stories::Stories(not_null<Session*> owner)
|
||||
: _owner(owner)
|
||||
, _expireTimer([=] { processExpired(); })
|
||||
, _markReadTimer([=] { sendMarkAsReadRequests(); })
|
||||
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); }) {
|
||||
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); })
|
||||
, _pollingTimer([=] { sendPollingRequests(); }) {
|
||||
}
|
||||
|
||||
Stories::~Stories() {
|
||||
Expects(_pollingSettings.empty());
|
||||
}
|
||||
|
||||
Session &Stories::owner() const {
|
||||
@@ -348,7 +352,7 @@ Story *Stories::parseAndApply(
|
||||
const auto result = i->second.get();
|
||||
const auto pinned = result->pinned();
|
||||
const auto mediaChanged = (result->media() != *media);
|
||||
if (result->applyChanges(*media, data)) {
|
||||
if (result->applyChanges(*media, data, now)) {
|
||||
if (result->pinned() != pinned) {
|
||||
savedStateUpdated(result);
|
||||
}
|
||||
@@ -358,6 +362,11 @@ Story *Stories::parseAndApply(
|
||||
if (const auto item = lookupItem(result)) {
|
||||
item->applyChanges(result);
|
||||
}
|
||||
_owner->refreshStoryItemViews(fullId);
|
||||
}
|
||||
const auto j = _pollingSettings.find(result);
|
||||
if (j != end(_pollingSettings)) {
|
||||
maybeSchedulePolling(result, j->second, now);
|
||||
}
|
||||
if (mediaChanged) {
|
||||
_preloaded.remove(fullId);
|
||||
@@ -378,7 +387,7 @@ Story *Stories::parseAndApply(
|
||||
StoryMedia{ *media },
|
||||
data.vdate().v,
|
||||
data.vexpire_date().v)).first->second.get();
|
||||
result->applyChanges(*media, data);
|
||||
result->applyChanges(*media, data, now);
|
||||
if (result->pinned()) {
|
||||
savedStateUpdated(result);
|
||||
}
|
||||
@@ -656,6 +665,7 @@ void Stories::applyDeleted(FullStoryId id) {
|
||||
preloadFinished(id);
|
||||
}
|
||||
_owner->refreshStoryItemViews(id);
|
||||
Assert(!_pollingSettings.contains(story.get()));
|
||||
if (i->second.empty()) {
|
||||
_stories.erase(i);
|
||||
}
|
||||
@@ -818,13 +828,15 @@ base::expected<not_null<Story*>, NoStory> Stories::lookup(
|
||||
_deleted.contains(id) ? NoStory::Deleted : NoStory::Unknown);
|
||||
}
|
||||
|
||||
void Stories::resolve(FullStoryId id, Fn<void()> done) {
|
||||
const auto already = lookup(id);
|
||||
if (already.has_value() || already.error() != NoStory::Unknown) {
|
||||
if (done) {
|
||||
done();
|
||||
void Stories::resolve(FullStoryId id, Fn<void()> done, bool force) {
|
||||
if (!force) {
|
||||
const auto already = lookup(id);
|
||||
if (already.has_value() || already.error() != NoStory::Unknown) {
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (const auto i = _resolveSent.find(id.peer); i != end(_resolveSent)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
const auto till = _readTill.find(peer->id);
|
||||
const auto readTill = (till != end(_readTill)) ? till->second : 0;
|
||||
|
@@ -153,7 +153,7 @@ public:
|
||||
|
||||
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
|
||||
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(
|
||||
not_null<Story*> story);
|
||||
@@ -209,6 +209,16 @@ public:
|
||||
StoryId storyMaxId);
|
||||
[[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:
|
||||
struct Saved {
|
||||
StoriesIds ids;
|
||||
@@ -217,6 +227,10 @@ private:
|
||||
bool loaded = false;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
struct PollingSettings {
|
||||
int chat = 0;
|
||||
int viewer = 0;
|
||||
};
|
||||
|
||||
void parseAndApply(const MTPUserStories &stories);
|
||||
[[nodiscard]] Story *parseAndApply(
|
||||
@@ -265,6 +279,14 @@ private:
|
||||
void startPreloading(not_null<Story*> story);
|
||||
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;
|
||||
std::unordered_map<
|
||||
PeerId,
|
||||
@@ -336,6 +358,9 @@ private:
|
||||
mtpRequestId _readTillsRequestId = 0;
|
||||
bool _readTillReceived = false;
|
||||
|
||||
base::flat_map<not_null<Story*>, PollingSettings> _pollingSettings;
|
||||
base::Timer _pollingTimer;
|
||||
|
||||
};
|
||||
|
||||
} // 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 edited = data.is_edited();
|
||||
const auto isPublic = data.is_public();
|
||||
@@ -424,6 +429,10 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TimeId Story::lastUpdateTime() const {
|
||||
return _lastUpdateTime;
|
||||
}
|
||||
|
||||
StoryPreload::StoryPreload(not_null<Story*> story, Fn<void()> done)
|
||||
: _story(story)
|
||||
, _done(std::move(done)) {
|
||||
|
@@ -113,7 +113,11 @@ public:
|
||||
const std::vector<StoryView> &slice,
|
||||
int total);
|
||||
|
||||
bool applyChanges(StoryMedia media, const MTPDstoryItem &data);
|
||||
bool applyChanges(
|
||||
StoryMedia media,
|
||||
const MTPDstoryItem &data,
|
||||
TimeId now);
|
||||
[[nodiscard]] TimeId lastUpdateTime() const;
|
||||
|
||||
private:
|
||||
const StoryId _id = 0;
|
||||
@@ -125,6 +129,7 @@ private:
|
||||
int _views = 0;
|
||||
const TimeId _date = 0;
|
||||
const TimeId _expires = 0;
|
||||
TimeId _lastUpdateTime = 0;
|
||||
bool _pinned : 1 = false;
|
||||
bool _isPublic : 1 = false;
|
||||
bool _closeFriends : 1 = false;
|
||||
|
Reference in New Issue
Block a user