Save media search state to memento.

This commit is contained in:
John Preston 2017-11-03 19:47:08 +04:00
parent 09d1e3629a
commit 39c5898fa4
11 changed files with 138 additions and 25 deletions

View File

@ -299,6 +299,41 @@ rpl::producer<SparseIdsSlice> SearchController::simpleIdsSlice(
}; };
} }
auto SearchController::saveState() -> SavedState {
auto result = SavedState();
if (_current != _cache.end()) {
result.query = _current->first;
result.peerList = std::move(_current->second->peerData.list);
if (auto &migrated = _current->second->migratedData) {
result.migratedList = std::move(migrated->list);
}
}
return result;
}
void SearchController::restoreState(SavedState &&state) {
if (!state.query.peerId) {
return;
}
auto it = _cache.find(state.query);
if (it == _cache.end()) {
it = _cache.emplace(
state.query,
std::make_unique<CacheEntry>(state.query)).first;
}
auto replace = Data(it->second->peerData.peer);
replace.list = std::move(state.peerList);
it->second->peerData = std::move(replace);
if (auto &migrated = state.migratedList) {
Assert(it->second->migratedData.has_value());
auto replace = Data(it->second->migratedData->peer);
replace.list = std::move(*migrated);
it->second->migratedData = std::move(replace);
}
_current = it;
}
void SearchController::requestMore( void SearchController::requestMore(
const SparseIdsSliceBuilder::AroundData &key, const SparseIdsSliceBuilder::AroundData &key,
const Query &query, const Query &query,
@ -341,6 +376,10 @@ void DelayedSearchController::setQuery(const Query &query) {
void DelayedSearchController::setQuery( void DelayedSearchController::setQuery(
const Query &query, const Query &query,
TimeMs delay) { TimeMs delay) {
if (currentQuery() == query) {
_timer.cancel();
return;
}
if (_controller.hasInCache(query)) { if (_controller.hasInCache(query)) {
setQueryFast(query); setQueryFast(query);
} else { } else {

View File

@ -50,6 +50,7 @@ SearchResult ParseSearchResult(
class SearchController : private MTP::Sender { class SearchController : private MTP::Sender {
public: public:
using IdsList = Storage::SparseIdsList;
struct Query { struct Query {
using MediaType = Storage::SharedMediaType; using MediaType = Storage::SharedMediaType;
@ -68,6 +69,11 @@ public:
} }
}; };
struct SavedState {
Query query;
IdsList peerList;
base::optional<IdsList> migratedList;
};
void setQuery(const Query &query); void setQuery(const Query &query);
bool hasInCache(const Query &query) const; bool hasInCache(const Query &query) const;
@ -82,13 +88,16 @@ public:
int limitBefore, int limitBefore,
int limitAfter); int limitAfter);
SavedState saveState();
void restoreState(SavedState &&state);
private: private:
struct Data { struct Data {
explicit Data(not_null<PeerData*> peer) : peer(peer) { explicit Data(not_null<PeerData*> peer) : peer(peer) {
} }
not_null<PeerData*> peer; not_null<PeerData*> peer;
Storage::SparseIdsList list; IdsList list;
base::flat_map< base::flat_map<
SparseIdsSliceBuilder::AroundData, SparseIdsSliceBuilder::AroundData,
rpl::lifetime> requests; rpl::lifetime> requests;
@ -133,6 +142,8 @@ public:
DelayedSearchController(); DelayedSearchController();
using Query = SearchController::Query; using Query = SearchController::Query;
using SavedState = SearchController::SavedState;
void setQuery(const Query &query); void setQuery(const Query &query);
void setQuery(const Query &query, TimeMs delay); void setQuery(const Query &query, TimeMs delay);
void setQueryFast(const Query &query); void setQueryFast(const Query &query);
@ -155,6 +166,14 @@ public:
return _sourceChanges.events(); return _sourceChanges.events();
} }
SavedState saveState() {
return _controller.saveState();
}
void restoreState(SavedState &&state) {
_controller.restoreState(std::move(state));
}
private: private:
SearchController _controller; SearchController _controller;
Query _nextQuery; Query _nextQuery;

View File

@ -88,17 +88,18 @@ infoLayerMediaSearch: SearchFieldRow(infoMediaSearch) {
fieldCancelSkip: 46px; fieldCancelSkip: 46px;
} }
infoTopBarSearchRow: SearchFieldRow(infoLayerMediaSearch) { infoTopBarSearchRow: SearchFieldRow(infoLayerMediaSearch) {
height: 52px;
padding: margins(0px, 12px, 8px, 10px); padding: margins(0px, 12px, 8px, 10px);
fieldCancel: CrossButton(contactsSearchCancel) { fieldCancel: CrossButton(contactsSearchCancel) {
width: 50px; width: 51px;
height: 52px; height: 52px;
cross: CrossAnimation { cross: CrossAnimation {
size: 38px; size: 42px;
skip: 12px; skip: 14px;
stroke: 2px; stroke: 2px;
minScale: 0.3; minScale: 0.3;
} }
crossPosition: point(3px, 8px); crossPosition: point(1px, 6px);
} }
} }

View File

@ -143,11 +143,18 @@ public:
int scrollTop() const { int scrollTop() const {
return _scrollTop; return _scrollTop;
} }
void setSearchFieldQuery(const QString &query) {
_searchFieldQuery = query;
}
QString searchFieldQuery() const {
return _searchFieldQuery;
}
private: private:
const PeerId _peerId = 0; const PeerId _peerId = 0;
const PeerId _migratedPeerId = 0; const PeerId _migratedPeerId = 0;
int _scrollTop = 0; int _scrollTop = 0;
QString _searchFieldQuery;
}; };

View File

@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_shared_media.h" #include "history/history_shared_media.h"
#include "info/info_content_widget.h" #include "info/info_content_widget.h"
#include "info/info_memento.h" #include "info/info_memento.h"
#include "info/media/info_media_widget.h"
namespace Info { namespace Info {
namespace { namespace {
@ -50,7 +51,7 @@ Controller::Controller(
: nullptr) : nullptr)
, _window(window) , _window(window)
, _section(memento->section()) { , _section(memento->section()) {
updateSearchControllers(); updateSearchControllers(memento);
} }
Wrap Controller::wrap() const { Wrap Controller::wrap() const {
@ -71,12 +72,13 @@ bool Controller::validateMementoPeer(
&& memento->migratedPeerId() == migratedPeerId(); && memento->migratedPeerId() == migratedPeerId();
} }
void Controller::setSection(const Section &section) { void Controller::setSection(not_null<ContentMemento*> memento) {
_section = section; _section = memento->section();
updateSearchControllers(); updateSearchControllers(memento);
} }
void Controller::updateSearchControllers() { void Controller::updateSearchControllers(
not_null<ContentMemento*> memento) {
auto isMedia = (_section.type() == Section::Type::Media); auto isMedia = (_section.type() == Section::Type::Media);
auto mediaType = isMedia auto mediaType = isMedia
? _section.mediaType() ? _section.mediaType()
@ -85,16 +87,21 @@ void Controller::updateSearchControllers() {
&& SharedMediaAllowSearch(mediaType); && SharedMediaAllowSearch(mediaType);
// auto hasCommonGroupsSearch // auto hasCommonGroupsSearch
// = (_section.type() == Section::Type::CommonGroups); // = (_section.type() == Section::Type::CommonGroups);
auto searchQuery = memento->searchFieldQuery();
if (isMedia) { if (isMedia) {
_searchController _searchController
= std::make_unique<Api::DelayedSearchController>(); = std::make_unique<Api::DelayedSearchController>();
_searchController->setQueryFast(produceSearchQuery()); auto mediaMemento = dynamic_cast<Media::Memento*>(memento.get());
Assert(mediaMemento != nullptr);
_searchController->restoreState(
mediaMemento->searchState());
} else { } else {
_searchController = nullptr; _searchController = nullptr;
} }
if (hasMediaSearch) { if (hasMediaSearch) {
_searchFieldController _searchFieldController
= std::make_unique<Ui::SearchFieldController>(); = std::make_unique<Ui::SearchFieldController>(
searchQuery);
_searchFieldController->queryValue() _searchFieldController->queryValue()
| rpl::start_with_next([=](QString &&query) { | rpl::start_with_next([=](QString &&query) {
_searchController->setQuery( _searchController->setQuery(
@ -105,12 +112,25 @@ void Controller::updateSearchControllers() {
} }
} }
void Controller::saveSearchState(not_null<ContentMemento*> memento) {
if (_searchFieldController) {
memento->setSearchFieldQuery(
_searchFieldController->query());
}
if (_searchController) {
auto mediaMemento = dynamic_cast<Media::Memento*>(
memento.get());
Assert(mediaMemento != nullptr);
mediaMemento->setSearchState(_searchController->saveState());
}
}
auto Controller::produceSearchQuery( auto Controller::produceSearchQuery(
QString &&query) const -> SearchQuery { const QString &query) const -> SearchQuery {
auto result = SearchQuery(); auto result = SearchQuery();
result.type = _section.mediaType(); result.type = _section.mediaType();
result.peerId = _peer->id; result.peerId = _peer->id;
result.query = std::move(query); result.query = query;
result.migratedPeerId = _migrated ? _migrated->id : PeerId(0); result.migratedPeerId = _migrated ? _migrated->id : PeerId(0);
return result; return result;
} }

View File

@ -97,7 +97,7 @@ public:
Wrap wrap() const; Wrap wrap() const;
rpl::producer<Wrap> wrapValue() const; rpl::producer<Wrap> wrapValue() const;
void setSection(const Section &section); void setSection(not_null<ContentMemento*> memento);
bool hasStackHistory() const; bool hasStackHistory() const;
not_null<Window::Controller*> window() const { not_null<Window::Controller*> window() const {
@ -114,6 +114,8 @@ public:
return _searchController->sourceChanged(); return _searchController->sourceChanged();
} }
void saveSearchState(not_null<ContentMemento*> memento);
rpl::lifetime &lifetime() { rpl::lifetime &lifetime() {
return _lifetime; return _lifetime;
} }
@ -123,9 +125,8 @@ public:
private: private:
using SearchQuery = Api::DelayedSearchController::Query; using SearchQuery = Api::DelayedSearchController::Query;
void updateSearchControllers(); void updateSearchControllers(not_null<ContentMemento*> memento);
SearchQuery produceSearchQuery( SearchQuery produceSearchQuery(const QString &query) const;
QString &&query = QString()) const;
not_null<WrapWidget*> _widget; not_null<WrapWidget*> _widget;
not_null<PeerData*> _peer; not_null<PeerData*> _peer;

View File

@ -189,7 +189,7 @@ bool InnerWidget::showInternal(not_null<Memento*> memento) {
void InnerWidget::switchToTab(Memento &&memento) { void InnerWidget::switchToTab(Memento &&memento) {
// Save state of the tab before setSection() call. // Save state of the tab before setSection() call.
_controller->setSection(memento.section()); _controller->setSection(&memento);
_list = setupList(); _list = setupList();
restoreState(&memento); restoreState(&memento);
_list->show(); _list->show();

View File

@ -54,6 +54,17 @@ Memento::Memento(not_null<Controller*> controller)
controller->section().mediaType()) { controller->section().mediaType()) {
} }
Memento::Memento(PeerId peerId, PeerId migratedPeerId, Type type)
: ContentMemento(peerId, migratedPeerId)
, _type(type) {
_searchState.query.type = type;
_searchState.query.peerId = peerId;
_searchState.query.migratedPeerId = migratedPeerId;
if (migratedPeerId) {
_searchState.migratedList = Storage::SparseIdsList();
}
}
Section Memento::section() const { Section Memento::section() const {
return Section(_type); return Section(_type);
} }
@ -119,7 +130,7 @@ std::unique_ptr<ContentMemento> Widget::createMemento() {
} }
void Widget::saveState(not_null<Memento*> memento) { void Widget::saveState(not_null<Memento*> memento) {
memento->setScrollTop(scrollTopSave()); controller()->saveSearchState(memento);
_inner->saveState(memento); _inner->saveState(memento);
} }

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/producer.h> #include <rpl/producer.h>
#include "info/info_content_widget.h" #include "info/info_content_widget.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "history/history_search_controller.h"
namespace Ui { namespace Ui {
class SearchFieldController; class SearchFieldController;
@ -41,11 +42,9 @@ class InnerWidget;
class Memento final : public ContentMemento { class Memento final : public ContentMemento {
public: public:
Memento(not_null<Controller*> controller); Memento(not_null<Controller*> controller);
Memento(PeerId peerId, PeerId migratedPeerId, Type type);
Memento(PeerId peerId, PeerId migratedPeerId, Type type) using SearchState = Api::DelayedSearchController::SavedState;
: ContentMemento(peerId, migratedPeerId)
, _type(type) {
}
object_ptr<ContentWidget> createWidget( object_ptr<ContentWidget> createWidget(
QWidget *parent, QWidget *parent,
@ -82,13 +81,20 @@ public:
int scrollTopShift() const { int scrollTopShift() const {
return _scrollTopShift; return _scrollTopShift;
} }
void setSearchState(SearchState &&state) {
_searchState = std::move(state);
}
SearchState searchState() {
return std::move(_searchState);
}
private: private:
Type _type = Type::Photo; Type _type = Type::Photo;
FullMsgId _aroundId; FullMsgId _aroundId;
int _idsLimit = 0; int _idsLimit = 0;
FullMsgId _scrollTopItem; FullMsgId _scrollTopItem;
int _scrollTopShift = 0;; int _scrollTopShift = 0;
SearchState _searchState;
}; };

View File

@ -29,6 +29,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Ui { namespace Ui {
SearchFieldController::SearchFieldController(const QString &query)
: _query(query) {
}
base::unique_qptr<Ui::RpWidget> SearchFieldController::createRowView( base::unique_qptr<Ui::RpWidget> SearchFieldController::createRowView(
QWidget *parent, QWidget *parent,
const style::SearchFieldRow &st) { const style::SearchFieldRow &st) {

View File

@ -36,6 +36,8 @@ class InputField;
class SearchFieldController { class SearchFieldController {
public: public:
SearchFieldController(const QString &query);
base::unique_qptr<Ui::InputField> createField( base::unique_qptr<Ui::InputField> createField(
QWidget *parent, QWidget *parent,
const style::InputField &st); const style::InputField &st);
@ -43,6 +45,9 @@ public:
QWidget *parent, QWidget *parent,
const style::SearchFieldRow &st); const style::SearchFieldRow &st);
QString query() const {
return _query.current();
}
rpl::producer<QString> queryValue() const { rpl::producer<QString> queryValue() const {
return _query.value(); return _query.value();
} }