From 97d34c5bb0de21a8c2ad2cb47ead64b2d3de89ff Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 15 Jan 2017 21:55:06 +0100 Subject: [PATCH] Better handling of tap on invalid/external links Tap on external urls or invalid links would jump to document's 1st page. We check first the validity of the link, and deal with wikipedia links or just show the external url. ConfirmBox: allow dismissing/cancelling by taping outside, to keep UX consistent. --- frontend/apps/reader/modules/readerlink.lua | 54 +++++++++++++++++++-- frontend/ui/widget/confirmbox.lua | 29 +++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/frontend/apps/reader/modules/readerlink.lua b/frontend/apps/reader/modules/readerlink.lua index a6b423132..ce42261a0 100644 --- a/frontend/apps/reader/modules/readerlink.lua +++ b/frontend/apps/reader/modules/readerlink.lua @@ -5,8 +5,10 @@ local UIManager = require("ui/uimanager") local Geom = require("ui/geometry") local Screen = require("device").screen local Device = require("device") +local logger = require("logger") local Event = require("ui/event") local _ = require("gettext") +local T = require("ffi/util").template local ReaderLink = InputContainer:new{ location_stack = {} @@ -141,13 +143,57 @@ function ReaderLink:onTap(_, ges) end function ReaderLink:onGotoLink(link) + logger.dbg("onGotoLink:", link) if self.ui.document.info.has_pages then - table.insert(self.location_stack, self.ui.paging:getBookLocation()) - self.ui:handleEvent(Event:new("GotoPage", link.page + 1)) + -- internal pdf links have a "page" attribute, while external ones have an "uri" attribute + if link.page then -- Internal link + logger.dbg("Internal link:", link) + table.insert(self.location_stack, self.ui.paging:getBookLocation()) + self.ui:handleEvent(Event:new("GotoPage", link.page + 1)) + return true + end + link = link.uri -- external link else - table.insert(self.location_stack, self.ui.rolling:getBookLocation()) - self.ui:handleEvent(Event:new("GotoXPointer", link)) + -- For crengine, internal links may look like : + -- #_doc_fragment_0_Organisation (link from anchor) + -- /body/DocFragment/body/ul[2]/li[5]/text()[3].16 (xpointer from full-text search) + -- If the XPointer does not exist (or is a full url), we will jump to page 1 + -- Best to check that this link exists in document with the following, + -- which accepts both of the above legitimate xpointer as input. + if self.ui.document:isXPointerInDocument(link) then + logger.dbg("Internal link:", link) + table.insert(self.location_stack, self.ui.rolling:getBookLocation()) + self.ui:handleEvent(Event:new("GotoXPointer", link)) + return true + end end + logger.dbg("External link:", link) + -- Check if it is a wikipedia link + local wiki_lang, wiki_page = link:match([[https?://([^%.]+).wikipedia.org/wiki/([^/]+)]]) + if wiki_lang and wiki_page then + logger.dbg("Wikipedia link:", wiki_lang, wiki_page) + -- Ask for user confirmation before launching lookup (on a + -- wikipedia page saved as epub, full of wikipedia links, it's + -- too easy to click on links when wanting to change page...) + local ConfirmBox = require("ui/widget/confirmbox") + UIManager:show(ConfirmBox:new{ + text = T(_("Would you like to read this Wikipedia %1 full page?\n\n%2\n"), wiki_lang:upper(), wiki_page:gsub("_", " ")), + cancel_on_tap_outside = true, + ok_callback = function() + UIManager:nextTick(function() + self.ui:handleEvent(Event:new("LookupWikipedia", wiki_page, false, true, wiki_lang)) + end) + end + }) + else + -- local Notification = require("ui/widget/notification") + local InfoMessage = require("ui/widget/infomessage") + UIManager:show(InfoMessage:new{ + text = T(_("Invalid or external link:\n%1"), link), + timeout = 1.0, + }) + end + -- don't propagate, user will notice and tap elsewhere if he wants to change page return true end diff --git a/frontend/ui/widget/confirmbox.lua b/frontend/ui/widget/confirmbox.lua index 0caad48a0..71bc666ff 100644 --- a/frontend/ui/widget/confirmbox.lua +++ b/frontend/ui/widget/confirmbox.lua @@ -22,6 +22,8 @@ local ImageWidget = require("ui/widget/imagewidget") local TextBoxWidget = require("ui/widget/textboxwidget") local HorizontalSpan = require("ui/widget/horizontalspan") local ButtonTable = require("ui/widget/buttontable") +local GestureRange = require("ui/gesturerange") +local Geom = require("ui/geometry") local UIManager = require("ui/uimanager") local Device = require("device") local Screen = Device.screen @@ -44,6 +46,23 @@ local ConfirmBox = InputContainer:new{ } function ConfirmBox:init() + if Device:isTouchDevice() then + self.ges_events.TapClose = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + } + } + end + if Device:hasKeys() then + self.key_events = { + Close = { {"Back"}, doc = "cancel" } + } + end local content = HorizontalGroup:new{ align = "center", ImageWidget:new{ @@ -110,10 +129,20 @@ function ConfirmBox:onCloseWidget() end function ConfirmBox:onClose() + -- Call cancel_callback, parent may expect a choice + self.cancel_callback() UIManager:close(self) return true end +function ConfirmBox:onTapClose(arg, ges) + if ges.pos:notIntersectWith(self[1][1].dimen) then + self:onClose() + return true + end + return false +end + function ConfirmBox:onSelect() logger.dbg("selected:", self.selected.x) if self.selected.x == 1 then