From c44bd3f94c53b237630ffd5b5d1d3ee7b582bbd7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 15 Mar 2021 10:53:26 +0530 Subject: [PATCH] Content server: Allow swiping left and right to show next/previous book on the book details page. Fixes #1918047 [[Enhancement - Content server] Make it possible to go to previous/next book by swiping in the Book details page](https://bugs.launchpad.net/calibre/+bug/1918047) --- src/pyj/book_list/book_details.pyj | 72 ++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/src/pyj/book_list/book_details.pyj b/src/pyj/book_list/book_details.pyj index 937513a551..5d573d544d 100644 --- a/src/pyj/book_list/book_details.pyj +++ b/src/pyj/book_list/book_details.pyj @@ -2,10 +2,9 @@ # License: GPL v3 Copyright: 2016, Kovid Goyal from __python__ import hash_literals -import traceback from elementmaker import E -from gettext import gettext as _ +import traceback from ajax import ajax, ajax_send, encode_query_component from book_list.delete_book import refresh_after_delete, start_delete_book from book_list.globals import get_session_data @@ -22,14 +21,17 @@ from book_list.ui import query_as_href, set_panel_handler, show_panel from book_list.views import search_query_for from date import format_date from dom import add_extra_css, build_rule, clear, ensure_id, svgicon, unique_id +from gettext import gettext as _ from modals import create_custom_dialog, error_dialog, warning_dialog +from read_book.touch import ( + copy_touch, install_handlers, interpret_single_gesture, touch_id, update_touch +) from session import get_interface_data from utils import ( conditional_timeout, debounce, fmt_sidx, human_readable, parse_url_params, safe_set_inner_html, sandboxed_html ) from widgets import create_button, create_spinner -from utils import human_readable bd_counter = 0 @@ -117,6 +119,15 @@ def setup_iframe(iframe): for a in de.querySelectorAll('a[href]'): a.setAttribute('target', '_blank') + def forward_touch_events(ev): + container = window.top.document.getElementById(render_book.container_id) + if container: + dup = v'new ev.constructor(ev.type, ev)' + container.dispatchEvent(dup) + + for key in ('start', 'move', 'end', 'cancel'): + iframe.contentWindow.addEventListener(f'touch{key}', forward_touch_events) + def adjust_all_iframes(ev): for iframe in document.querySelectorAll(f'.{CLASS_NAME} iframe'): @@ -467,10 +478,12 @@ def next_book(book_id, delta): def render_book(container_id, book_id): render_book.book_id = book_id + render_book.container_id = container_id is_random = parse_url_params().book_id is '0' c = document.getElementById(container_id) if not c: return + install_touch_handlers(c, book_id) metadata = book_metadata(book_id) render_book.title = metadata.title set_title(c, metadata.title) @@ -597,6 +610,59 @@ def fetch_metadata(container_id, book_id, proceed): )) +def install_touch_handlers(container, book_id): + ongoing_touches = {} + gesture_id = 0 + container_id = container.id + + def has_active_touches(): + for tid in ongoing_touches: + t = ongoing_touches[tid] + if t.active: + return True + return False + + def handle_touch(ev): + nonlocal gesture_id, ongoing_touches + container = document.getElementById(container_id) + if not container: + return + if ev.type is 'touchstart': + for touch in ev.changedTouches: + ongoing_touches[touch_id(touch)] = copy_touch(touch) + gesture_id += 1 + elif ev.type is 'touchmove': + for touch in ev.changedTouches: + t = ongoing_touches[touch_id(touch)] + if t: + update_touch(t, touch) + elif ev.type is 'touchcancel': + for touch in ev.changedTouches: + v'delete ongoing_touches[touch_id(touch)]' + elif ev.type is 'touchend': + for touch in ev.changedTouches: + t = ongoing_touches[touch_id(touch)] + if t: + t.active = False + update_touch(t, touch) + if not has_active_touches(): + touches = ongoing_touches + ongoing_touches = {} + num = len(touches) + if num is 1: + gesture = interpret_single_gesture(touches[Object.keys(touches)[0]], gesture_id) + if gesture.type is 'swipe' and gesture.axis is 'horizontal': + delta = -1 if gesture.direction is 'right' else 1 + next_book(book_id, delta) + + install_handlers(container, { + 'handle_touchstart': handle_touch, + 'handle_touchmove': handle_touch, + 'handle_touchend': handle_touch, + 'handle_touchcancel': handle_touch, + }, True) + + def create_book_details(container): q = parse_url_params() current_book_id = q.book_id