Mark stories as read.
This commit is contained in:
		
							parent
							
								
									f323370752
								
							
						
					
					
						commit
						f814e401b9
					
				
					 7 changed files with 158 additions and 27 deletions
				
			
		| 
						 | 
				
			
			@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#include "data/data_photo.h"
 | 
			
		||||
#include "data/data_document.h"
 | 
			
		||||
#include "data/data_session.h"
 | 
			
		||||
#include "data/data_stories.h"
 | 
			
		||||
#include "data/data_user.h"
 | 
			
		||||
#include "data/data_channel.h"
 | 
			
		||||
#include "data/data_download_manager.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -1685,6 +1686,9 @@ bool Application::readyToQuit() {
 | 
			
		|||
				if (session->api().isQuitPrevent()) {
 | 
			
		||||
					prevented = true;
 | 
			
		||||
				}
 | 
			
		||||
				if (session->data().stories().isQuitPrevent()) {
 | 
			
		||||
					prevented = true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
 | 
			
		||||
#include "api/api_text_entities.h"
 | 
			
		||||
#include "apiwrap.h"
 | 
			
		||||
#include "core/application.h"
 | 
			
		||||
#include "data/data_changes.h"
 | 
			
		||||
#include "data/data_document.h"
 | 
			
		||||
#include "data/data_file_origin.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +31,7 @@ namespace {
 | 
			
		|||
constexpr auto kMaxResolveTogether = 100;
 | 
			
		||||
constexpr auto kIgnorePreloadAroundIfLoaded = 15;
 | 
			
		||||
constexpr auto kPreloadAroundCount = 30;
 | 
			
		||||
constexpr auto kMarkAsReadDelay = 3 * crl::time(1000);
 | 
			
		||||
 | 
			
		||||
using UpdateFlag = StoryUpdate::Flag;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +63,7 @@ std::optional<StoryMedia> ParseMedia(
 | 
			
		|||
} // namespace
 | 
			
		||||
 | 
			
		||||
bool StoriesList::unread() const {
 | 
			
		||||
	return !ids.empty() && readTill < ids.front();
 | 
			
		||||
	return !ids.empty() && readTill < ids.back();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Story::Story(
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +190,9 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
 | 
			
		|||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stories::Stories(not_null<Session*> owner) : _owner(owner) {
 | 
			
		||||
Stories::Stories(not_null<Session*> owner)
 | 
			
		||||
: _owner(owner)
 | 
			
		||||
, _markReadTimer([=] { sendMarkAsReadRequests(); }) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stories::~Stories() {
 | 
			
		||||
| 
						 | 
				
			
			@ -222,13 +226,13 @@ StoriesList Stories::parse(const MTPUserStories &stories) {
 | 
			
		|||
	for (const auto &story : list) {
 | 
			
		||||
		story.match([&](const MTPDstoryItem &data) {
 | 
			
		||||
			if (const auto story = parseAndApply(result.user, data)) {
 | 
			
		||||
				result.ids.push_back(story->id());
 | 
			
		||||
				result.ids.emplace(story->id());
 | 
			
		||||
			} else {
 | 
			
		||||
				applyDeleted({ peerFromUser(userId), data.vid().v });
 | 
			
		||||
				--result.total;
 | 
			
		||||
			}
 | 
			
		||||
		}, [&](const MTPDstoryItemSkipped &data) {
 | 
			
		||||
			result.ids.push_back(data.vid().v);
 | 
			
		||||
			result.ids.emplace(data.vid().v);
 | 
			
		||||
		}, [&](const MTPDstoryItemDeleted &data) {
 | 
			
		||||
			applyDeleted({ peerFromUser(userId), data.vid().v });
 | 
			
		||||
			--result.total;
 | 
			
		||||
| 
						 | 
				
			
			@ -434,13 +438,13 @@ void Stories::applyDeleted(FullStoryId id) {
 | 
			
		|||
		return list.user->id;
 | 
			
		||||
	});
 | 
			
		||||
	if (j != end(_all)) {
 | 
			
		||||
		const auto till = ranges::remove(j->ids, id.story);
 | 
			
		||||
		const auto removed = int(std::distance(till, end(j->ids)));
 | 
			
		||||
		if (till != end(j->ids)) {
 | 
			
		||||
			j->ids.erase(till, end(j->ids));
 | 
			
		||||
			j->total = std::max(j->total - removed, 0);
 | 
			
		||||
		const auto removed = j->ids.remove(id.story);
 | 
			
		||||
		if (removed) {
 | 
			
		||||
			if (j->ids.empty()) {
 | 
			
		||||
				_all.erase(j);
 | 
			
		||||
			} else {
 | 
			
		||||
				Assert(j->total > 0);
 | 
			
		||||
				--j->total;
 | 
			
		||||
			}
 | 
			
		||||
			_allChanged.fire({});
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -534,8 +538,8 @@ void Stories::applyChanges(StoriesList &&list) {
 | 
			
		|||
	if (i != end(_all)) {
 | 
			
		||||
		auto added = false;
 | 
			
		||||
		for (const auto id : list.ids) {
 | 
			
		||||
			if (!ranges::contains(i->ids, id)) {
 | 
			
		||||
				i->ids.push_back(id);
 | 
			
		||||
			if (!i->ids.contains(id)) {
 | 
			
		||||
				i->ids.emplace(id);
 | 
			
		||||
				++i->total;
 | 
			
		||||
				added = true;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -584,4 +588,78 @@ void Stories::loadAround(FullStoryId id) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Stories::markAsRead(FullStoryId id) {
 | 
			
		||||
	const auto i = ranges::find(_all, id.peer, [](const StoriesList &list) {
 | 
			
		||||
		return list.user->id;
 | 
			
		||||
	});
 | 
			
		||||
	Assert(i != end(_all));
 | 
			
		||||
	if (i->readTill >= id.story) {
 | 
			
		||||
		return;
 | 
			
		||||
	} else if (!_markReadPending.contains(id.peer)) {
 | 
			
		||||
		sendMarkAsReadRequests();
 | 
			
		||||
	}
 | 
			
		||||
	_markReadPending.emplace(id.peer);
 | 
			
		||||
	i->readTill = id.story;
 | 
			
		||||
	_markReadTimer.callOnce(kMarkAsReadDelay);
 | 
			
		||||
	_allChanged.fire({});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Stories::sendMarkAsReadRequest(
 | 
			
		||||
		not_null<PeerData*> peer,
 | 
			
		||||
		StoryId tillId) {
 | 
			
		||||
	Expects(peer->isUser());
 | 
			
		||||
 | 
			
		||||
	const auto peerId = peer->id;
 | 
			
		||||
	_markReadRequests.emplace(peerId);
 | 
			
		||||
	const auto finish = [=] {
 | 
			
		||||
		_markReadRequests.remove(peerId);
 | 
			
		||||
		if (!_markReadTimer.isActive()
 | 
			
		||||
			&& _markReadPending.contains(peerId)) {
 | 
			
		||||
			sendMarkAsReadRequests();
 | 
			
		||||
		}
 | 
			
		||||
		if (_markReadRequests.empty()) {
 | 
			
		||||
			if (Core::Quitting()) {
 | 
			
		||||
				LOG(("Stories doesn't prevent quit any more."));
 | 
			
		||||
			}
 | 
			
		||||
			Core::App().quitPreventFinished();
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const auto api = &_owner->session().api();
 | 
			
		||||
	api->request(MTPstories_ReadStories(
 | 
			
		||||
		peer->asUser()->inputUser,
 | 
			
		||||
		MTP_int(tillId)
 | 
			
		||||
	)).done(finish).fail(finish).send();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Stories::sendMarkAsReadRequests() {
 | 
			
		||||
	_markReadTimer.cancel();
 | 
			
		||||
	for (auto i = begin(_markReadPending); i != end(_markReadPending);) {
 | 
			
		||||
		const auto peerId = *i;
 | 
			
		||||
		if (_markReadRequests.contains(peerId)) {
 | 
			
		||||
			++i;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		const auto j = ranges::find(_all, peerId, [](
 | 
			
		||||
				const StoriesList &list) {
 | 
			
		||||
			return list.user->id;
 | 
			
		||||
		});
 | 
			
		||||
		if (j != end(_all)) {
 | 
			
		||||
			sendMarkAsReadRequest(j->user, j->readTill);
 | 
			
		||||
		}
 | 
			
		||||
		i = _markReadPending.erase(i);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Stories::isQuitPrevent() {
 | 
			
		||||
	if (!_markReadPending.empty()) {
 | 
			
		||||
		sendMarkAsReadRequests();
 | 
			
		||||
	}
 | 
			
		||||
	if (_markReadRequests.empty()) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	LOG(("Stories prevents quit, marking as read..."));
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Data
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "base/expected.h"
 | 
			
		||||
#include "base/timer.h"
 | 
			
		||||
 | 
			
		||||
class Image;
 | 
			
		||||
class PhotoData;
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +71,7 @@ private:
 | 
			
		|||
 | 
			
		||||
struct StoriesList {
 | 
			
		||||
	not_null<UserData*> user;
 | 
			
		||||
	std::vector<StoryId> ids;
 | 
			
		||||
	base::flat_set<StoryId> ids;
 | 
			
		||||
	StoryId readTill = 0;
 | 
			
		||||
	int total = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -113,6 +114,9 @@ public:
 | 
			
		|||
		FullStoryId id) const;
 | 
			
		||||
	void resolve(FullStoryId id, Fn<void()> done);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool isQuitPrevent();
 | 
			
		||||
	void markAsRead(FullStoryId id);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	[[nodiscard]] StoriesList parse(const MTPUserStories &stories);
 | 
			
		||||
	[[nodiscard]] Story *parseAndApply(
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +133,9 @@ private:
 | 
			
		|||
	void applyDeleted(FullStoryId id);
 | 
			
		||||
	void removeDependencyStory(not_null<Story*> story);
 | 
			
		||||
 | 
			
		||||
	void sendMarkAsReadRequests();
 | 
			
		||||
	void sendMarkAsReadRequest(not_null<PeerData*> peer, StoryId tillId);
 | 
			
		||||
 | 
			
		||||
	const not_null<Session*> _owner;
 | 
			
		||||
	base::flat_map<
 | 
			
		||||
		PeerId,
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +161,10 @@ private:
 | 
			
		|||
 | 
			
		||||
	mtpRequestId _loadMoreRequestId = 0;
 | 
			
		||||
 | 
			
		||||
	base::flat_set<PeerId> _markReadPending;
 | 
			
		||||
	base::Timer _markReadTimer;
 | 
			
		||||
	base::flat_set<PeerId> _markReadRequests;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Data
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,11 +37,15 @@ struct List::Layout {
 | 
			
		|||
	float64 userpicLeft = 0.;
 | 
			
		||||
	float64 photoLeft = 0.;
 | 
			
		||||
	float64 left = 0.;
 | 
			
		||||
	float64 single = 0.;
 | 
			
		||||
	int leftFull = 0;
 | 
			
		||||
	int leftSmall = 0;
 | 
			
		||||
	int singleFull = 0;
 | 
			
		||||
	int singleSmall = 0;
 | 
			
		||||
	int startIndexSmall = 0;
 | 
			
		||||
	int endIndexSmall = 0;
 | 
			
		||||
	int startIndexFull = 0;
 | 
			
		||||
	int endIndexFull = 0;
 | 
			
		||||
	int singleFull = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
List::List(
 | 
			
		||||
| 
						 | 
				
			
			@ -277,11 +281,15 @@ List::Layout List::computeLayout() const {
 | 
			
		|||
		.userpicLeft = userpicLeft,
 | 
			
		||||
		.photoLeft = photoLeft,
 | 
			
		||||
		.left = userpicLeft - photoLeft,
 | 
			
		||||
		.single = lerp(st.shift, singleFull),
 | 
			
		||||
		.leftFull = leftFull,
 | 
			
		||||
		.leftSmall = leftSmall,
 | 
			
		||||
		.singleFull = singleFull,
 | 
			
		||||
		.singleSmall = st.shift,
 | 
			
		||||
		.startIndexSmall = startIndexSmall,
 | 
			
		||||
		.endIndexSmall = endIndexSmall,
 | 
			
		||||
		.startIndexFull = startIndexFull,
 | 
			
		||||
		.endIndexFull = endIndexFull,
 | 
			
		||||
		.singleFull = singleFull,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -296,8 +304,6 @@ void List::paintEvent(QPaintEvent *e) {
 | 
			
		|||
	auto &rendering = _data.empty() ? _hidingData : _data;
 | 
			
		||||
	const auto line = lerp(st.lineTwice, full.lineTwice) / 2.;
 | 
			
		||||
	const auto lineRead = lerp(st.lineReadTwice, full.lineReadTwice) / 2.;
 | 
			
		||||
	const auto singleSmall = st.shift;
 | 
			
		||||
	const auto single = lerp(singleSmall, layout.singleFull);
 | 
			
		||||
	const auto photoTopSmall = (st.height - st.photo) / 2.;
 | 
			
		||||
	const auto photoTop = lerp(photoTopSmall, full.photoTop);
 | 
			
		||||
	const auto photo = lerp(st.photo, full.photo);
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +352,7 @@ void List::paintEvent(QPaintEvent *e) {
 | 
			
		|||
		const auto full = (drawFull && indexFull < layout.endIndexFull)
 | 
			
		||||
			? &rendering.items[indexFull]
 | 
			
		||||
			: nullptr;
 | 
			
		||||
		const auto x = layout.left + single * index;
 | 
			
		||||
		const auto x = layout.left + layout.single * index;
 | 
			
		||||
		return Single{ x, indexSmall, small, indexFull, full };
 | 
			
		||||
	};
 | 
			
		||||
	const auto hasUnread = [&](const Single &single) {
 | 
			
		||||
| 
						 | 
				
			
			@ -697,19 +703,17 @@ void List::updateSelected() {
 | 
			
		|||
	const auto &full = st::dialogsStoriesFull;
 | 
			
		||||
	const auto p = mapFromGlobal(_lastMousePosition);
 | 
			
		||||
	const auto layout = computeLayout();
 | 
			
		||||
	const auto firstRightFull = full.left + layout.singleFull;
 | 
			
		||||
	const auto firstRightSmall = st.left
 | 
			
		||||
	const auto firstRightFull = layout.leftFull
 | 
			
		||||
		+ (layout.startIndexFull + 1) * layout.singleFull;
 | 
			
		||||
	const auto firstRightSmall = layout.leftSmall
 | 
			
		||||
		+ st.photoLeft
 | 
			
		||||
		+ st.photo;
 | 
			
		||||
	const auto stepFull = layout.singleFull;
 | 
			
		||||
	const auto stepSmall = st.shift;
 | 
			
		||||
	const auto lastRightAddFull = 0;
 | 
			
		||||
	const auto lastRightAddSmall = st.photoLeft;
 | 
			
		||||
	const auto lerp = [&](float64 a, float64 b) {
 | 
			
		||||
		return a + (b - a) * layout.ratio;
 | 
			
		||||
	};
 | 
			
		||||
	const auto firstRight = lerp(firstRightSmall, firstRightFull);
 | 
			
		||||
	const auto step = lerp(stepSmall, stepFull);
 | 
			
		||||
	const auto lastRightAdd = lerp(lastRightAddSmall, lastRightAddFull);
 | 
			
		||||
	const auto activateFull = (layout.ratio >= 0.5);
 | 
			
		||||
	const auto startIndex = activateFull
 | 
			
		||||
| 
						 | 
				
			
			@ -721,14 +725,16 @@ void List::updateSelected() {
 | 
			
		|||
	const auto x = p.x();
 | 
			
		||||
	const auto infiniteIndex = (x < firstRight)
 | 
			
		||||
		? 0
 | 
			
		||||
		: int(std::floor(((x - firstRight) / step) + 1));
 | 
			
		||||
		: int(std::floor(((x - firstRight) / layout.single) + 1));
 | 
			
		||||
	const auto index = (endIndex == startIndex)
 | 
			
		||||
		? -1
 | 
			
		||||
		: (infiniteIndex == endIndex - startIndex
 | 
			
		||||
			&& x < firstRight
 | 
			
		||||
				+ (endIndex - startIndex - 1) * step
 | 
			
		||||
				+ (endIndex - startIndex - 1) * layout.single
 | 
			
		||||
				+ lastRightAdd)
 | 
			
		||||
		? (infiniteIndex - 1) // Last small part should still be clickable.
 | 
			
		||||
		: (startIndex + infiniteIndex >= endIndex)
 | 
			
		||||
		? -1
 | 
			
		||||
		: infiniteIndex;
 | 
			
		||||
	const auto selected = (index < 0
 | 
			
		||||
		|| startIndex + index >= layout.itemsCount)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,8 @@ constexpr auto kSiblingOutsidePart = 0.24;
 | 
			
		|||
constexpr auto kSiblingUserpicSize = 0.3;
 | 
			
		||||
constexpr auto kInnerHeightMultiplier = 1.6;
 | 
			
		||||
constexpr auto kPreloadUsersCount = 3;
 | 
			
		||||
constexpr auto kMarkAsReadAfterSeconds = 1;
 | 
			
		||||
constexpr auto kMarkAsReadAfterProgress = 0.2;
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -360,7 +362,7 @@ void Controller::show(
 | 
			
		|||
	showSiblings(lists, index);
 | 
			
		||||
 | 
			
		||||
	const auto &list = lists[index];
 | 
			
		||||
	const auto id = list.ids[subindex];
 | 
			
		||||
	const auto id = *(begin(list.ids) + subindex);
 | 
			
		||||
	const auto storyId = FullStoryId{
 | 
			
		||||
		.peer = list.user->id,
 | 
			
		||||
		.story = id,
 | 
			
		||||
| 
						 | 
				
			
			@ -464,6 +466,7 @@ void Controller::updatePhotoPlayback(const Player::TrackState &state) {
 | 
			
		|||
void Controller::updatePlayback(const Player::TrackState &state) {
 | 
			
		||||
	_slider->updatePlayback(state);
 | 
			
		||||
	updatePowerSaveBlocker(state);
 | 
			
		||||
	maybeMarkAsRead(state);
 | 
			
		||||
	if (Player::IsStoppedAtEnd(state.state)) {
 | 
			
		||||
		if (!subjumpFor(1)) {
 | 
			
		||||
			_delegate->storiesClose();
 | 
			
		||||
| 
						 | 
				
			
			@ -471,6 +474,26 @@ void Controller::updatePlayback(const Player::TrackState &state) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Controller::maybeMarkAsRead(const Player::TrackState &state) {
 | 
			
		||||
	const auto length = state.length;
 | 
			
		||||
	const auto position = Player::IsStoppedAtEnd(state.state)
 | 
			
		||||
		? state.length
 | 
			
		||||
		: Player::IsStoppedOrStopping(state.state)
 | 
			
		||||
		? 0
 | 
			
		||||
		: state.position;
 | 
			
		||||
	if (position > state.frequency * kMarkAsReadAfterSeconds) {
 | 
			
		||||
		if (position > kMarkAsReadAfterProgress * length) {
 | 
			
		||||
			markAsRead();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Controller::markAsRead() {
 | 
			
		||||
	Expects(_list.has_value());
 | 
			
		||||
 | 
			
		||||
	_list->user->owner().stories().markAsRead(_shown);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Controller::subjumpAvailable(int delta) const {
 | 
			
		||||
	const auto index = _index + delta;
 | 
			
		||||
	if (index < 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -482,6 +505,9 @@ bool Controller::subjumpAvailable(int delta) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool Controller::subjumpFor(int delta) {
 | 
			
		||||
	if (delta > 0) {
 | 
			
		||||
		markAsRead();
 | 
			
		||||
	}
 | 
			
		||||
	const auto index = _index + delta;
 | 
			
		||||
	if (index < 0) {
 | 
			
		||||
		if (_siblingLeft && _siblingLeft->shownId().valid()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -507,7 +533,7 @@ void Controller::subjumpTo(int index) {
 | 
			
		|||
 | 
			
		||||
	const auto id = FullStoryId{
 | 
			
		||||
		.peer = _list->user->id,
 | 
			
		||||
		.story = _list->ids[index]
 | 
			
		||||
		.story = *(begin(_list->ids) + index)
 | 
			
		||||
	};
 | 
			
		||||
	auto &stories = _list->user->owner().stories();
 | 
			
		||||
	if (stories.lookup(id)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -554,6 +580,9 @@ bool Controller::jumpFor(int delta) {
 | 
			
		|||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (delta == 1) {
 | 
			
		||||
		if (_list && _index + 1 >= _list->total) {
 | 
			
		||||
			markAsRead();
 | 
			
		||||
		}
 | 
			
		||||
		if (const auto right = _siblingRight.get()) {
 | 
			
		||||
			_delegate->storiesJumpTo(
 | 
			
		||||
				&right->peer()->session(),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -123,6 +123,8 @@ private:
 | 
			
		|||
	void updatePhotoPlayback(const Player::TrackState &state);
 | 
			
		||||
	void updatePlayback(const Player::TrackState &state);
 | 
			
		||||
	void updatePowerSaveBlocker(const Player::TrackState &state);
 | 
			
		||||
	void maybeMarkAsRead(const Player::TrackState &state);
 | 
			
		||||
	void markAsRead();
 | 
			
		||||
 | 
			
		||||
	void showSiblings(
 | 
			
		||||
		const std::vector<Data::StoriesList> &lists,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2486,7 +2486,8 @@ void SessionController::openPeerStories(PeerId peerId) {
 | 
			
		|||
		return list.user->id;
 | 
			
		||||
	});
 | 
			
		||||
	if (i != end(all) && !i->ids.empty()) {
 | 
			
		||||
		openPeerStory(i->user, i->ids.front());
 | 
			
		||||
		const auto j = i->ids.lower_bound(i->readTill + 1);
 | 
			
		||||
		openPeerStory(i->user, j != i->ids.end() ? *j : i->ids.front());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue