diff --git a/public/js/download.js b/public/js/download.js index 1ae23bb..aca45eb 100644 --- a/public/js/download.js +++ b/public/js/download.js @@ -95,7 +95,7 @@ const search = () => { try { const path = new URL(input).pathname; - const match = /\/title\/([0-9]+)/.exec(path); + const match = /\/(?:title|manga)\/([0-9]+)/.exec(path); int_id = parseInt(match[1]); } catch (e) { int_id = parseInt(input); diff --git a/public/js/reader.js b/public/js/reader.js index 479cd08..ac5b92a 100644 --- a/public/js/reader.js +++ b/public/js/reader.js @@ -5,12 +5,68 @@ let longPages = false; $(() => { getPages(); + const storedMode = localStorage.getItem('mode') || 'continuous'; + + setProp('mode', storedMode); + updateMode(storedMode, page); + $('#mode-select').val(storedMode); + $('#page-select').change(() => { const p = parseInt($('#page-select').val()); toPage(p); }); + + $('#mode-select').change(() => { + const mode = $('#mode-select').val(); + const curIdx = parseInt($('#page-select').val()); + + updateMode(mode, curIdx); + }); }); +$(window).resize(() => { + const mode = getProp('mode'); + if (mode === 'continuous') return; + + const wideScreen = $(window).width() > $(window).height(); + const propMode = wideScreen ? 'height' : 'width'; + setProp('mode', propMode); +}); + +/** + * Update the reader mode + * + * @function updateMode + * @param {string} mode - The mode. Can be one of the followings: + * {'continuous', 'paged', 'height', 'width'} + * @param {number} targetPage - The one-based index of the target page + */ +const updateMode = (mode, targetPage) => { + localStorage.setItem('mode', mode); + + // The mode to be put into the `mode` prop. It can't be `screen` + let propMode = mode; + + if (mode === 'paged') { + const wideScreen = $(window).width() > $(window).height(); + propMode = wideScreen ? 'height' : 'width'; + } + + setProp('mode', propMode); + + if (mode === 'continuous') { + waitForPage(items.length, () => { + setupScroller(); + }); + } + + waitForPage(targetPage, () => { + setTimeout(() => { + toPage(targetPage); + }, 100); + }); +}; + /** * Set an alpine.js property * @@ -22,6 +78,17 @@ const setProp = (key, prop) => { $('#root').get(0).__x.$data[key] = prop; }; +/** + * Get an alpine.js property + * + * @function getProp + * @param {string} key - Key of the data property + * @return {*} The data property + */ +const getProp = (key) => { + return $('#root').get(0).__x.$data[key]; +}; + /** * Get dimension of the pages in the entry from the API and update the view */ @@ -50,11 +117,6 @@ const getPages = () => { setProp('items', items); setProp('loading', false); - - waitForPage(items.length, () => { - toPage(page); - setupScroller(); - }); }) .catch(e => { const errMsg = `Failed to get the page dimensions. ${e}`; @@ -71,7 +133,15 @@ const getPages = () => { * @param {number} idx - One-based index of the page */ const toPage = (idx) => { - $(`#${idx}`).get(0).scrollIntoView(true); + const mode = getProp('mode'); + if (mode === 'continuous') { + $(`#${idx}`).get(0).scrollIntoView(true); + } else { + if (idx >= 1 && idx <= items.length) { + setProp('curItem', items[idx - 1]); + } + } + replaceHistory(idx); UIkit.modal($('#modal-sections')).hide(); }; @@ -137,6 +207,8 @@ const replaceHistory = (idx) => { * @function setupScroller */ const setupScroller = () => { + const mode = getProp('mode'); + if (mode !== 'continuous') return; $('#root img').each((idx, el) => { $(el).on('inview', (event, inView) => { if (inView) { @@ -193,3 +265,30 @@ const nextEntry = (nextUrl) => { redirect(nextUrl); }); }; + +/** + * Show the next or the previous page + * + * @function flipPage + * @param {bool} isNext - Whether we are going to the next page + */ +const flipPage = (isNext) => { + const curItem = getProp('curItem'); + const idx = parseInt(curItem.id); + const delta = isNext ? 1 : -1; + const newIdx = idx + delta; + + toPage(newIdx); + + if (isNext) + setProp('flipAnimation', 'right'); + else + setProp('flipAnimation', 'left'); + + setTimeout(() => { + setProp('flipAnimation', null); + }, 500); + + replaceHistory(newIdx); + saveProgress(newIdx); +}; diff --git a/src/config.cr b/src/config.cr index 91a6667..60c2e40 100644 --- a/src/config.cr +++ b/src/config.cr @@ -18,6 +18,7 @@ class Config home: true property plugin_path : String = File.expand_path "~/mango/plugins", home: true + property download_timeout_seconds : Int32 = 30 property mangadex = Hash(String, String | Int32).new @[YAML::Field(ignore: true)] diff --git a/src/util/proxy.cr b/src/util/proxy.cr index 26c8d5a..2325419 100644 --- a/src/util/proxy.cr +++ b/src/util/proxy.cr @@ -5,7 +5,7 @@ require "http_proxy" module HTTP class Client private def self.exec(uri : URI, tls : TLSContext = nil) - Logger.debug "Using monkey-patched HTTP::Client" + Logger.debug "Setting proxy" previous_def uri, tls do |client, path| client.set_proxy get_proxy uri yield client, path diff --git a/src/util/web.cr b/src/util/web.cr index 041e25e..1bd38ec 100644 --- a/src/util/web.cr +++ b/src/util/web.cr @@ -81,3 +81,15 @@ macro get_sort_opt sort_opt = SortOptions.new sort_method, is_ascending end end + +module HTTP + class Client + private def self.exec(uri : URI, tls : TLSContext = nil) + Logger.debug "Setting read timeout" + previous_def uri, tls do |client, path| + client.read_timeout = Config.current.download_timeout_seconds.seconds + yield client, path + end + end + end +end diff --git a/src/views/reader.html.ecr b/src/views/reader.html.ecr index a8983a0..b1acde8 100644 --- a/src/views/reader.html.ecr +++ b/src/views/reader.html.ecr @@ -3,41 +3,67 @@ <%= render_component "head" %> -
-