CS FTS: Implement opening viewer to search result

This commit is contained in:
Kovid Goyal 2022-12-11 21:43:55 +05:30
parent ad6252ac41
commit d63f0ff0d1
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 67 additions and 19 deletions

View File

@ -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.top_bar import create_top_bar
from book_list.ui import set_panel_handler from book_list.ui import set_panel_handler
from book_list.views import create_image 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 complete import create_search_bar
from dom import add_extra_css, clear, set_css from dom import add_extra_css, clear, set_css
from gettext import gettext as _ from gettext import gettext as _
@ -184,9 +184,12 @@ def open_format(book_id, fmt):
text = s.text text = s.text
break break
url = open_book_url(book_id, fmt) 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: 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): def book_result_tile_clicked(ev):

View File

@ -20,6 +20,21 @@ from widgets import create_button, create_spinner
from worker import start_worker 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): def get_toc_data(book):
spine = book.manifest.spine spine = book.manifest.spine
spine_toc_map = {name: v'[]' for name in spine} spine_toc_map = {name: v'[]' for name in spine}
@ -68,6 +83,7 @@ class SearchOverlay:
c.style.userSelect = 'none' c.style.userSelect = 'none'
c.addEventListener('keydown', self.onkeydown) c.addEventListener('keydown', self.onkeydown)
c.addEventListener('keyup', self.onkeyup) c.addEventListener('keyup', self.onkeyup)
self.result_handler = None
create_top_bar(c, title=_('Search in book'), action=self.hide, icon='close') create_top_bar(c, title=_('Search in book'), action=self.hide, icon='close')
@ -176,6 +192,37 @@ class SearchOverlay:
self.clear_caches() self.clear_caches()
return self._worker 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): def queue_search(self, query, book, current_name):
self.request_counter += 1 self.request_counter += 1
self.original_position = self.view.currently_showing.bookpos self.original_position = self.view.currently_showing.bookpos
@ -197,18 +244,10 @@ class SearchOverlay:
def on_worker_message(self, evt): def on_worker_message(self, evt):
msg = evt.data msg = evt.data
if self.result_handler:
return self.result_handler(msg)
if msg.type is 'error': if msg.type is 'error':
details = msg.msg emsg, details = parse_error_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
error_dialog(_('Could not search'), emsg, details) error_dialog(_('Could not search'), emsg, details)
elif msg.id is self.search_in_flight.id: elif msg.id is self.search_in_flight.id:
if msg.type is 'search_complete': if msg.type is 'search_complete':
@ -273,11 +312,6 @@ class SearchOverlay:
entry.appendChild(E.span(result.after + '…')) entry.appendChild(E.span(result.after + '…'))
ul.appendChild(entry) 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 @property
def current_result_container(self): def current_result_container(self):
return self.container.querySelector('.current') return self.container.querySelector('.current')

View File

@ -178,11 +178,16 @@ def search_in_text_of(name):
} }
result.toc_nodes = toc_nodes_for_search_result(result) result.toc_nodes = toc_nodes_for_search_result(result)
self.postMessage({'type': 'search_result', 'id': wc.current_query_id, '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 match_counts[q] += 1
def queue_next_spine_item(spine_idx, allow_current_name): def queue_next_spine_item(spine_idx, allow_current_name):
name = wc.current_book.spine[spine_idx] 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): if not name or (not allow_current_name and name is wc.current_query.current_name):
send_search_complete() send_search_complete()
return return

View File

@ -856,6 +856,12 @@ class View:
self.show_loading_callback_timer = setTimeout(self.show_loading_message.bind(None, msg), 200) self.show_loading_callback_timer = setTimeout(self.show_loading_message.bind(None, msg), 200)
def hide_loading(self): 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: if self.show_loading_callback_timer is not None:
clearTimeout(self.show_loading_callback_timer) clearTimeout(self.show_loading_callback_timer)
self.show_loading_callback_timer = None self.show_loading_callback_timer = None