mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Add syncing of annotations along with last read position
This commit is contained in:
parent
0c4010b3af
commit
14b87b8446
@ -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]
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
# }}}
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user