From 5d757a6dc0ca3e3fcded6b7b915239f7093a468d Mon Sep 17 00:00:00 2001 From: Piotr Tanski Date: Tue, 1 Dec 2020 10:30:38 +0100 Subject: [PATCH] [EGD-4553] Notes search engine implemented. (#1112) --- changelog.md | 1 + image/assets/lang/English.json | 1 + .../application-notes/ApplicationNotes.cpp | 27 +++++-- .../application-notes/ApplicationNotes.hpp | 3 + module-apps/application-notes/CMakeLists.txt | 10 +++ .../application-notes/data/NotesFoundData.cpp | 22 ++++++ .../application-notes/data/NotesFoundData.hpp | 25 ++++++ .../model/NotesRepository.cpp | 17 +++++ .../model/NotesRepository.hpp | 7 +- .../model/SearchResultsListModel.cpp | 59 ++++++++++++++ .../model/SearchResultsListModel.hpp | 34 +++++++++ .../presenter/SearchEngineWindowPresenter.cpp | 21 +++++ .../presenter/SearchEngineWindowPresenter.hpp | 41 ++++++++++ .../windows/NoteMainWindow.cpp | 3 + .../windows/SearchEngineWindow.cpp | 61 +++++++++++++++ .../windows/SearchEngineWindow.hpp | 33 ++++++++ .../windows/SearchResultsWindow.cpp | 76 +++++++++++++++++++ .../windows/SearchResultsWindow.hpp | 34 +++++++++ module-db/CMakeLists.txt | 1 + module-db/Interface/NotesRecord.cpp | 30 +++++++- module-db/Interface/NotesRecord.hpp | 2 + module-db/Tables/NotesTable.cpp | 19 +++++ module-db/Tables/NotesTable.hpp | 1 + .../queries/notes/QueryNotesGetByText.cpp | 33 ++++++++ .../queries/notes/QueryNotesGetByText.hpp | 36 +++++++++ module-db/tests/NotesRecord_tests.cpp | 12 +++ module-db/tests/NotesTable_tests.cpp | 8 ++ 27 files changed, 605 insertions(+), 12 deletions(-) create mode 100644 module-apps/application-notes/data/NotesFoundData.cpp create mode 100644 module-apps/application-notes/data/NotesFoundData.hpp create mode 100644 module-apps/application-notes/model/SearchResultsListModel.cpp create mode 100644 module-apps/application-notes/model/SearchResultsListModel.hpp create mode 100644 module-apps/application-notes/presenter/SearchEngineWindowPresenter.cpp create mode 100644 module-apps/application-notes/presenter/SearchEngineWindowPresenter.hpp create mode 100644 module-apps/application-notes/windows/SearchEngineWindow.cpp create mode 100644 module-apps/application-notes/windows/SearchEngineWindow.hpp create mode 100644 module-apps/application-notes/windows/SearchResultsWindow.cpp create mode 100644 module-apps/application-notes/windows/SearchResultsWindow.hpp create mode 100644 module-db/queries/notes/QueryNotesGetByText.cpp create mode 100644 module-db/queries/notes/QueryNotesGetByText.hpp diff --git a/changelog.md b/changelog.md index 16728dd668f1d7491c7f53f89e8f89190294a86f..1a5a297d2ae03d3711a01cfff3c774b865f54b86 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ ### Added * `[notes]` Notes application implemented. +* `[notes]` Notes search engine implemented. ### Changed diff --git a/image/assets/lang/English.json b/image/assets/lang/English.json index 90249487c840d90b29a4a983d506a745feb02536..396ca916749329013bbfe4d54887c4a1f7b3cfd4 100644 --- a/image/assets/lang/English.json +++ b/image/assets/lang/English.json @@ -130,6 +130,7 @@ "app_notes_delete_note": "Delete", "app_notes_note_delete_confirmation": "Do you really want to delete this note?", "app_notes_no_notes": "There are no notes yet.\nPress Left arrow to add new.", + "app_notes_search_no_results": "No notes found.", "app_calllog_title_main": "Calls", "app_calllog_new_note": "New Note", diff --git a/module-apps/application-notes/ApplicationNotes.cpp b/module-apps/application-notes/ApplicationNotes.cpp index 9da802e578a2eab9a4455ea543e0c18f6f225785..e8e9a569171aceaa15b45c2d6e46408a7d9722dc 100644 --- a/module-apps/application-notes/ApplicationNotes.cpp +++ b/module-apps/application-notes/ApplicationNotes.cpp @@ -7,6 +7,8 @@ #include "windows/NoteMainWindow.hpp" #include "windows/NotePreviewWindow.hpp" #include "windows/NoteEditWindow.hpp" +#include "windows/SearchEngineWindow.hpp" +#include "windows/SearchResultsWindow.hpp" #include #include @@ -80,22 +82,33 @@ namespace app void ApplicationNotes::createUserInterface() { - windowsFactory.attach(gui::name::window::main_window, [this](Application *app, const std::string &name) { - auto notesRepository = std::make_unique(this); - auto notesProvider = std::make_shared(this, std::move(notesRepository)); + windowsFactory.attach(gui::name::window::main_window, [](Application *app, const std::string &name) { + auto notesRepository = std::make_unique(app); + auto notesProvider = std::make_shared(app, std::move(notesRepository)); auto presenter = std::make_unique(notesProvider); return std::make_unique(app, std::move(presenter)); }); - windowsFactory.attach(gui::name::window::note_preview, [this](Application *app, const std::string &name) { - auto notesRepository = std::make_unique(this); + windowsFactory.attach(gui::name::window::note_preview, [](Application *app, const std::string &name) { + auto notesRepository = std::make_unique(app); auto presenter = std::make_unique(std::move(notesRepository)); return std::make_unique(app, std::move(presenter)); }); - windowsFactory.attach(gui::name::window::note_edit, [this](Application *app, const std::string &name) { - auto notesRepository = std::make_unique(this); + windowsFactory.attach(gui::name::window::note_edit, [](Application *app, const std::string &name) { + auto notesRepository = std::make_unique(app); auto presenter = std::make_unique(std::move(notesRepository)); return std::make_unique(app, std::move(presenter)); }); + windowsFactory.attach(gui::name::window::notes_search, [](Application *app, const std::string &name) { + auto notesRepository = std::make_unique(app); + auto presenter = std::make_unique(std::move(notesRepository)); + return std::make_unique(app, std::move(presenter)); + }); + windowsFactory.attach(gui::name::window::notes_search_result, [](Application *app, const std::string &name) { + return std::make_unique(app); + }); + windowsFactory.attach(gui::name::window::note_dialog, [](Application *app, const std::string &name) { + return std::make_unique(app, name); + }); windowsFactory.attach(gui::name::window::note_confirm_dialog, [](Application *app, const std::string &name) { return std::make_unique(app, name); }); diff --git a/module-apps/application-notes/ApplicationNotes.hpp b/module-apps/application-notes/ApplicationNotes.hpp index b6d5c33649d2c70b13711798f48c9682299d3279..25dc2440bed9cba860644e856ebaadd565bf2484 100644 --- a/module-apps/application-notes/ApplicationNotes.hpp +++ b/module-apps/application-notes/ApplicationNotes.hpp @@ -10,6 +10,9 @@ namespace gui::name::window { inline constexpr auto note_preview = "NotePreview"; inline constexpr auto note_edit = "NoteEdit"; + inline constexpr auto notes_search = "NotesSearch"; + inline constexpr auto notes_search_result = "NotesSearchResult"; + inline constexpr auto note_dialog = "Dialog"; inline constexpr auto note_confirm_dialog = "ConfirmDialog"; } // namespace gui::name::window diff --git a/module-apps/application-notes/CMakeLists.txt b/module-apps/application-notes/CMakeLists.txt index f34ccd6c7587022a6100f656a85549827258a461..513162c05948eca05859c8c3e5af213ffee6a355 100644 --- a/module-apps/application-notes/CMakeLists.txt +++ b/module-apps/application-notes/CMakeLists.txt @@ -13,29 +13,39 @@ target_sources( ${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/ApplicationNotes.cpp" "${CMAKE_CURRENT_LIST_DIR}/model/NotesListModel.cpp" + "${CMAKE_CURRENT_LIST_DIR}/model/SearchResultsListModel.cpp" "${CMAKE_CURRENT_LIST_DIR}/model/NotesRepository.cpp" "${CMAKE_CURRENT_LIST_DIR}/presenter/NotesMainWindowPresenter.cpp" "${CMAKE_CURRENT_LIST_DIR}/presenter/NotePreviewWindowPresenter.cpp" "${CMAKE_CURRENT_LIST_DIR}/presenter/NoteEditWindowPresenter.cpp" + "${CMAKE_CURRENT_LIST_DIR}/presenter/SearchEngineWindowPresenter.cpp" "${CMAKE_CURRENT_LIST_DIR}/widgets/NotesItem.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NoteMainWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NotePreviewWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NoteEditWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NotesOptions.cpp" + "${CMAKE_CURRENT_LIST_DIR}/windows/SearchEngineWindow.cpp" + "${CMAKE_CURRENT_LIST_DIR}/windows/SearchResultsWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/data/NoteSwitchData.cpp" + "${CMAKE_CURRENT_LIST_DIR}/data/NotesFoundData.cpp" PUBLIC "${CMAKE_CURRENT_LIST_DIR}/ApplicationNotes.hpp" "${CMAKE_CURRENT_LIST_DIR}/model/NotesListModel.hpp" + "${CMAKE_CURRENT_LIST_DIR}/model/SearchResultsListModel.hpp" "${CMAKE_CURRENT_LIST_DIR}/model/NotesRepository.hpp" "${CMAKE_CURRENT_LIST_DIR}/presenter/NotesMainWindowPresenter.hpp" "${CMAKE_CURRENT_LIST_DIR}/presenter/NotePreviewWindowPresenter.hpp" "${CMAKE_CURRENT_LIST_DIR}/presenter/NoteEditWindowPresenter.hpp" + "${CMAKE_CURRENT_LIST_DIR}/presenter/SearchEngineWindowPresenter.hpp" "${CMAKE_CURRENT_LIST_DIR}/widgets/NotesItem.hpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NoteMainWindow.hpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NotePreviewWindow.hpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NoteEditWindow.hpp" + "${CMAKE_CURRENT_LIST_DIR}/windows/SearchEngineWindow.hpp" + "${CMAKE_CURRENT_LIST_DIR}/windows/SearchResultsWindow.hpp" "${CMAKE_CURRENT_LIST_DIR}/windows/NotesOptions.hpp" "${CMAKE_CURRENT_LIST_DIR}/data/NoteSwitchData.hpp" + "${CMAKE_CURRENT_LIST_DIR}/data/NotesFoundData.hpp" ) target_include_directories(${PROJECT_NAME} PRIVATE diff --git a/module-apps/application-notes/data/NotesFoundData.cpp b/module-apps/application-notes/data/NotesFoundData.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f46105a215a5f5a63415b1b5c7216f27c33b8e78 --- /dev/null +++ b/module-apps/application-notes/data/NotesFoundData.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "NotesFoundData.hpp" + +namespace app::notes +{ + NotesFoundData::NotesFoundData(std::string searchText, std::vector notes) + : gui::SwitchData(std::string{"NotesFoundData"}), searchText{std::move(searchText)}, recordsFound{ + std::move(notes)} + {} + + const std::string &NotesFoundData::getSearchText() const noexcept + { + return searchText; + } + + const std::vector &NotesFoundData::getFoundRecords() const noexcept + { + return recordsFound; + } +} // namespace app::notes diff --git a/module-apps/application-notes/data/NotesFoundData.hpp b/module-apps/application-notes/data/NotesFoundData.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2035bbd32526f021c9fe717d981d784b45a2f9d1 --- /dev/null +++ b/module-apps/application-notes/data/NotesFoundData.hpp @@ -0,0 +1,25 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include + +#include +#include + +namespace app::notes +{ + class NotesFoundData : public gui::SwitchData + { + public: + NotesFoundData(std::string searchText, std::vector notes); + + const std::string &getSearchText() const noexcept; + const std::vector &getFoundRecords() const noexcept; + + private: + std::string searchText; + std::vector recordsFound; + }; +} // namespace app::notes diff --git a/module-apps/application-notes/model/NotesRepository.cpp b/module-apps/application-notes/model/NotesRepository.cpp index fa844084736878766dc7d30c2401d68213817a6a..dbe72ad205e9d33ab1f86b02b9ca763c5274cd63 100644 --- a/module-apps/application-notes/model/NotesRepository.cpp +++ b/module-apps/application-notes/model/NotesRepository.cpp @@ -4,6 +4,7 @@ #include "NotesRepository.hpp" #include +#include #include #include @@ -30,6 +31,22 @@ namespace app::notes DBServiceAPI::GetQuery(application, db::Interface::Name::Notes, std::move(query)); } + void NotesDBRepository::getByText(const std::string &text, const OnFilteredCallback &callback) + { + auto query = std::make_unique(text); + query->setQueryListener(db::QueryCallback::fromFunction([callback](auto response) { + auto result = dynamic_cast(response); + if (result == nullptr) { + return false; + } + if (callback) { + callback(result->getRecords()); + } + return true; + })); + DBServiceAPI::GetQuery(application, db::Interface::Name::Notes, std::move(query)); + } + void NotesDBRepository::save(const NotesRecord ¬e, const OnResultCallback &callback) { auto query = std::make_unique(note); diff --git a/module-apps/application-notes/model/NotesRepository.hpp b/module-apps/application-notes/model/NotesRepository.hpp index d4a0e09bad7acf3c5ae9cb569f38b3e630443fff..b773a1d1ef11bba5c40702e22d0df5931e68f9e7 100644 --- a/module-apps/application-notes/model/NotesRepository.hpp +++ b/module-apps/application-notes/model/NotesRepository.hpp @@ -14,12 +14,14 @@ namespace app::notes class AbstractNotesRepository { public: - using OnGetCallback = std::function &, unsigned int)>; - using OnResultCallback = std::function; + using OnGetCallback = std::function &, unsigned int)>; + using OnFilteredCallback = std::function &)>; + using OnResultCallback = std::function; virtual ~AbstractNotesRepository() noexcept = default; virtual void get(std::uint32_t offset, std::uint32_t limit, const OnGetCallback &callback) = 0; + virtual void getByText(const std::string &text, const OnFilteredCallback &callback) = 0; virtual void save(const NotesRecord ¬e, const OnResultCallback &callback) = 0; virtual void remove(const NotesRecord ¬e, const OnResultCallback &callback) = 0; }; @@ -30,6 +32,7 @@ namespace app::notes explicit NotesDBRepository(Application *application); void get(std::uint32_t offset, std::uint32_t limit, const OnGetCallback &callback) override; + void getByText(const std::string &text, const OnFilteredCallback &callback) override; void save(const NotesRecord ¬e, const OnResultCallback &callback) override; void remove(const NotesRecord ¬e, const OnResultCallback &callback) override; diff --git a/module-apps/application-notes/model/SearchResultsListModel.cpp b/module-apps/application-notes/model/SearchResultsListModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd2dab58020ffabd3aa988c61651afa745003f72 --- /dev/null +++ b/module-apps/application-notes/model/SearchResultsListModel.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "SearchResultsListModel.hpp" + +#include "module-apps/application-notes/style/NotesListStyle.hpp" +#include "module-apps/application-notes/data/NoteSwitchData.hpp" + +#include +#include + +namespace app::notes +{ + SearchResultsListModel::SearchResultsListModel(Application *application) : application{application} + {} + + void SearchResultsListModel::setResults(const std::vector &results) + { + eraseInternalData(); + for (const auto ¬e : results) { + auto item = createItem(note); + internalData.push_back(std::move(item)); + } + } + + gui::NotesItem *SearchResultsListModel::createItem(const NotesRecord &record) const + { + auto item = new gui::NotesItem(std::make_shared(record)); + item->deleteByList = false; + item->activatedCallback = [this, record](gui::Item &) { + auto data = std::make_unique(record); + data->ignoreCurrentWindowOnStack = true; + application->switchWindow(gui::name::window::note_preview, std::move(data)); + return true; + }; + return item; + } + + void SearchResultsListModel::requestRecords(std::uint32_t offset, std::uint32_t limit) + { + setupModel(offset, limit); + list->onProviderDataUpdate(); + } + + unsigned int SearchResultsListModel::requestRecordsCount() + { + return internalData.size(); + } + + gui::ListItem *SearchResultsListModel::getItem(gui::Order order) + { + return getRecord(order); + } + + unsigned int SearchResultsListModel::getMinimalItemHeight() const + { + return style::list::item::Height; + } +} // namespace app::notes diff --git a/module-apps/application-notes/model/SearchResultsListModel.hpp b/module-apps/application-notes/model/SearchResultsListModel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..57eb8166e6cbc8f13ce22594e3245377f995d370 --- /dev/null +++ b/module-apps/application-notes/model/SearchResultsListModel.hpp @@ -0,0 +1,34 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include +#include + +#include + +#include +#include "module-apps/application-notes/widgets/NotesItem.hpp" +#include + +namespace app::notes +{ + class SearchResultsListModel : public InternalModel, public gui::ListItemProvider + { + public: + explicit SearchResultsListModel(Application *application); + + void setResults(const std::vector &results); + + void requestRecords(std::uint32_t offset, std::uint32_t limit) override; + unsigned int requestRecordsCount() override; + unsigned int getMinimalItemHeight() const override; + gui::ListItem *getItem(gui::Order order) override; + + private: + gui::NotesItem *createItem(const NotesRecord &record) const; + + Application *application; + }; +} // namespace app::notes diff --git a/module-apps/application-notes/presenter/SearchEngineWindowPresenter.cpp b/module-apps/application-notes/presenter/SearchEngineWindowPresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9afbf1b91ea2be2b500d7629bd1be40875a5773d --- /dev/null +++ b/module-apps/application-notes/presenter/SearchEngineWindowPresenter.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "SearchEngineWindowPresenter.hpp" + +namespace app::notes +{ + SearchEngineWindowPresenter::SearchEngineWindowPresenter(std::unique_ptr &¬esRepository) + : notesRepository{std::move(notesRepository)} + {} + + void SearchEngineWindowPresenter::searchFor(const std::string &searchText) + { + if (searchText.empty()) { + getView()->notesFound({}, searchText); + return; + } + notesRepository->getByText( + searchText, [searchText, this](const auto &records) { getView()->notesFound(records, searchText); }); + } +} // namespace app::notes diff --git a/module-apps/application-notes/presenter/SearchEngineWindowPresenter.hpp b/module-apps/application-notes/presenter/SearchEngineWindowPresenter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b39c006fbbe489b4b83b517017ba7dfe35ccbc9d --- /dev/null +++ b/module-apps/application-notes/presenter/SearchEngineWindowPresenter.hpp @@ -0,0 +1,41 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include "BasePresenter.hpp" + +#include + +namespace app::notes +{ + class SearchEngineWindowContract + { + public: + class View + { + public: + virtual ~View() noexcept = default; + + virtual void notesFound(const std::vector ¬es, const std::string &searchText) = 0; + }; + class Presenter : public BasePresenter + { + public: + virtual ~Presenter() noexcept = default; + + virtual void searchFor(const std::string &searchText) = 0; + }; + }; + + class SearchEngineWindowPresenter : public SearchEngineWindowContract::Presenter + { + public: + explicit SearchEngineWindowPresenter(std::unique_ptr &¬esRepository); + + void searchFor(const std::string &searchText) override; + + private: + std::unique_ptr notesRepository; + }; +} // namespace app::notes diff --git a/module-apps/application-notes/windows/NoteMainWindow.cpp b/module-apps/application-notes/windows/NoteMainWindow.cpp index ee608380680ec77ebd89d4f8dfafb01e77557b90..6fed7c62c1b2c75eed595f5a8ae290519dd8bc16 100644 --- a/module-apps/application-notes/windows/NoteMainWindow.cpp +++ b/module-apps/application-notes/windows/NoteMainWindow.cpp @@ -167,6 +167,9 @@ namespace app::notes application->switchWindow(gui::name::window::note_edit, std::make_unique(NotesRecord{})); } + else if (inputEvent.is(gui::KeyCode::KEY_RIGHT)) { + application->switchWindow(gui::name::window::notes_search); + } } return AppWindow::onInput(inputEvent); } diff --git a/module-apps/application-notes/windows/SearchEngineWindow.cpp b/module-apps/application-notes/windows/SearchEngineWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56132571054d1530aad8ca2dd2d49204f79c0703 --- /dev/null +++ b/module-apps/application-notes/windows/SearchEngineWindow.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "SearchEngineWindow.hpp" + +#include +#include +#include + +#include + +namespace app::notes +{ + SearchEngineWindow::SearchEngineWindow(Application *application, + std::unique_ptr &&windowPresenter) + : gui::AppWindow{application, gui::name::window::notes_search}, presenter{std::move(windowPresenter)} + { + presenter->attach(this); + buildInterface(); + } + + SearchEngineWindow::~SearchEngineWindow() noexcept + { + destroyInterface(); + } + + void SearchEngineWindow::buildInterface() + { + AppWindow::buildInterface(); + setTitle(utils::localize.get("app_notes_title_main")); + + bottomBar->setActive(gui::BottomBar::Side::CENTER, true); + bottomBar->setText(gui::BottomBar::Side::CENTER, utils::localize.get(style::strings::common::search)); + bottomBar->setActive(gui::BottomBar::Side::RIGHT, true); + bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back)); + + input = gui::inputBox(this, utils::localize.get("common_search_uc"), "search"); + setFocusItem(input); + } + + void SearchEngineWindow::destroyInterface() + { + erase(); + input = nullptr; + } + + bool SearchEngineWindow::onInput(const gui::InputEvent &inputEvent) + { + if (inputEvent.isShortPress() && inputEvent.is(gui::KeyCode::KEY_ENTER)) { + presenter->searchFor(input->getText()); + return true; + } + return AppWindow::onInput(inputEvent); + } + + void SearchEngineWindow::notesFound(const std::vector ¬es, const std::string &searchText) + { + application->switchWindow(gui::name::window::notes_search_result, + std::make_unique(searchText, notes)); + } +} // namespace app::notes diff --git a/module-apps/application-notes/windows/SearchEngineWindow.hpp b/module-apps/application-notes/windows/SearchEngineWindow.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f844ebd8d30383b678defcc9952a927ea98b16da --- /dev/null +++ b/module-apps/application-notes/windows/SearchEngineWindow.hpp @@ -0,0 +1,33 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include "AppWindow.hpp" + +#include +#include + +#include +#include + +namespace app::notes +{ + class SearchEngineWindow : public gui::AppWindow, public SearchEngineWindowContract::View + { + public: + SearchEngineWindow(Application *application, + std::unique_ptr &&windowPresenter); + ~SearchEngineWindow() noexcept override; + + void buildInterface() override; + void destroyInterface() override; + bool onInput(const gui::InputEvent &inputEvent) override; + + void notesFound(const std::vector ¬es, const std::string &searchText) override; + + private: + std::unique_ptr presenter; + gui::Text *input = nullptr; + }; +} // namespace app::notes diff --git a/module-apps/application-notes/windows/SearchResultsWindow.cpp b/module-apps/application-notes/windows/SearchResultsWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7a6c50ef3e7d87faa02c2008987d3fbbcd9e4e7 --- /dev/null +++ b/module-apps/application-notes/windows/SearchResultsWindow.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "SearchResultsWindow.hpp" + +#include +#include +#include +#include +#include + +namespace app::notes +{ + SearchResultsWindow::SearchResultsWindow(Application *application) + : AppWindow(application, gui::name::window::notes_search_result), listModel{ + std::make_shared( + application)} + { + buildInterface(); + } + + SearchResultsWindow::~SearchResultsWindow() noexcept + { + destroyInterface(); + } + + void SearchResultsWindow::buildInterface() + { + AppWindow::buildInterface(); + setTitle(utils::localize.get("app_notes_title_main")); + + bottomBar->setActive(gui::BottomBar::Side::CENTER, true); + bottomBar->setText(gui::BottomBar::Side::CENTER, utils::localize.get(::style::strings::common::open)); + bottomBar->setActive(gui::BottomBar::Side::RIGHT, true); + bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(::style::strings::common::back)); + + list = + new gui::ListView(this, style::list::X, style::list::Y, style::list::Width, style::list::Height, listModel); + list->setScrollTopMargin(::style::margins::small); + setFocusItem(list); + } + + void SearchResultsWindow::destroyInterface() + { + erase(); + list = nullptr; + } + + void SearchResultsWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) + { + auto foundNotesData = dynamic_cast(data); + if (foundNotesData == nullptr) { + onNothingFound(); + return; + } + + if (const auto &records = foundNotesData->getFoundRecords(); !records.empty()) { + listModel->setResults(records); + list->rebuildList(); + setFocusItem(list); + } + else { + onNothingFound(foundNotesData->getSearchText()); + } + } + + void SearchResultsWindow::onNothingFound(const std::string &searchText) + { + gui::DialogMetadata meta{utils::localize.get("common_results_prefix") + searchText, + "search_big", + utils::localize.get("app_notes_search_no_results")}; + auto data = std::make_unique(meta); + data->ignoreCurrentWindowOnStack = true; + application->switchWindow(gui::name::window::note_dialog, std::move(data)); + } +} // namespace app::notes diff --git a/module-apps/application-notes/windows/SearchResultsWindow.hpp b/module-apps/application-notes/windows/SearchResultsWindow.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4b80ca5554e2bd96f3cba948dec6df1e8dade204 --- /dev/null +++ b/module-apps/application-notes/windows/SearchResultsWindow.hpp @@ -0,0 +1,34 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include + +#include "AppWindow.hpp" + +#include +#include + +#include + +namespace app::notes +{ + class SearchResultsWindow : public gui::AppWindow + { + public: + explicit SearchResultsWindow(Application *application); + ~SearchResultsWindow() noexcept override; + + void buildInterface() override; + void destroyInterface() override; + + void onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) override; + + private: + void onNothingFound(const std::string &searchText = {}); + + std::shared_ptr listModel; + gui::ListView *list = nullptr; + }; +} // namespace app::notes diff --git a/module-db/CMakeLists.txt b/module-db/CMakeLists.txt index 8e41145c3e03d32de3e34762b593645e9cbd3069..7fa9f45d7aba3ea99aa33214738f18e4528eebba 100644 --- a/module-db/CMakeLists.txt +++ b/module-db/CMakeLists.txt @@ -88,6 +88,7 @@ set(SOURCES queries/messages/threads/QueryThreadRemove.cpp queries/messages/threads/QueryThreadMarkAsRead.cpp queries/notes/QueryNotesGet.cpp + queries/notes/QueryNotesGetByText.cpp queries/notes/QueryNoteStore.cpp queries/notes/QueryNoteRemove.cpp queries/calllog/QueryCalllogSetAllRead.cpp diff --git a/module-db/Interface/NotesRecord.cpp b/module-db/Interface/NotesRecord.cpp index 1a16b8aa472a45516934a967ef4c6b125345bd1b..130e4f35791662093de69a24a859377451a19ef0 100644 --- a/module-db/Interface/NotesRecord.cpp +++ b/module-db/Interface/NotesRecord.cpp @@ -4,6 +4,7 @@ #include "NotesRecord.hpp" #include "queries/notes/QueryNotesGet.hpp" +#include "queries/notes/QueryNotesGetByText.hpp" #include "queries/notes/QueryNoteStore.hpp" #include "queries/notes/QueryNoteRemove.hpp" @@ -96,9 +97,20 @@ NotesRecord NotesRecordInterface::GetByID(std::uint32_t id) std::vector NotesRecordInterface::getNotes(std::uint32_t offset, std::uint32_t limit) const { std::vector records; - const auto notes = notesDB->notes.getLimitOffset(offset, limit); - for (const auto &w : notes) { - NotesRecord record{w.ID, w.date, w.snippet}; + const auto ¬es = notesDB->notes.getLimitOffset(offset, limit); + for (const auto ¬e : notes) { + NotesRecord record{note.ID, note.date, note.snippet}; + records.push_back(std::move(record)); + } + return records; +} + +std::vector NotesRecordInterface::getNotesByText(const std::string &text) const +{ + std::vector records; + const auto ¬es = notesDB->notes.getByText(text); + for (const auto ¬e : notes) { + NotesRecord record{note.ID, note.date, note.snippet}; records.push_back(std::move(record)); } return records; @@ -109,6 +121,9 @@ std::unique_ptr NotesRecordInterface::runQuery(std::shared_ptr< if (typeid(*query) == typeid(db::query::QueryNotesGet)) { return getQuery(query); } + if (typeid(*query) == typeid(db::query::QueryNotesGetByText)) { + return getByTextQuery(query); + } if (typeid(*query) == typeid(db::query::QueryNoteStore)) { return storeQuery(query); } @@ -127,6 +142,15 @@ std::unique_ptr NotesRecordInterface::getQuery(const std::share return response; } +std::unique_ptr NotesRecordInterface::getByTextQuery(const std::shared_ptr &query) +{ + const auto localQuery = static_cast(query.get()); + const auto &records = getNotesByText(localQuery->getText()); + auto response = std::make_unique(records); + response->setRequestQuery(query); + return response; +} + std::unique_ptr NotesRecordInterface::storeQuery(const std::shared_ptr &query) { const auto localQuery = static_cast(query.get()); diff --git a/module-db/Interface/NotesRecord.hpp b/module-db/Interface/NotesRecord.hpp index e83f2707427ac8b1bfee485053abb606a8b701f3..3dc295bf988b08c6de424dd43ba304e2652ed7ec 100644 --- a/module-db/Interface/NotesRecord.hpp +++ b/module-db/Interface/NotesRecord.hpp @@ -47,8 +47,10 @@ class NotesRecordInterface : public RecordInterface getNotes(std::uint32_t offset, std::uint32_t limit) const; + std::vector getNotesByText(const std::string &text) const; std::unique_ptr getQuery(const std::shared_ptr &query); + std::unique_ptr getByTextQuery(const std::shared_ptr &query); std::unique_ptr storeQuery(const std::shared_ptr &query); std::unique_ptr removeQuery(const std::shared_ptr &query); diff --git a/module-db/Tables/NotesTable.cpp b/module-db/Tables/NotesTable.cpp index 62045d9cb83e6df6ddf79e8d6610b3bd39e4b593..7f31ccc89d671c955f5cf4bb68343f2f43374ca8 100644 --- a/module-db/Tables/NotesTable.cpp +++ b/module-db/Tables/NotesTable.cpp @@ -117,6 +117,25 @@ std::vector NotesTable::getLimitOffsetByField(std::uint32_t offse return ret; } +std::vector NotesTable::getByText(const std::string &text) +{ + auto retQuery = db->query("SELECT *, INSTR(snippet,'%s') pos FROM notes WHERE pos > 0;", text.c_str()); + if (retQuery == nullptr || retQuery->getRowCount() == 0) { + return {}; + } + + std::vector records; + do { + NotesTableRow row{ + (*retQuery)[0].getUInt32(), // ID + (*retQuery)[1].getUInt32(), // date + (*retQuery)[2].getString() // snippet + }; + records.push_back(std::move(row)); + } while (retQuery->nextRow()); + return records; +} + std::uint32_t NotesTable::count() { auto queryRet = db->query("SELECT COUNT(*) FROM notes;"); diff --git a/module-db/Tables/NotesTable.hpp b/module-db/Tables/NotesTable.hpp index db001927c5ad56aa03402f4b5531cc5daf8d89dc..0172ae27b6f23791005df0bf25ce210b595f9b2c 100644 --- a/module-db/Tables/NotesTable.hpp +++ b/module-db/Tables/NotesTable.hpp @@ -41,6 +41,7 @@ class NotesTable : public Table std::uint32_t limit, NotesTableFields field, const char *str) override; + std::vector getByText(const std::string &text); std::uint32_t count() override; std::uint32_t countByFieldId(const char *field, std::uint32_t id) override; diff --git a/module-db/queries/notes/QueryNotesGetByText.cpp b/module-db/queries/notes/QueryNotesGetByText.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c61e32f6766519d23bf4a07fe55e22b3bc3f2220 --- /dev/null +++ b/module-db/queries/notes/QueryNotesGetByText.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "QueryNotesGetByText.hpp" + +namespace db::query +{ + QueryNotesGetByText::QueryNotesGetByText(std::string text) : Query(Query::Type::Read), text{std::move(text)} + {} + + const std::string &QueryNotesGetByText::getText() const noexcept + { + return text; + } + + std::string QueryNotesGetByText::debugInfo() const + { + return {"QueryNotesGetByText"}; + } + + NotesGetByTextResult::NotesGetByTextResult(std::vector notes) : records{std::move(notes)} + {} + + const std::vector &NotesGetByTextResult::getRecords() const noexcept + { + return records; + } + + auto NotesGetByTextResult::debugInfo() const -> std::string + { + return {"NotesGetByTextResult"}; + } +} // namespace db::query diff --git a/module-db/queries/notes/QueryNotesGetByText.hpp b/module-db/queries/notes/QueryNotesGetByText.hpp new file mode 100644 index 0000000000000000000000000000000000000000..265f094a5630c86ef600a1379337fe238397ebc2 --- /dev/null +++ b/module-db/queries/notes/QueryNotesGetByText.hpp @@ -0,0 +1,36 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include + +#include +#include "Interface/NotesRecord.hpp" + +namespace db::query +{ + class QueryNotesGetByText : public Query + { + public: + explicit QueryNotesGetByText(std::string text); + + [[nodiscard]] const std::string &getText() const noexcept; + [[nodiscard]] std::string debugInfo() const override; + + private: + std::string text; + }; + + class NotesGetByTextResult : public QueryResult + { + public: + explicit NotesGetByTextResult(std::vector notes); + + [[nodiscard]] const std::vector &getRecords() const noexcept; + [[nodiscard]] std::string debugInfo() const override; + + private: + std::vector records; + }; +} // namespace db::query diff --git a/module-db/tests/NotesRecord_tests.cpp b/module-db/tests/NotesRecord_tests.cpp index f2d07f8eaac2ab6c93f3d5d19520fcbca899f95a..0b19faaaa519a8a4e7cfae53b5c3fe0b8f87575b 100644 --- a/module-db/tests/NotesRecord_tests.cpp +++ b/module-db/tests/NotesRecord_tests.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -40,6 +41,17 @@ TEST_CASE("Notes Record tests") REQUIRE(getResult->getRecords()[0].snippet == testSnippet); } + SECTION("Get notes by text query") + { + constexpr auto testSearch = "TEST"; + auto query = std::make_unique(testSearch); + auto response = notesRecordInterface.runQuery(std::move(query)); + auto getResult = static_cast(response.get()); + + REQUIRE(getResult->getRecords().size() == 1); + REQUIRE(getResult->getRecords()[0].snippet == testSnippet); + } + SECTION("Add a note") { NotesRecord record; diff --git a/module-db/tests/NotesTable_tests.cpp b/module-db/tests/NotesTable_tests.cpp index 60a746d4a180d3f771a6cd1b87f71da588862b74..6b13ef6b97b16c78a44eba56a2d713450ebf87ae 100644 --- a/module-db/tests/NotesTable_tests.cpp +++ b/module-db/tests/NotesTable_tests.cpp @@ -33,6 +33,14 @@ TEST_CASE("Notes Table tests") REQUIRE(records[0].snippet == testSnippet); } + SECTION("Get notes by text query") + { + constexpr auto testSearch = "TEST"; + const auto &records = table.getByText(testSearch); + REQUIRE(records.size() == 1); + REQUIRE(records[0].snippet == testSnippet); + } + SECTION("Add a note") { NotesTableRow row;