Implement UserPhotosViewer using rpl.

This commit is contained in:
John Preston 2017-09-06 17:50:11 +03:00
parent 2690618da2
commit 696478843e
8 changed files with 239 additions and 206 deletions

View File

@ -25,14 +25,25 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_facade.h"
#include "storage/storage_user_photos.h"
UserPhotosSlice::UserPhotosSlice(Key key) : UserPhotosSlice(key, base::none) {
UserPhotosSlice::UserPhotosSlice(Key key) : UserPhotosSlice(
key,
{},
base::none,
base::none,
0) {
}
UserPhotosSlice::UserPhotosSlice(
Key key,
base::optional<int> fullCount)
const std::deque<PhotoId> &ids,
base::optional<int> fullCount,
base::optional<int> skippedBefore,
int skippedAfter)
: _key(key)
, _fullCount(fullCount) {
, _ids(ids)
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
}
base::optional<int> UserPhotosSlice::indexOf(PhotoId photoId) const {
@ -79,82 +90,62 @@ QString UserPhotosSlice::debug() const {
return before + middle + after;
}
UserPhotosViewer::UserPhotosViewer(
class UserPhotosSliceBuilder {
public:
using Key = UserPhotosSlice::Key;
UserPhotosSliceBuilder(Key key, int limitBefore, int limitAfter);
bool applyUpdate(const Storage::UserPhotosResult &update);
bool applyUpdate(const Storage::UserPhotosSliceUpdate &update);
void checkInsufficientPhotos();
rpl::producer<PhotoId> insufficientPhotosAround() const {
return _insufficientPhotosAround.events();
}
UserPhotosSlice snapshot() const;
private:
void mergeSliceData(
base::optional<int> count,
const std::deque<PhotoId> &photoIds,
base::optional<int> skippedBefore,
int skippedAfter);
void sliceToLimits();
Key _key;
std::deque<PhotoId> _ids;
base::optional<int> _fullCount;
base::optional<int> _skippedBefore;
int _skippedAfter = 0;
int _limitBefore = 0;
int _limitAfter = 0;
rpl::event_stream<PhotoId> _insufficientPhotosAround;
};
UserPhotosSliceBuilder::UserPhotosSliceBuilder(
Key key,
int limitBefore,
int limitAfter)
: _key(key)
, _limitBefore(limitBefore)
, _limitAfter(limitAfter)
, _data(_key) {
, _limitAfter(limitAfter) {
}
void UserPhotosViewer::start() {
auto applyUpdateCallback = [this](auto &update) {
this->applyUpdate(update);
};
subscribe(Auth().storage().userPhotosSliceUpdated(), applyUpdateCallback);
loadInitial();
}
void UserPhotosViewer::loadInitial() {
auto weak = base::make_weak_unique(this);
Auth().storage().query(Storage::UserPhotosQuery(
_key,
_limitBefore,
_limitAfter), [weak](Storage::UserPhotosResult &&result) {
if (weak) {
weak->applyStoredResult(std::move(result));
}
});
}
void UserPhotosViewer::applyStoredResult(Storage::UserPhotosResult &&result) {
bool UserPhotosSliceBuilder::applyUpdate(const Storage::UserPhotosResult &update) {
mergeSliceData(
result.count,
result.photoIds,
result.skippedBefore,
result.skippedAfter);
update.count,
update.photoIds,
update.skippedBefore,
update.skippedAfter);
return true;
}
void UserPhotosViewer::mergeSliceData(
base::optional<int> count,
const std::deque<PhotoId> &photoIds,
base::optional<int> skippedBefore,
int skippedAfter) {
if (photoIds.empty()) {
if (_data._fullCount != count) {
_data._fullCount = count;
if (_data._fullCount && *_data._fullCount <= _data.size()) {
_data._fullCount = _data.size();
_data._skippedBefore = _data._skippedAfter = 0;
}
updated.notify(_data);
}
sliceToLimits();
return;
}
if (count) {
_data._fullCount = count;
}
_data._skippedAfter = skippedAfter;
_data._ids = photoIds;
if (_data._fullCount) {
_data._skippedBefore = *_data._fullCount
- _data._skippedAfter
- int(_data._ids.size());
}
sliceToLimits();
updated.notify(_data);
}
void UserPhotosViewer::applyUpdate(const SliceUpdate &update) {
bool UserPhotosSliceBuilder::applyUpdate(const Storage::UserPhotosSliceUpdate &update) {
if (update.userId != _key.userId) {
return;
return false;
}
auto idsCount = update.photoIds ? int(update.photoIds->size()) : 0;
mergeSliceData(
@ -162,28 +153,99 @@ void UserPhotosViewer::applyUpdate(const SliceUpdate &update) {
update.photoIds ? *update.photoIds : std::deque<PhotoId> {},
update.count | func::add(-idsCount),
0);
return true;
}
void UserPhotosViewer::sliceToLimits() {
auto aroundIt = base::find(_data._ids, _key.photoId);
auto removeFromBegin = (aroundIt - _data._ids.begin() - _limitBefore);
auto removeFromEnd = (_data._ids.end() - aroundIt - _limitAfter - 1);
void UserPhotosSliceBuilder::checkInsufficientPhotos() {
sliceToLimits();
}
void UserPhotosSliceBuilder::mergeSliceData(
base::optional<int> count,
const std::deque<PhotoId> &photoIds,
base::optional<int> skippedBefore,
int skippedAfter) {
if (photoIds.empty()) {
if (_fullCount != count) {
_fullCount = count;
if (_fullCount && *_fullCount <= _ids.size()) {
_fullCount = _ids.size();
_skippedBefore = _skippedAfter = 0;
}
}
} else {
if (count) {
_fullCount = count;
}
_skippedAfter = skippedAfter;
_ids = photoIds;
if (_fullCount) {
_skippedBefore = *_fullCount
- _skippedAfter
- int(_ids.size());
}
}
sliceToLimits();
}
void UserPhotosSliceBuilder::sliceToLimits() {
auto aroundIt = base::find(_ids, _key.photoId);
auto removeFromBegin = (aroundIt - _ids.begin() - _limitBefore);
auto removeFromEnd = (_ids.end() - aroundIt - _limitAfter - 1);
if (removeFromEnd > 0) {
_data._ids.erase(_data._ids.end() - removeFromEnd, _data._ids.end());
_data._skippedAfter += removeFromEnd;
_ids.erase(_ids.end() - removeFromEnd, _ids.end());
_skippedAfter += removeFromEnd;
}
if (removeFromBegin > 0) {
_data._ids.erase(_data._ids.begin(), _data._ids.begin() + removeFromBegin);
if (_data._skippedBefore) {
*_data._skippedBefore += removeFromBegin;
_ids.erase(_ids.begin(), _ids.begin() + removeFromBegin);
if (_skippedBefore) {
*_skippedBefore += removeFromBegin;
}
} else if (removeFromBegin < 0 && (!_data._skippedBefore || *_data._skippedBefore > 0)) {
requestPhotos();
} else if (removeFromBegin < 0 && (!_skippedBefore || *_skippedBefore > 0)) {
_insufficientPhotosAround.fire(_ids.empty() ? 0 : _ids.front());
}
}
void UserPhotosViewer::requestPhotos() {
Auth().api().requestUserPhotos(
App::user(_key.userId),
_data._ids.empty() ? 0 : _data._ids.front());
UserPhotosSlice UserPhotosSliceBuilder::snapshot() const {
return UserPhotosSlice(_key, _ids, _fullCount, _skippedBefore, _skippedAfter);
}
rpl::producer<UserPhotosSlice> UserPhotosViewer(
UserPhotosSlice::Key key,
int limitBefore,
int limitAfter) {
return [key, limitBefore, limitAfter](auto consumer) {
auto lifetime = rpl::lifetime();
auto builder = lifetime.make_state<UserPhotosSliceBuilder>(
key,
limitBefore,
limitAfter);
auto applyUpdate = [=](auto &&update) {
if (builder->applyUpdate(std::move(update))) {
consumer.put_next(builder->snapshot());
}
};
auto requestPhotosAround = [user = App::user(key.userId)](PhotoId photoId) {
Auth().api().requestUserPhotos(user, photoId);
};
builder->insufficientPhotosAround()
| rpl::on_next(requestPhotosAround)
| rpl::start(lifetime);
Auth().storage().userPhotosSliceUpdated()
| rpl::on_next(applyUpdate)
| rpl::start(lifetime);
Auth().storage().query(Storage::UserPhotosQuery(
key,
limitBefore,
limitAfter
))
| rpl::on_next(applyUpdate)
| rpl::on_done([=] { builder->checkInsufficientPhotos(); })
| rpl::start(lifetime);
return lifetime;
};
}

View File

@ -23,13 +23,17 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_user_photos.h"
#include "base/weak_unique_ptr.h"
class UserPhotosViewer;
class UserPhotosSlice {
public:
using Key = Storage::UserPhotosKey;
UserPhotosSlice(Key key);
UserPhotosSlice(Key key, base::optional<int> fullCount);
UserPhotosSlice(
Key key,
const std::deque<PhotoId> &ids,
base::optional<int> fullCount,
base::optional<int> skippedBefore,
int skippedAfter);
const Key &key() const { return _key; }
@ -50,41 +54,11 @@ private:
base::optional<int> _skippedBefore;
int _skippedAfter = 0;
friend class UserPhotosViewer;
friend class UserPhotosSliceBuilder;
};
class UserPhotosViewer :
private base::Subscriber,
public base::enable_weak_from_this {
public:
using Key = Storage::UserPhotosKey;
UserPhotosViewer(Key key, int limitBefore, int limitAfter);
void start();
base::Observable<UserPhotosSlice> updated;
private:
using InitialResult = Storage::UserPhotosResult;
using SliceUpdate = Storage::UserPhotosSliceUpdate;
void loadInitial();
void requestPhotos();
void applyStoredResult(InitialResult &&result);
void applyUpdate(const SliceUpdate &update);
void sliceToLimits();
void mergeSliceData(
base::optional<int> count,
const std::deque<PhotoId> &photoIds,
base::optional<int> skippedBefore,
int skippedAfter);
Key _key;
int _limitBefore = 0;
int _limitAfter = 0;
UserPhotosSlice _data;
};
rpl::producer<UserPhotosSlice> UserPhotosViewer(
UserPhotosSlice::Key key,
int limitBefore,
int limitAfter);

View File

@ -78,13 +78,12 @@ struct MediaView::SharedMedia {
};
struct MediaView::UserPhotos {
UserPhotos(UserPhotosViewer::Key key)
: key(key)
, slice(key, kIdsLimit, kIdsLimit) {
UserPhotos(UserPhotosSlice::Key key)
: key(key) {
}
UserPhotosViewer::Key key;
UserPhotosViewer slice;
UserPhotosSlice::Key key;
rpl::lifetime lifetime;
};
MediaView::MediaView() : TWidget(nullptr)
@ -1075,7 +1074,7 @@ void MediaView::validateSharedMedia() {
}
void MediaView::handleSharedMediaUpdate(const SharedMediaSliceWithLast &update) {
if (isHidden() || (!_photo && !_doc) || !_sharedMedia) {
if ((!_photo && !_doc) || !_sharedMedia) {
_sharedMediaData = base::none;
} else {
_sharedMediaData = update;
@ -1120,21 +1119,24 @@ bool MediaView::validUserPhotos() const {
void MediaView::validateUserPhotos() {
if (auto key = userPhotosKey()) {
_userPhotos = std::make_unique<UserPhotos>(*key);
subscribe(_userPhotos->slice.updated, [this](const UserPhotosSlice &data) {
handleUserPhotosUpdate(data);
});
_userPhotos->slice.start();
UserPhotosViewer(
*key,
kIdsLimit,
kIdsLimit
) | rpl::on_next([this](UserPhotosSlice &&update) {
handleUserPhotosUpdate(std::move(update));
}) | rpl::start(_userPhotos->lifetime);
} else {
_userPhotos = nullptr;
_userPhotosData = base::none;
}
}
void MediaView::handleUserPhotosUpdate(const UserPhotosSlice &update) {
if (isHidden() || !_photo || !_userPhotos) {
void MediaView::handleUserPhotosUpdate(UserPhotosSlice &&update) {
if (!_photo || !_userPhotos) {
_userPhotosData = base::none;
} else {
_userPhotosData = update;
_userPhotosData = std::move(update);
}
findCurrent();
updateControls();

View File

@ -178,11 +178,11 @@ private:
void handleSharedMediaUpdate(const SharedMediaSliceWithLast &update);
struct UserPhotos;
using UserPhotosKey = UserPhotosViewer::Key;
using UserPhotosKey = UserPhotosSlice::Key;
base::optional<UserPhotosKey> userPhotosKey() const;
bool validUserPhotos() const;
void validateUserPhotos();
void handleUserPhotosUpdate(const UserPhotosSlice &update);
void handleUserPhotosUpdate(UserPhotosSlice &&update);
void refreshMediaViewer();
void refreshNavVisibility();

View File

@ -44,11 +44,9 @@ public:
void add(UserPhotosAddSlice &&query);
void remove(UserPhotosRemoveOne &&query);
void remove(UserPhotosRemoveAfter &&query);
void query(
UserPhotosQuery &&query,
base::lambda_once<void(UserPhotosResult&&)> &&callback);
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
base::Observable<UserPhotosSliceUpdate> &userPhotosSliceUpdated();
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() const;
private:
SharedMedia _sharedMedia;
@ -110,14 +108,12 @@ void Facade::Impl::remove(UserPhotosRemoveAfter &&query) {
return _userPhotos.remove(std::move(query));
}
void Facade::Impl::query(
UserPhotosQuery &&query,
base::lambda_once<void(UserPhotosResult&&)> &&callback) {
return _userPhotos.query(std::move(query), std::move(callback));
rpl::producer<UserPhotosResult> Facade::Impl::query(UserPhotosQuery &&query) const {
return _userPhotos.query(std::move(query));
}
base::Observable<UserPhotosSliceUpdate> &Facade::Impl::userPhotosSliceUpdated() {
return _userPhotos.sliceUpdated;
rpl::producer<UserPhotosSliceUpdate> Facade::Impl::userPhotosSliceUpdated() const {
return _userPhotos.sliceUpdated();
}
Facade::Facade() : _impl(std::make_unique<Impl>()) {
@ -177,13 +173,11 @@ void Facade::remove(UserPhotosRemoveAfter &&query) {
return _impl->remove(std::move(query));
}
void Facade::query(
UserPhotosQuery &&query,
base::lambda_once<void(UserPhotosResult&&)> &&callback) {
return _impl->query(std::move(query), std::move(callback));
rpl::producer<UserPhotosResult> Facade::query(UserPhotosQuery &&query) const {
return _impl->query(std::move(query));
}
base::Observable<UserPhotosSliceUpdate> &Facade::userPhotosSliceUpdated() {
rpl::producer<UserPhotosSliceUpdate> Facade::userPhotosSliceUpdated() const {
return _impl->userPhotosSliceUpdated();
}

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "base/enum_mask.h"
#include "rpl/producer.h"
namespace Storage {
@ -62,11 +63,9 @@ public:
void add(UserPhotosAddSlice &&query);
void remove(UserPhotosRemoveOne &&query);
void remove(UserPhotosRemoveAfter &&query);
void query(
UserPhotosQuery &&query,
base::lambda_once<void(UserPhotosResult&&)> &&callback);
base::Observable<UserPhotosSliceUpdate> &userPhotosSliceUpdated();
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
rpl::producer<UserPhotosSliceUpdate> userPhotosSliceUpdated() const;
~Facade();

View File

@ -81,38 +81,37 @@ void UserPhotos::List::sendUpdate() {
auto update = SliceUpdate();
update.photoIds = &_photoIds;
update.count = _count;
sliceUpdated.notify(update, true);
_sliceUpdated.fire(std::move(update));
}
void UserPhotos::List::query(
const UserPhotosQuery &query,
base::lambda_once<void(UserPhotosResult&&)> &&callback) {
auto result = UserPhotosResult {};
result.count = _count;
rpl::producer<UserPhotosResult> UserPhotos::List::query(
UserPhotosQuery &&query) const {
return [this, query = std::move(query)](auto consumer) {
auto result = UserPhotosResult {};
result.count = _count;
auto position = base::find(_photoIds, query.key.photoId);
if (position != _photoIds.end()) {
auto haveBefore = int(position - _photoIds.begin());
auto haveEqualOrAfter = int(_photoIds.end() - position);
auto before = qMin(haveBefore, query.limitBefore);
auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1);
result.photoIds = std::deque<PhotoId>(
position - before,
position + equalOrAfter);
auto position = base::find(_photoIds, query.key.photoId);
if (position != _photoIds.end()) {
auto haveBefore = int(position - _photoIds.begin());
auto haveEqualOrAfter = int(_photoIds.end() - position);
auto before = qMin(haveBefore, query.limitBefore);
auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1);
result.photoIds = std::deque<PhotoId>(
position - before,
position + equalOrAfter);
auto skippedInIds = (haveBefore - before);
result.skippedBefore = _count
| func::add(-int(_photoIds.size()) + skippedInIds);
result.skippedBefore = haveBefore - before;
result.skippedAfter = (haveEqualOrAfter - equalOrAfter);
}
base::TaskQueue::Main().Put(
[
callback = std::move(callback),
result = std::move(result)
]() mutable {
callback(std::move(result));
});
auto skippedInIds = (haveBefore - before);
result.skippedBefore = _count
| func::add(-int(_photoIds.size()) + skippedInIds);
result.skippedBefore = haveBefore - before;
result.skippedAfter = (haveEqualOrAfter - equalOrAfter);
consumer.put_next(std::move(result));
} else if (_count) {
consumer.put_next(std::move(result));
}
consumer.put_done();
return rpl::lifetime();
};
}
std::map<UserId, UserPhotos::List>::iterator
@ -122,12 +121,13 @@ UserPhotos::enforceLists(UserId user) {
return result;
}
result = _lists.emplace(user, List {}).first;
subscribe(result->second.sliceUpdated, [this, user](const SliceUpdate &update) {
sliceUpdated.notify(UserPhotosSliceUpdate(
result->second.sliceUpdated(
) | rpl::on_next([this, user](SliceUpdate &&update) {
_sliceUpdated.fire(UserPhotosSliceUpdate(
user,
update.photoIds,
update.count), true);
});
update.count));
}) | rpl::start(_lifetime);
return result;
}
@ -157,20 +157,15 @@ void UserPhotos::remove(UserPhotosRemoveAfter &&query) {
}
}
void UserPhotos::query(
const UserPhotosQuery &query,
base::lambda_once<void(UserPhotosResult&&)> &&callback) {
rpl::producer<UserPhotosResult> UserPhotos::query(UserPhotosQuery &&query) const {
auto userIt = _lists.find(query.key.userId);
if (userIt != _lists.end()) {
userIt->second.query(query, std::move(callback));
} else {
base::TaskQueue::Main().Put(
[
callback = std::move(callback)
]() mutable {
callback(UserPhotosResult());
});
return userIt->second.query(std::move(query));
}
return [](auto consumer) {
consumer.put_done();
return rpl::lifetime();
};
}
} // namespace Storage

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "storage/storage_facade.h"
#include "rpl/event_stream.h"
namespace Storage {
@ -135,17 +136,18 @@ struct UserPhotosSliceUpdate {
base::optional<int> count;
};
class UserPhotos : private base::Subscriber {
class UserPhotos {
public:
void add(UserPhotosAddNew &&query);
void add(UserPhotosAddSlice &&query);
void remove(UserPhotosRemoveOne &&query);
void remove(UserPhotosRemoveAfter &&query);
void query(
const UserPhotosQuery &query,
base::lambda_once<void(UserPhotosResult&&)> &&callback);
base::Observable<UserPhotosSliceUpdate> sliceUpdated;
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
rpl::producer<UserPhotosSliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
private:
class List {
@ -156,15 +158,15 @@ private:
int count);
void removeOne(PhotoId photoId);
void removeAfter(PhotoId photoId);
void query(
const UserPhotosQuery &query,
base::lambda_once<void(UserPhotosResult&&)> &&callback);
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
struct SliceUpdate {
const std::deque<PhotoId> *photoIds = nullptr;
base::optional<int> count;
};
base::Observable<SliceUpdate> sliceUpdated;
rpl::producer<SliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
private:
void sendUpdate();
@ -172,6 +174,8 @@ private:
base::optional<int> _count;
std::deque<PhotoId> _photoIds;
rpl::event_stream<SliceUpdate> _sliceUpdated;
};
using SliceUpdate = List::SliceUpdate;
@ -179,6 +183,9 @@ private:
std::map<UserId, List> _lists;
rpl::lifetime _lifetime;
rpl::event_stream<UserPhotosSliceUpdate> _sliceUpdated;
};
} // namespace Storage