diff --git a/src/pyj/book_list/home.pyj b/src/pyj/book_list/home.pyj index 586e61624d..b129f5a36c 100644 --- a/src/pyj/book_list/home.pyj +++ b/src/pyj/book_list/home.pyj @@ -62,10 +62,9 @@ def sync_data_received(library_id, lrmap, load_type, xhr, ev): print('Failed to get book sync data') return data = JSON.parse(xhr.responseText) - last_read_data = data.last_read_positions db = get_db() - for key in last_read_data: + for key in data: new_vals = data[key] entry = {'last_read': None, 'last_read_position': None, 'annotations_map': None} prev_last_read = lrmap[key] diff --git a/src/pyj/range_utils.pyj b/src/pyj/range_utils.pyj index 3e7216146a..33511a72e8 100644 --- a/src/pyj/range_utils.pyj +++ b/src/pyj/range_utils.pyj @@ -53,6 +53,11 @@ def unwrap_crw(crw): unwrap(node) +def unwrap_all_crw(): + for node in document.querySelectorAll('span[data-calibre-range-wrapper]'): + unwrap(node) + + def select_crw(crw): nodes = document.querySelectorAll(f'span[data-calibre-range-wrapper="{crw}"]') r = document.createRange() diff --git a/src/pyj/read_book/create_annotation.pyj b/src/pyj/read_book/create_annotation.pyj index 0f3a67286c..daaff5f288 100644 --- a/src/pyj/read_book/create_annotation.pyj +++ b/src/pyj/read_book/create_annotation.pyj @@ -10,6 +10,7 @@ from book_list.globals import get_session_data from book_list.theme import cached_color_to_rgba, get_color from dom import clear, ensure_id, svgicon, unique_id from modals import error_dialog, question_dialog +from read_book.annotations import merge_annotation_maps from read_book.globals import ui_operations from read_book.shortcuts import shortcut_for_key_event @@ -24,6 +25,17 @@ class AnnotationsManager: highlights = highlights or v'[]' self.highlights = {h.uuid: h for h in highlights} + def merge_highlights(self, highlights): + highlights = highlights or v'[]' + updated = False + if highlights.length: + base = {'highlight': Object.values(self.highlights)} + newvals = {'highlight': highlights} + updated, ans = merge_annotation_maps(base, newvals) + if updated: + self.set_highlights(ans) + return updated + def remove_highlight(self, uuid): h = self.highlights[uuid] if h: diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 0ccf0de307..55ee6c1254 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -12,8 +12,8 @@ from select import ( from fs_images import fix_fullscreen_svg_images from iframe_comm import IframeClient from range_utils import ( - reset_highlight_counter, select_crw, set_selection_to_highlight, unwrap_crw, - wrap_text_in_range + reset_highlight_counter, select_crw, set_selection_to_highlight, unwrap_all_crw, + unwrap_crw, wrap_text_in_range ) from read_book.cfi import ( cfi_for_selection, range_from_cfi, scroll_to as scroll_to_cfi @@ -138,6 +138,7 @@ class IframeBoss: 'handle_navigation_shortcut': self.on_handle_navigation_shortcut, 'annotations': self.annotations_msg_received, 'copy_selection': self.copy_selection, + 'replace_highlights': self.replace_highlights, } self.comm = IframeClient(handlers) self.last_window_ypos = 0 @@ -770,6 +771,7 @@ class IframeBoss: def apply_highlights_on_load(self, highlights): clear_annot_id_uuid_map() + reset_highlight_counter() strcmp = v'new Intl.Collator().compare' highlights.sort(def (a, b): return strcmp(a.timestamp, b.timestamp);) for h in highlights: @@ -784,6 +786,11 @@ class IframeBoss: unwrap_crw(crw) v'delete annot_id_uuid_map[crw]' + def replace_highlights(self, data): + highlights = data.highlights + unwrap_all_crw() + self.apply_highlights_on_load(highlights or v'[]') + def add_highlight_listeners(self, wrapper): wrapper.addEventListener('dblclick', self.highlight_wrapper_dblclicked) diff --git a/src/pyj/read_book/overlay.pyj b/src/pyj/read_book/overlay.pyj index 767bb84b05..6a29ba52ad 100644 --- a/src/pyj/read_book/overlay.pyj +++ b/src/pyj/read_book/overlay.pyj @@ -131,8 +131,8 @@ class SyncBook: # {{{ container.appendChild(E.div( style='display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%', E.div(style='margin:1ex 1em; max-width: 80vw', - E.h2(_('Syncing to last read position')), - E.p(_('Downloading last read data from server, please wait...')), + E.h2(_('Syncing last read position and annotations')), + E.p(_('Downloading data from server, please wait...')), E.div(style='display:flex; justify-content:flex-end', create_button(_('Cancel'), action=self.cancel), ) @@ -154,25 +154,28 @@ class SyncBook: # {{{ if xhr.responseText is 'login required for sync': error_dialog(_('Failed to fetch sync data'), _('You must setup user accounts and login to use the sync functionality')) else: - error_dialog(_('Failed to fetch sync data'), _('Failed to download last read data from server, click "Show details" for more information.'), xhr.error_html) + error_dialog(_('Failed to fetch sync data'), _('Failed to download sync data from server, click "Show details" for more information.'), xhr.error_html) return data = JSON.parse(xhr.responseText) book = self.overlay.view.book dev = get_device_uuid() epoch = 0 ans = None + new_annotations_map = None for key in data: book_id, fmt = key.partition(':')[::2] if book_id is str(book.key[1]) and fmt.upper() is book.key[2].upper(): - last_read_positions = data[key] + new_vals = data[key] + last_read_positions = new_vals.last_read_positions + new_annotations_map = new_vals.annotations_map for d in last_read_positions: if d.device is not dev and d.epoch > epoch: epoch = d.epoch ans = d - if ans is not None: - cfi = ans.cfi - if cfi: - self.overlay.view.goto_cfi(cfi) + break + cfi = ans?.cfi + if new_annotations_map or cfi: + self.overlay.view.sync_data_received(cfi, new_annotations_map) # }}} diff --git a/src/pyj/read_book/ui.pyj b/src/pyj/read_book/ui.pyj index 4b3fa5e7ff..b228523867 100644 --- a/src/pyj/read_book/ui.pyj +++ b/src/pyj/read_book/ui.pyj @@ -74,6 +74,7 @@ class ReadUI: ui_operations.toggle_toc = self.toggle_toc.bind(self) 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) def on_resize(self): self.view.on_resize() @@ -201,6 +202,12 @@ class ReadUI: self.db.update_annotations_data_from_key(library_id, book_id, fmt, amap) ajax_send(f'book-update-annotations/{library_id}/{book_id}/{fmt}', amap, def (): pass;) + def annotations_synced(self, amap): + library_id = self.base_url_data.library_id + book_id = self.base_url_data.book_id + fmt = self.base_url_data.fmt + self.db.update_annotations_data_from_key(library_id, book_id, fmt, amap) + @property def url_data(self): ans = {'library_id':self.base_url_data.library_id, 'book_id':self.base_url_data.book_id, 'fmt': self.base_url_data.fmt} diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 72e7576d05..30f4afc33d 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -989,6 +989,16 @@ class View: process_node(toc) return found + def sync_data_received(self, reading_pos_cfi, annotations_map): + if annotations_map: + ui_operations.annotations_synced(annotations_map) + if annotations_map.highlight: + if self.annotations_manager.merge_highlights(annotations_map.highlight): + hl = self.annotations_manager.highlights_for_currently_showing() + self.iframe_wrapper.send_message('replace_highlights', highlights=hl) + if reading_pos_cfi: + self.goto_cfi(reading_pos_cfi) + def on_next_spine_item(self, data): spine = self.book.manifest.spine idx = spine.indexOf(self.currently_showing.name)