From 1867cadd5ca5237419259b08dbfcf1df138bfe74 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Aug 2020 21:28:09 +0530 Subject: [PATCH] Content server viewer: Allow editing metadata from the view metadata panel Fixes #1890755 --- src/pyj/book_list/edit_metadata.pyj | 29 ++++++++++++++-- src/pyj/read_book/db.pyj | 6 ++++ src/pyj/read_book/overlay.pyj | 52 ++++++++++++++++++++++++++--- src/pyj/read_book/ui.pyj | 23 +++++++++++++ 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/pyj/book_list/edit_metadata.pyj b/src/pyj/book_list/edit_metadata.pyj index afd91450bc..46e08309fc 100644 --- a/src/pyj/book_list/edit_metadata.pyj +++ b/src/pyj/book_list/edit_metadata.pyj @@ -15,6 +15,7 @@ from book_list.comments_editor import ( create_comments_editor, focus_comments_editor, get_comments_html, set_comments_html ) +from book_list.globals import get_current_query from book_list.library_data import ( book_metadata, cover_url, current_library_id, field_names_for, library_data, load_status, loaded_book_ids, set_book_metadata @@ -937,8 +938,15 @@ def changes_submitted(container_id, book_id, end_type, xhr, ev): error_dialog(_('Could not update metadata for book'), _('Server returned an invalid response'), err.toString()) return - for bid in dirtied: - set_book_metadata(bid, dirtied[book_id]) + cq = get_current_query() + if cq.from_read_book and window.opener: + window.opener.postMessage( + {'type': 'update_cached_book_metadata', 'library_id': cq.library_id, 'book_id': cq.book_id, 'metadata': dirtied[book_id], 'from_read_book': cq.from_read_book}, + document.location.protocol + '//' + document.location.host + ) + else: + for bid in dirtied: + set_book_metadata(bid, dirtied[bid]) on_close(container_id) @@ -994,7 +1002,10 @@ def on_close(container_id): q = parse_url_params() show_book(container_id, int(q.book_id)) return - back() + if get_current_query().from_read_book: + window.close() + else: + back() def proceed_after_succesful_fetch_metadata(container_id, book_id): @@ -1054,6 +1065,18 @@ def init(container_id): container.lastChild.focus() container.lastChild.appendChild(E.div(_('Loading books from the calibre library, please wait...'), style='margin: 1ex 1em')) conditional_timeout(container_id, 5, check_for_books_loaded) + cq = get_current_query() + if cq.from_read_book: + window.opener.postMessage( + {'type': 'edit_metadata_opened', 'library_id': cq.library_id, 'book_id': cq.book_id, 'from_read_book': cq.from_read_book}, + document.location.protocol + '//' + document.location.host + ) + window.addEventListener('unload', def (ev): + window.opener.postMessage( + {'type': 'edit_metadata_closed', 'from_read_book': cq.from_read_book}, + document.location.protocol + '//' + document.location.host + ) + ) set_panel_handler('edit_metadata', init) diff --git a/src/pyj/read_book/db.pyj b/src/pyj/read_book/db.pyj index 5374086224..a68dca47f2 100644 --- a/src/pyj/read_book/db.pyj +++ b/src/pyj/read_book/db.pyj @@ -297,6 +297,12 @@ class DB: book.last_read[unkey] = book.recent_date = now self.do_op(['books'], book, _('Failed to write to the books database'), op='put') + def update_metadata(self, book, new_metadata): + if book.metadata: + for key in Object.keys(new_metadata): + book.metadata[key] = new_metadata[key] + self.do_op(['books'], book, _('Failed to write to the books database'), op='put') + def update_annotations_data_from_key(self, library_id, book_id, fmt, new_data): unkey = username_key(get_interface_data().username) self.get_book(library_id, book_id, fmt, None, def(book): diff --git a/src/pyj/read_book/overlay.pyj b/src/pyj/read_book/overlay.pyj index 8dfb53e96f..2acfaa4b7a 100644 --- a/src/pyj/read_book/overlay.pyj +++ b/src/pyj/read_book/overlay.pyj @@ -4,12 +4,16 @@ from __python__ import bound_methods, hash_literals from elementmaker import E from gettext import gettext as _ +from uuid import short_uuid from book_list.book_details import CLASS_NAME as BD_CLASS_NAME, render_metadata from book_list.globals import get_session_data -from book_list.library_data import sync_library_books +from book_list.library_data import ( + current_library_id, set_book_metadata, sync_library_books +) from book_list.router import home from book_list.theme import get_color +from book_list.ui import query_as_href from dom import add_extra_css, build_rule, clear, set_css, svgicon, unique_id from modals import error_dialog from read_book.globals import runtime, ui_operations @@ -665,10 +669,44 @@ class Overlay: def show_metadata(self): self.hide_current_panel() + from_read_book = short_uuid() - def show_metadata_overlay(mi, pathtoebook, overlay, container): - container.appendChild(E.div(class_=BD_CLASS_NAME, style='padding: 1ex 1em')) + def show_metadata_overlay(mi, pathtoebook, lname, book_id, overlay, container): + container.appendChild(E.div(class_=BD_CLASS_NAME, style='padding: 1ex 1em', id=from_read_book)) table = E.table(class_='metadata') + + def handle_message(msg): + data = msg.data + if data.from_read_book is not from_read_book: + return + if data.type is 'edit_metadata_opened': + if document.getElementById(from_read_book) and self.panels.length and self.panels[-1].from_read_book is from_read_book: + self.hide_current_panel() + return + if data.type is 'edit_metadata_closed': + ui_operations.stop_waiting_for_messages_from(this) + return + if data.type is 'update_cached_book_metadata' and data.metadata: + if current_library_id() is data.library_id: + mi = data.metadata + book_id = int(data.book_id) + set_book_metadata(book_id, mi) + book = self.view.book + if book and book.key[0] is data.library_id and book.key[1] is book_id: + ui_operations.update_metadata(book, mi) + + if not runtime.is_standalone_viewer and lname: + + container.lastChild.appendChild(E.div( + style='text-align: right', + E.a(_('Edit metadata of book in library'), class_='blue-link', onclick=def(): + w = window.open(query_as_href({ + 'from_read_book': from_read_book, 'library_id': lname, 'book_id': book_id + ''}, 'edit_metadata') + , '_blank' + ) + ui_operations.wait_for_messages_from(w, handle_message.bind(w)) + ) + )) container.lastChild.appendChild(table) render_metadata(mi, table, None, f'html {{ font-size: {document.documentElement.style.fontSize} }}') for a in table.querySelectorAll('a[href]'): @@ -680,7 +718,13 @@ class Overlay: style='margin-top: 1ex; padding-top: 1ex; border-top: solid 1px', _('Path: {}').format(pathtoebook))) - self.panels.push(SimpleOverlay(self, show_metadata_overlay.bind(None, self.view.book.metadata, self.view.book.manifest.pathtoebook), self.view.book.metadata.title)) + book = self.view.book + key = book.key or v'[null, 0]' + lname, book_id = key + book_id = int(book_id or 0) + panel = SimpleOverlay(self, show_metadata_overlay.bind(None, book.metadata, book.manifest.pathtoebook, lname, book_id), self.view.book.metadata.title) + panel.from_read_book = from_read_book + self.panels.push(panel) self.show_current_panel() def show_ask_for_location(self): diff --git a/src/pyj/read_book/ui.pyj b/src/pyj/read_book/ui.pyj index e1fe7c06a1..c711c1ceda 100644 --- a/src/pyj/read_book/ui.pyj +++ b/src/pyj/read_book/ui.pyj @@ -53,7 +53,9 @@ class ReadUI: id=self.display_id, style='display:none', )) self.view = View(container.lastChild) + self.windows_to_listen_for_messages_from = [] window.addEventListener('resize', debounce(self.on_resize.bind(self), 250)) + window.addEventListener('message', self.message_from_other_window.bind(self)) self.db = get_db(self.db_initialized.bind(self), self.show_error.bind(self)) ui_operations.get_file = self.db.get_file ui_operations.get_mathjax_files = self.db.get_mathjax_files @@ -75,6 +77,9 @@ class ReadUI: ui_operations.toggle_full_screen = self.toggle_full_screen.bind(self) ui_operations.highlights_changed = self.highlights_changed.bind(self) ui_operations.annotations_synced = self.annotations_synced.bind(self) + ui_operations.wait_for_messages_from = self.wait_for_messages_from.bind(self) + ui_operations.stop_waiting_for_messages_from = self.stop_waiting_for_messages_from.bind(self) + ui_operations.update_metadata = self.update_metadata.bind(self) ui_operations.open_url = def(url): window.open(url, '_blank') ui_operations.copy_selection = def(text): @@ -534,3 +539,21 @@ class ReadUI: def update_url_state(self, replace): push_state(self.url_data, replace=replace, mode=read_book_mode) + + def update_metadata(self, book, metadata): + self.db.update_metadata(book, metadata) + + def wait_for_messages_from(self, w, callback): + self.windows_to_listen_for_messages_from.push(v'[w, callback]') + + def stop_waiting_for_messages_from(self, w): + self.windows_to_listen_for_messages_from = [x for x in self.windows_to_listen_for_messages_from if x[0] is not w] + + def message_from_other_window(self, msg): + if not self.windows_to_listen_for_messages_from.length: + return + old = self.windows_to_listen_for_messages_from + for x in old: + w, callback = x + if w is msg.source: + callback(msg)