From 475ffbd1d25e52c471d6b16e17c085ee85750e85 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 11 Oct 2023 18:53:10 +0530 Subject: [PATCH] Start work on notes UI in the content server --- src/calibre/db/cache.py | 17 +++++++++++++++++ src/calibre/ebooks/metadata/book/render.py | 2 +- src/calibre/srv/code.py | 1 + src/calibre/srv/metadata.py | 3 +++ src/pyj/book_list/book_details.pyj | 12 +++++++++++- src/pyj/book_list/library_data.pyj | 2 +- src/pyj/book_list/router.pyj | 9 +++++++++ 7 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index b707ec8569..ef6ca9284c 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -697,6 +697,23 @@ class Cache: return self.backend.notes.allowed_fields return field in self.backend.notes.allowed_fields + @read_api + def items_with_notes_in_book(self, book_id: int) -> dict[str, dict[int, str]]: + ' Return a dict of field to items that have associated notes for that field for the specified book ' + ans = {} + for k in self.backend.notes.allowed_fields: + try: + field = self.fields[k] + except KeyError: + continue + v = {} + for item_id in field.ids_for_book(book_id): + if self.backend.notes_for(k, item_id): + v[item_id] = field.table.id_map[item_id] + if v: + ans[k] = v + return ans + @write_api def set_notes_for(self, field, item_id, doc: str, searchable_text: str = copy_marked_up_text, resource_hashes=(), remove_unused_resources=False) -> int: ''' diff --git a/src/calibre/ebooks/metadata/book/render.py b/src/calibre/ebooks/metadata/book/render.py index 8f411350d3..8d1bd7d426 100644 --- a/src/calibre/ebooks/metadata/book/render.py +++ b/src/calibre/ebooks/metadata/book/render.py @@ -134,7 +134,7 @@ def mi_to_html( item_id = None if item_id_if_has_note is None else item_id_if_has_note(field, field_value) if item_id is not None: note = ' {2}'.format( - _('Click to open note'), notes_action(field=field, value=field_value, item_id=item_id), note_markup) + _('Show notes for: {}').format(field_value), notes_action(field=field, value=field_value, item_id=item_id), note_markup) return link + note return '' diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index 9fcf8a24e7..9d48d91ca6 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -234,6 +234,7 @@ def get_library_init_data(ctx, rd, db, num, sorts, orders, vl): ans['book_display_fields'] = get_field_list(db) ans['fts_enabled'] = db.is_fts_enabled() ans['book_details_vertical_categories'] = db._pref('book_details_vertical_categories', ()) + ans['fields_that_support_notes'] = tuple(db._field_supports_notes()) mdata = ans['metadata'] = {} try: extra_books = { diff --git a/src/calibre/srv/metadata.py b/src/calibre/srv/metadata.py index eaef505f1e..d600b909e5 100644 --- a/src/calibre/srv/metadata.py +++ b/src/calibre/srv/metadata.py @@ -90,6 +90,9 @@ def book_as_json(db, book_id): link_maps = db.get_all_link_maps_for_book(book_id) if link_maps: ans['link_maps'] = link_maps + x = db.items_with_notes_in_book(book_id) + if x: + ans['items_with_notes'] = {field: {v: k for k, v in items.items()} for field, items in x.items()} return ans diff --git a/src/pyj/book_list/book_details.pyj b/src/pyj/book_list/book_details.pyj index 918bbe2168..4274001ac4 100644 --- a/src/pyj/book_list/book_details.pyj +++ b/src/pyj/book_list/book_details.pyj @@ -14,7 +14,7 @@ from book_list.library_data import ( current_virtual_library, download_url, library_data, load_status, set_book_metadata ) -from book_list.router import back, home, open_book, report_a_load_failure +from book_list.router import back, home, open_book, report_a_load_failure, show_note_url from book_list.theme import ( color_scheme, get_color, get_color_as_rgba, get_font_size ) @@ -192,6 +192,13 @@ def render_metadata(mi, table, book_id, iframe_css): # {{{ comments = v'[]' link_maps = mi.link_maps or v'{}' + def add_note_link(field, name, val, parent): + if mi.items_with_notes[field] and mi.items_with_notes[field][val]: + parent.appendChild(document.createTextNode(' ')) + parent.appendChild(E.a( + svgicon('pencil'), title=_('Show notes for: {}').format(val), href=show_note_url( + book_id, field, val, close_action='close_window'), target='_new', class_='blue-link')) + def add_row(field, name, val, is_searchable=False, is_html=False, join=None, search_text=None, use_quotes=True): if val is undefined or val is None: return @@ -219,6 +226,8 @@ def render_metadata(mi, table, book_id, iframe_css): # {{{ parent.appendChild(v) else: parent.appendChild(document.createTextNode(v)) + if jstype(v) is 'string' and not is_html: + add_note_link(field, name, v, parent) table.appendChild(E.tr(E.td(name), E.td())) if is_html and /[<>]/.test(val + ''): @@ -337,6 +346,7 @@ def render_metadata(mi, table, book_id, iframe_css): # {{{ else: print("WARNING: Translation of series template is incorrect as it does not have an tag") table.lastChild.lastChild.appendChild(s) + add_note_link(field, name, val, table.lastChild.lastChild) def process_field(field, fm): name = fm.name or field diff --git a/src/pyj/book_list/library_data.pyj b/src/pyj/book_list/library_data.pyj index 0da3d22ce4..c955d90745 100644 --- a/src/pyj/book_list/library_data.pyj +++ b/src/pyj/book_list/library_data.pyj @@ -83,7 +83,7 @@ def update_library_data(data): if library_data.for_library is not current_library_id(): library_data.field_names = {} library_data.for_library = current_library_id() - for key in 'search_result sortable_fields field_metadata metadata virtual_libraries book_display_fields bools_are_tristate book_details_vertical_categories fts_enabled'.split(' '): + for key in 'search_result sortable_fields field_metadata metadata virtual_libraries book_display_fields bools_are_tristate book_details_vertical_categories fts_enabled fields_that_support_notes'.split(' '): library_data[key] = data[key] sr = library_data.search_result if sr: diff --git a/src/pyj/book_list/router.pyj b/src/pyj/book_list/router.pyj index fe272d0f1a..d23015edea 100644 --- a/src/pyj/book_list/router.pyj +++ b/src/pyj/book_list/router.pyj @@ -87,6 +87,15 @@ def open_book_url(book_id, fmt, extra_query): return ans + encode_query(q, '#') +def show_note_url(book_id, field, item_value, close_action='back'): + lid = current_library_id() + ans = absolute_path('') + q = {'book_id':book_id, 'field': field, 'item':item_value, 'panel': 'show_note', 'close_action': close_action} + if lid: + q.library_id = lid + return ans + encode_query(q, '#') + + def push_state(query, replace=False, mode='book_list', call_handler=True): query = {k:query[k] for k in query if query[k]} if mode is not 'book_list':