From d63f0ff0d15eb7de504a72fb791cfd5c29a1d715 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Dec 2022 21:43:55 +0530 Subject: [PATCH] CS FTS: Implement opening viewer to search result --- src/pyj/book_list/fts.pyj | 9 ++-- src/pyj/read_book/search.pyj | 66 ++++++++++++++++++++++------- src/pyj/read_book/search_worker.pyj | 5 +++ src/pyj/read_book/view.pyj | 6 +++ 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/pyj/book_list/fts.pyj b/src/pyj/book_list/fts.pyj index 55c9c40bac..11fdadcdf5 100644 --- a/src/pyj/book_list/fts.pyj +++ b/src/pyj/book_list/fts.pyj @@ -11,7 +11,7 @@ from book_list.router import back, push_state, open_book_url from book_list.top_bar import create_top_bar from book_list.ui import set_panel_handler from book_list.views import create_image -from book_list.library_data import current_library_id +from book_list.library_data import current_library_id, download_url from complete import create_search_bar from dom import add_extra_css, clear, set_css from gettext import gettext as _ @@ -184,9 +184,12 @@ def open_format(book_id, fmt): text = s.text break url = open_book_url(book_id, fmt) - window.open(url, '_blank') + if fmt is 'PDF': + url = download_url(book_id, fmt, 'inline') + w = window.open(url, '_blank') if text: - pass + text = str.strip(text, '…').replaceAll('\x1c', '').replaceAll('\x1e', '') + w.read_book_initial_open_search_text = {'text': text, 'query': current_fts_query.query} def book_result_tile_clicked(ev): diff --git a/src/pyj/read_book/search.pyj b/src/pyj/read_book/search.pyj index 2a21b43a23..28d9084b69 100644 --- a/src/pyj/read_book/search.pyj +++ b/src/pyj/read_book/search.pyj @@ -20,6 +20,21 @@ from widgets import create_button, create_spinner from worker import start_worker +def parse_error_msg(msg): + details = msg.msg + emsg = _('Unknown error') + if msg.code is GET_SPINE_FAILED: + emsg = _('Loading text from the book failed.') + elif msg.code is CONNECT_FAILED: + emsg = _('Connecting to database storing the local copy of the book failed in the worker thread.') + elif msg.code is UNHANDLED_ERROR: + emsg = _('There was an unhandled error while searching.') + elif msg.code is DB_ERROR: + emsg = msg.error.msg + details = msg.error.details + return emsg, details + + def get_toc_data(book): spine = book.manifest.spine spine_toc_map = {name: v'[]' for name in spine} @@ -68,6 +83,7 @@ class SearchOverlay: c.style.userSelect = 'none' c.addEventListener('keydown', self.onkeydown) c.addEventListener('keyup', self.onkeyup) + self.result_handler = None create_top_bar(c, title=_('Search in book'), action=self.hide, icon='close') @@ -176,6 +192,37 @@ class SearchOverlay: self.clear_caches() return self._worker + def do_initial_search(self, text, query): + q = {'mode': 'contains', 'case_sensitive': True, 'text': text, 'only_first_match': True} + self.request_counter += 1 + self.initial_search_result_counter = 0 + self.initial_search_human_readable_query = query + self.search_in_flight.id = self.request_counter + self.result_handler = self.handle_initial_search_result + self.worker.postMessage({ + 'type': 'search', 'current_name': '', 'id': self.request_counter, 'query': q + }) + + def handle_initial_search_result(self, msg): + if msg.type is 'error': + emsg, details = parse_error_msg(msg) + error_dialog(_('Could not search'), emsg, details) + self.result_handler = None + self.initial_search_result_counter = 0 + elif msg.id is self.search_in_flight.id: + if msg.type is 'search_complete': + self.search_in_flight.id = None + self.result_handler = None + if self.initial_search_result_counter is 0: + self.view.hide_loading() + window.alert(_('The full text search query {} was not found in the book, try a manual search.').format(self.initial_search_human_readable_query)) + self.initial_search_result_counter = 0 + elif msg.type is 'search_result': + self.initial_search_result_counter += 1 + if self.initial_search_result_counter is 1: + self.view.discover_search_result(msg.result) + self.view.hide_loading() + def queue_search(self, query, book, current_name): self.request_counter += 1 self.original_position = self.view.currently_showing.bookpos @@ -197,18 +244,10 @@ class SearchOverlay: def on_worker_message(self, evt): msg = evt.data + if self.result_handler: + return self.result_handler(msg) if msg.type is 'error': - details = msg.msg - emsg = _('Unknown error') - if msg.code is GET_SPINE_FAILED: - emsg = _('Loading text from the book failed.') - elif msg.code is CONNECT_FAILED: - emsg = _('Connecting to database storing the local copy of the book failed in the worker thread.') - elif msg.code is UNHANDLED_ERROR: - emsg = _('There was an unhandled error while searching.') - elif msg.code is DB_ERROR: - emsg = msg.error.msg - details = msg.error.details + emsg, details = parse_error_msg(msg) error_dialog(_('Could not search'), emsg, details) elif msg.id is self.search_in_flight.id: if msg.type is 'search_complete': @@ -273,11 +312,6 @@ class SearchOverlay: entry.appendChild(E.span(result.after + '…')) ul.appendChild(entry) - def make_query_programmatically(self, text, mode, case_sensitive): - self.current_query = {'text': text, 'mode': mode, 'case_sensitive': case_sensitive} - self.show() - self.run_search() - @property def current_result_container(self): return self.container.querySelector('.current') diff --git a/src/pyj/read_book/search_worker.pyj b/src/pyj/read_book/search_worker.pyj index 2a6d61b9f8..9433ef0cee 100644 --- a/src/pyj/read_book/search_worker.pyj +++ b/src/pyj/read_book/search_worker.pyj @@ -178,11 +178,16 @@ def search_in_text_of(name): } result.toc_nodes = toc_nodes_for_search_result(result) self.postMessage({'type': 'search_result', 'id': wc.current_query_id, 'result': result}) + if wc.current_query.query.only_first_match: + break match_counts[q] += 1 def queue_next_spine_item(spine_idx, allow_current_name): name = wc.current_book.spine[spine_idx] + if wc.current_query.query.only_first_match and wc.result_num > 0: + send_search_complete() + return if not name or (not allow_current_name and name is wc.current_query.current_name): send_search_complete() return diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 62312cf24d..87232bc756 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -856,6 +856,12 @@ class View: self.show_loading_callback_timer = setTimeout(self.show_loading_message.bind(None, msg), 200) def hide_loading(self): + if window.read_book_initial_open_search_text: + q = window.read_book_initial_open_search_text + v'delete window.read_book_initial_open_search_text' + self.search_overlay.do_initial_search(q.text, q.query) + self.show_loading_message(_('Searching for: {}').format(q.query)) + return if self.show_loading_callback_timer is not None: clearTimeout(self.show_loading_callback_timer) self.show_loading_callback_timer = None