mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 23:53:58 +02:00
Add search to the Downloads section.
This commit is contained in:
parent
5be72e8ce2
commit
95f5f28906
9 changed files with 148 additions and 16 deletions
|
@ -127,7 +127,7 @@ object_ptr<Media::ListWidget> InnerWidget::setupList() {
|
||||||
result->lifetime());
|
result->lifetime());
|
||||||
_selectedLists.fire(result->selectedListValue());
|
_selectedLists.fire(result->selectedListValue());
|
||||||
_listTops.fire(result->topValue());
|
_listTops.fire(result->topValue());
|
||||||
_controller->mediaSourceQueryValue(
|
_controller->searchQueryValue(
|
||||||
) | rpl::start_with_next([this](const QString &query) {
|
) | rpl::start_with_next([this](const QString &query) {
|
||||||
_empty->setSearchQuery(query);
|
_empty->setSearchQuery(query);
|
||||||
}, result->lifetime());
|
}, result->lifetime());
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "info/media/info_media_widget.h"
|
#include "info/media/info_media_widget.h"
|
||||||
#include "info/media/info_media_list_section.h"
|
#include "info/media/info_media_list_section.h"
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
|
#include "ui/text/format_song_document_name.h"
|
||||||
#include "data/data_download_manager.h"
|
#include "data/data_download_manager.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
|
@ -70,7 +71,11 @@ bool Provider::isPossiblyMyItem(not_null<const HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> Provider::fullCount() {
|
std::optional<int> Provider::fullCount() {
|
||||||
return _fullCount;
|
return _queryWords.empty()
|
||||||
|
? _fullCount
|
||||||
|
: (_foundCount || _fullCount.has_value())
|
||||||
|
? _foundCount
|
||||||
|
: std::optional<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Provider::restart() {
|
void Provider::restart() {
|
||||||
|
@ -84,6 +89,27 @@ void Provider::checkPreload(
|
||||||
bool preloadBottom) {
|
bool preloadBottom) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Provider::setSearchQuery(QString query) {
|
||||||
|
if (_query == query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_query = query;
|
||||||
|
auto words = TextUtilities::PrepareSearchWords(_query);
|
||||||
|
if (!_started || _queryWords == words) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_queryWords = std::move(words);
|
||||||
|
if (searchMode()) {
|
||||||
|
_foundCount = 0;
|
||||||
|
for (auto &element : _elements) {
|
||||||
|
if ((element.found = computeIsFound(element))) {
|
||||||
|
++_foundCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_refreshed.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
void Provider::refreshViewer() {
|
void Provider::refreshViewer() {
|
||||||
if (_started) {
|
if (_started) {
|
||||||
return;
|
return;
|
||||||
|
@ -99,7 +125,7 @@ void Provider::refreshViewer() {
|
||||||
const auto item = id->object.item;
|
const auto item = id->object.item;
|
||||||
if (!copy.remove(item) && !_downloaded.contains(item)) {
|
if (!copy.remove(item) && !_downloaded.contains(item)) {
|
||||||
_downloading.emplace(item);
|
_downloading.emplace(item);
|
||||||
_elements.push_back({
|
addElementNow({
|
||||||
.item = item,
|
.item = item,
|
||||||
.started = id->started,
|
.started = id->started,
|
||||||
.path = id->path,
|
.path = id->path,
|
||||||
|
@ -180,21 +206,37 @@ void Provider::performAdd() {
|
||||||
for (auto &element : base::take(_addPostponed)) {
|
for (auto &element : base::take(_addPostponed)) {
|
||||||
_downloaded.emplace(element.item);
|
_downloaded.emplace(element.item);
|
||||||
if (!_downloading.remove(element.item)) {
|
if (!_downloading.remove(element.item)) {
|
||||||
_elements.push_back(std::move(element));
|
addElementNow(std::move(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshPostponed(true);
|
refreshPostponed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Provider::addElementNow(Element &&element) {
|
||||||
|
_elements.push_back(std::move(element));
|
||||||
|
auto &added = _elements.back();
|
||||||
|
fillSearchIndex(added);
|
||||||
|
added.found = searchMode() && computeIsFound(added);
|
||||||
|
if (added.found) {
|
||||||
|
++_foundCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Provider::remove(not_null<const HistoryItem*> item) {
|
void Provider::remove(not_null<const HistoryItem*> item) {
|
||||||
_addPostponed.erase(
|
_addPostponed.erase(
|
||||||
ranges::remove(_addPostponed, item, &Element::item),
|
ranges::remove(_addPostponed, item, &Element::item),
|
||||||
end(_addPostponed));
|
end(_addPostponed));
|
||||||
_downloading.remove(item);
|
_downloading.remove(item);
|
||||||
_downloaded.remove(item);
|
_downloaded.remove(item);
|
||||||
_elements.erase(
|
const auto proj = [&](const Element &element) {
|
||||||
ranges::remove(_elements, item, &Element::item),
|
if (element.item != item) {
|
||||||
end(_elements));
|
return false;
|
||||||
|
} else if (element.found && searchMode()) {
|
||||||
|
--_foundCount;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
_elements.erase(ranges::remove_if(_elements, proj), end(_elements));
|
||||||
if (const auto i = _layouts.find(item); i != end(_layouts)) {
|
if (const auto i = _layouts.find(item); i != end(_layouts)) {
|
||||||
_layoutRemoved.fire(i->second.item.get());
|
_layoutRemoved.fire(i->second.item.get());
|
||||||
_layouts.erase(i);
|
_layouts.erase(i);
|
||||||
|
@ -252,10 +294,14 @@ rpl::producer<> Provider::refreshed() {
|
||||||
|
|
||||||
std::vector<ListSection> Provider::fillSections(
|
std::vector<ListSection> Provider::fillSections(
|
||||||
not_null<Overview::Layout::Delegate*> delegate) {
|
not_null<Overview::Layout::Delegate*> delegate) {
|
||||||
markLayoutsStale();
|
const auto search = searchMode();
|
||||||
|
|
||||||
|
if (!search) {
|
||||||
|
markLayoutsStale();
|
||||||
|
}
|
||||||
const auto guard = gsl::finally([&] { clearStaleLayouts(); });
|
const auto guard = gsl::finally([&] { clearStaleLayouts(); });
|
||||||
|
|
||||||
if (_elements.empty()) {
|
if (_elements.empty() || (search && !_foundCount)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +310,9 @@ std::vector<ListSection> Provider::fillSections(
|
||||||
ListSection(Type::File, sectionDelegate()));
|
ListSection(Type::File, sectionDelegate()));
|
||||||
auto §ion = result.back();
|
auto §ion = result.back();
|
||||||
for (const auto &element : ranges::views::reverse(_elements)) {
|
for (const auto &element : ranges::views::reverse(_elements)) {
|
||||||
if (auto layout = getLayout(element, delegate)) {
|
if (search && !element.found) {
|
||||||
|
continue;
|
||||||
|
} else if (auto layout = getLayout(element, delegate)) {
|
||||||
section.addItem(layout);
|
section.addItem(layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,6 +364,47 @@ bool Provider::isAfter(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Provider::searchMode() const {
|
||||||
|
return !_queryWords.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Provider::fillSearchIndex(Element &element) {
|
||||||
|
auto strings = QStringList(QFileInfo(element.path).fileName());
|
||||||
|
if (const auto media = element.item->media()) {
|
||||||
|
if (const auto document = media->document()) {
|
||||||
|
strings.append(document->filename());
|
||||||
|
strings.append(Ui::Text::FormatDownloadsName(document).text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
element.words = TextUtilities::PrepareSearchWords(strings.join(' '));
|
||||||
|
element.letters.clear();
|
||||||
|
for (const auto &word : element.words) {
|
||||||
|
element.letters.emplace(word.front());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Provider::computeIsFound(const Element &element) const {
|
||||||
|
Expects(!_queryWords.empty());
|
||||||
|
|
||||||
|
const auto has = [&](const QString &queryWord) {
|
||||||
|
if (!element.letters.contains(queryWord.front())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto &word : element.words) {
|
||||||
|
if (word.startsWith(queryWord)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
for (const auto &queryWord : _queryWords) {
|
||||||
|
if (!has(queryWord)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Provider::itemRemoved(not_null<const HistoryItem*> item) {
|
void Provider::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
remove(item);
|
remove(item);
|
||||||
}
|
}
|
||||||
|
@ -393,9 +482,13 @@ void Provider::applyDragSelection(
|
||||||
selected.clear();
|
selected.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto search = !_queryWords.isEmpty();
|
||||||
auto chosen = base::flat_set<not_null<const HistoryItem*>>();
|
auto chosen = base::flat_set<not_null<const HistoryItem*>>();
|
||||||
chosen.reserve(till - from);
|
chosen.reserve(till - from);
|
||||||
for (auto i = from; i != till; ++i) {
|
for (auto i = from; i != till; ++i) {
|
||||||
|
if (search && !i->found) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const auto item = i->item;
|
const auto item = i->item;
|
||||||
chosen.emplace(item);
|
chosen.emplace(item);
|
||||||
ChangeItemSelection(
|
ChangeItemSelection(
|
||||||
|
|
|
@ -44,6 +44,8 @@ public:
|
||||||
void refreshViewer() override;
|
void refreshViewer() override;
|
||||||
rpl::producer<> refreshed() override;
|
rpl::producer<> refreshed() override;
|
||||||
|
|
||||||
|
void setSearchQuery(QString query);
|
||||||
|
|
||||||
std::vector<Media::ListSection> fillSections(
|
std::vector<Media::ListSection> fillSections(
|
||||||
not_null<Overview::Layout::Delegate*> delegate) override;
|
not_null<Overview::Layout::Delegate*> delegate) override;
|
||||||
rpl::producer<not_null<Media::BaseLayout*>> layoutRemoved() override;
|
rpl::producer<not_null<Media::BaseLayout*>> layoutRemoved() override;
|
||||||
|
@ -86,6 +88,10 @@ private:
|
||||||
not_null<HistoryItem*> item;
|
not_null<HistoryItem*> item;
|
||||||
int64 started = 0; // unixtime * 1000
|
int64 started = 0; // unixtime * 1000
|
||||||
QString path;
|
QString path;
|
||||||
|
|
||||||
|
QStringList words;
|
||||||
|
base::flat_set<QChar> letters;
|
||||||
|
bool found = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool sectionHasFloatingHeader() override;
|
bool sectionHasFloatingHeader() override;
|
||||||
|
@ -94,6 +100,10 @@ private:
|
||||||
not_null<const Media::BaseLayout*> item,
|
not_null<const Media::BaseLayout*> item,
|
||||||
not_null<const Media::BaseLayout*> previous) override;
|
not_null<const Media::BaseLayout*> previous) override;
|
||||||
|
|
||||||
|
[[nodiscard]] bool searchMode() const;
|
||||||
|
void fillSearchIndex(Element &element);
|
||||||
|
[[nodiscard]] bool computeIsFound(const Element &element) const;
|
||||||
|
|
||||||
void itemRemoved(not_null<const HistoryItem*> item);
|
void itemRemoved(not_null<const HistoryItem*> item);
|
||||||
void markLayoutsStale();
|
void markLayoutsStale();
|
||||||
void clearStaleLayouts();
|
void clearStaleLayouts();
|
||||||
|
@ -102,6 +112,7 @@ private:
|
||||||
void addPostponed(not_null<const Data::DownloadedId*> entry);
|
void addPostponed(not_null<const Data::DownloadedId*> entry);
|
||||||
void performRefresh();
|
void performRefresh();
|
||||||
void performAdd();
|
void performAdd();
|
||||||
|
void addElementNow(Element &&element);
|
||||||
void remove(not_null<const HistoryItem*> item);
|
void remove(not_null<const HistoryItem*> item);
|
||||||
void trackItemSession(not_null<const HistoryItem*> item);
|
void trackItemSession(not_null<const HistoryItem*> item);
|
||||||
|
|
||||||
|
@ -127,6 +138,10 @@ private:
|
||||||
rpl::event_stream<not_null<Media::BaseLayout*>> _layoutRemoved;
|
rpl::event_stream<not_null<Media::BaseLayout*>> _layoutRemoved;
|
||||||
rpl::event_stream<> _refreshed;
|
rpl::event_stream<> _refreshed;
|
||||||
|
|
||||||
|
QString _query;
|
||||||
|
QStringList _queryWords;
|
||||||
|
int _foundCount = 0;
|
||||||
|
|
||||||
base::flat_map<not_null<Main::Session*>, rpl::lifetime> _trackedSessions;
|
base::flat_map<not_null<Main::Session*>, rpl::lifetime> _trackedSessions;
|
||||||
bool _postponedRefreshSort = false;
|
bool _postponedRefreshSort = false;
|
||||||
bool _postponedRefresh = false;
|
bool _postponedRefresh = false;
|
||||||
|
|
|
@ -108,6 +108,10 @@ rpl::producer<QString> AbstractController::mediaSourceQueryValue() const {
|
||||||
return rpl::single(QString());
|
return rpl::single(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> AbstractController::searchQueryValue() const {
|
||||||
|
return rpl::single(QString());
|
||||||
|
}
|
||||||
|
|
||||||
AbstractController::AbstractController(
|
AbstractController::AbstractController(
|
||||||
not_null<Window::SessionController*> parent)
|
not_null<Window::SessionController*> parent)
|
||||||
: SessionNavigation(&parent->session())
|
: SessionNavigation(&parent->session())
|
||||||
|
@ -221,8 +225,8 @@ void Controller::updateSearchControllers(
|
||||||
: Section::MediaType::kCount;
|
: Section::MediaType::kCount;
|
||||||
const auto hasMediaSearch = isMedia
|
const auto hasMediaSearch = isMedia
|
||||||
&& SharedMediaAllowSearch(mediaType);
|
&& SharedMediaAllowSearch(mediaType);
|
||||||
const auto hasCommonGroupsSearch
|
const auto hasCommonGroupsSearch = (type == Type::CommonGroups);
|
||||||
= (type == Type::CommonGroups);
|
const auto hasDownloadsSearch = (type == Type::Downloads);
|
||||||
const auto hasMembersSearch = (type == Type::Members)
|
const auto hasMembersSearch = (type == Type::Members)
|
||||||
|| (type == Type::Profile);
|
|| (type == Type::Profile);
|
||||||
const auto searchQuery = memento->searchFieldQuery();
|
const auto searchQuery = memento->searchFieldQuery();
|
||||||
|
@ -236,7 +240,10 @@ void Controller::updateSearchControllers(
|
||||||
} else {
|
} else {
|
||||||
_searchController = nullptr;
|
_searchController = nullptr;
|
||||||
}
|
}
|
||||||
if (hasMediaSearch || hasCommonGroupsSearch || hasMembersSearch) {
|
if (hasMediaSearch
|
||||||
|
|| hasCommonGroupsSearch
|
||||||
|
|| hasDownloadsSearch
|
||||||
|
|| hasMembersSearch) {
|
||||||
_searchFieldController
|
_searchFieldController
|
||||||
= std::make_unique<Ui::SearchFieldController>(
|
= std::make_unique<Ui::SearchFieldController>(
|
||||||
searchQuery);
|
searchQuery);
|
||||||
|
@ -300,9 +307,11 @@ rpl::producer<bool> Controller::searchEnabledByContent() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<QString> Controller::mediaSourceQueryValue() const {
|
rpl::producer<QString> Controller::mediaSourceQueryValue() const {
|
||||||
return _searchController
|
return _searchController->currentQueryValue();
|
||||||
? _searchController->currentQueryValue()
|
}
|
||||||
: rpl::single(QString()); // #TODO downloads search
|
|
||||||
|
rpl::producer<QString> Controller::searchQueryValue() const {
|
||||||
|
return searchFieldController()->queryValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
|
rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
|
||||||
|
|
|
@ -141,6 +141,7 @@ public:
|
||||||
int limitBefore,
|
int limitBefore,
|
||||||
int limitAfter) const;
|
int limitAfter) const;
|
||||||
virtual rpl::producer<QString> mediaSourceQueryValue() const;
|
virtual rpl::producer<QString> mediaSourceQueryValue() const;
|
||||||
|
virtual rpl::producer<QString> searchQueryValue() const;
|
||||||
|
|
||||||
void showSection(
|
void showSection(
|
||||||
std::shared_ptr<Window::SectionMemento> memento,
|
std::shared_ptr<Window::SectionMemento> memento,
|
||||||
|
@ -198,6 +199,7 @@ public:
|
||||||
int limitBefore,
|
int limitBefore,
|
||||||
int limitAfter) const override;
|
int limitAfter) const override;
|
||||||
rpl::producer<QString> mediaSourceQueryValue() const override;
|
rpl::producer<QString> mediaSourceQueryValue() const override;
|
||||||
|
rpl::producer<QString> searchQueryValue() const override;
|
||||||
bool takeSearchStartsFocused() {
|
bool takeSearchStartsFocused() {
|
||||||
return base::take(_searchStartsFocused);
|
return base::take(_searchStartsFocused);
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,8 @@ public:
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
not_null<DocumentData*> document) = 0;
|
not_null<DocumentData*> document) = 0;
|
||||||
|
|
||||||
|
virtual void setSearchQuery(QString query) = 0;
|
||||||
|
|
||||||
[[nodiscard]] virtual int64 scrollTopStatePosition(
|
[[nodiscard]] virtual int64 scrollTopStatePosition(
|
||||||
not_null<HistoryItem*> item) = 0;
|
not_null<HistoryItem*> item) = 0;
|
||||||
[[nodiscard]] virtual HistoryItem *scrollTopStateItem(
|
[[nodiscard]] virtual HistoryItem *scrollTopStateItem(
|
||||||
|
|
|
@ -162,6 +162,11 @@ void ListWidget::start() {
|
||||||
|
|
||||||
if (_controller->isDownloads()) {
|
if (_controller->isDownloads()) {
|
||||||
_provider->refreshViewer();
|
_provider->refreshViewer();
|
||||||
|
|
||||||
|
_controller->searchQueryValue(
|
||||||
|
) | rpl::start_with_next([this](QString &&query) {
|
||||||
|
_provider->setSearchQuery(std::move(query));
|
||||||
|
}, lifetime());
|
||||||
} else {
|
} else {
|
||||||
trackSession(&session());
|
trackSession(&session());
|
||||||
|
|
||||||
|
|
|
@ -341,6 +341,10 @@ bool Provider::isAfter(
|
||||||
return (GetUniversalId(a) < GetUniversalId(b));
|
return (GetUniversalId(a) < GetUniversalId(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Provider::setSearchQuery(QString query) {
|
||||||
|
Unexpected("Media::Provider::setSearchQuery.");
|
||||||
|
}
|
||||||
|
|
||||||
SparseIdsMergedSlice::Key Provider::sliceKey(
|
SparseIdsMergedSlice::Key Provider::sliceKey(
|
||||||
UniversalMsgId universalId) const {
|
UniversalMsgId universalId) const {
|
||||||
using Key = SparseIdsMergedSlice::Key;
|
using Key = SparseIdsMergedSlice::Key;
|
||||||
|
|
|
@ -46,6 +46,8 @@ public:
|
||||||
not_null<const HistoryItem*> a,
|
not_null<const HistoryItem*> a,
|
||||||
not_null<const HistoryItem*> b) override;
|
not_null<const HistoryItem*> b) override;
|
||||||
|
|
||||||
|
void setSearchQuery(QString query) override;
|
||||||
|
|
||||||
ListItemSelectionData computeSelectionData(
|
ListItemSelectionData computeSelectionData(
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
TextSelection selection) override;
|
TextSelection selection) override;
|
||||||
|
|
Loading…
Add table
Reference in a new issue