Add syncing of annotations along with last read position

This commit is contained in:
Kovid Goyal 2020-07-01 12:40:42 +05:30
parent 0c4010b3af
commit 14b87b8446
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 55 additions and 12 deletions

View File

@ -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]

View File

@ -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()

View File

@ -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:

View File

@ -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)

View File

@ -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)
# }}}

View File

@ -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}

View File

@ -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)