From 87b72fbd30d670e970e239114877275fa829bfa7 Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Sun, 19 Jul 2020 10:43:55 +0000 Subject: [PATCH] Support 'System' theme setting (#91) --- public/css/mango.css | 10 ++--- public/js/admin.js | 33 +++++++++++---- public/js/download.js | 86 +++++++++++++++++++-------------------- public/js/theme.js | 57 ++++++++++++++++++++------ src/views/admin.html.ecr | 8 ++++ src/views/layout.html.ecr | 2 +- src/views/login.html.ecr | 2 +- 7 files changed, 126 insertions(+), 72 deletions(-) diff --git a/public/css/mango.css b/public/css/mango.css index 2f452ea..0227126 100644 --- a/public/css/mango.css +++ b/public/css/mango.css @@ -30,18 +30,18 @@ cursor: pointer; } -.uk-list li { +.uk-list li:not(.nopointer) { cursor: pointer; } -.reader-bg { - background-color: black; -} - #scan-status { cursor: auto; } +.reader-bg { + background-color: black; +} + .break-word { word-wrap: break-word; } diff --git a/public/js/admin.js b/public/js/admin.js index 6968755..5c9051d 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -1,13 +1,14 @@ -var scanning = false; -function scan() { +let scanning = false; + +const scan = () => { scanning = true; $('#scan-status > div').removeAttr('hidden'); $('#scan-status > span').attr('hidden', ''); - var color = $('#scan').css('color'); + const color = $('#scan').css('color'); $('#scan').css('color', 'gray'); - $.post(base_url + 'api/admin/scan', function (data) { - var ms = data.milliseconds; - var titles = data.titles; + $.post(base_url + 'api/admin/scan', (data) => { + const ms = data.milliseconds; + const titles = data.titles; $('#scan-status > span').text('Scanned ' + titles + ' titles in ' + ms + 'ms'); $('#scan-status > span').removeAttr('hidden'); $('#scan').css('color', color); @@ -15,11 +16,25 @@ function scan() { scanning = false; }); } -$(function() { - $('li').click(function() { - url = $(this).attr('data-url'); + +String.prototype.capitalize = function() { + return this.charAt(0).toUpperCase() + this.slice(1); +} + +$(() => { + $('li').click((e) => { + const url = $(e.currentTarget).attr('data-url'); if (url) { $(location).attr('href', url); } }); + + const setting = loadThemeSetting(); + $('#theme-select').val(setting.capitalize()); + + $('#theme-select').change((e) => { + const newSetting = $(e.currentTarget).val().toLowerCase(); + saveThemeSetting(newSetting); + setTheme(); + }); }); diff --git a/public/js/download.js b/public/js/download.js index 5c848d7..e3a014c 100644 --- a/public/js/download.js +++ b/public/js/download.js @@ -32,32 +32,34 @@ const download = () => { const chapters = globalChapters.filter(c => ids.indexOf(c.id) >= 0); console.log(ids); $.ajax({ - type: 'POST', - url: base_url + 'api/admin/mangadex/download', - data: JSON.stringify({chapters: chapters}), - contentType: "application/json", - dataType: 'json' - }) - .done(data => { - console.log(data); - if (data.error) { - alert('danger', `Failed to add chapters to the download queue. Error: ${data.error}`); - return; - } - const successCount = parseInt(data.success); - const failCount = parseInt(data.fail); - UIkit.modal.confirm(`${successCount} of ${successCount + failCount} chapters added to the download queue. Proceed to the download manager?`).then(() => { - window.location.href = base_url + 'admin/downloads'; + type: 'POST', + url: base_url + 'api/admin/mangadex/download', + data: JSON.stringify({ + chapters: chapters + }), + contentType: "application/json", + dataType: 'json' + }) + .done(data => { + console.log(data); + if (data.error) { + alert('danger', `Failed to add chapters to the download queue. Error: ${data.error}`); + return; + } + const successCount = parseInt(data.success); + const failCount = parseInt(data.fail); + UIkit.modal.confirm(`${successCount} of ${successCount + failCount} chapters added to the download queue. Proceed to the download manager?`).then(() => { + window.location.href = base_url + 'admin/downloads'; + }); + styleModal(); + }) + .fail((jqXHR, status) => { + alert('danger', `Failed to add chapters to the download queue. Error: [${jqXHR.status}] ${jqXHR.statusText}`); + }) + .always(() => { + $('#download-spinner').attr('hidden', ''); + $('#download-btn').removeAttr('hidden'); }); - styleModal(); - }) - .fail((jqXHR, status) => { - alert('danger', `Failed to add chapters to the download queue. Error: [${jqXHR.status}] ${jqXHR.statusText}`); - }) - .always(() => { - $('#download-spinner').attr('hidden', ''); - $('#download-btn').removeAttr('hidden'); - }); }); styleModal(); }; @@ -66,8 +68,7 @@ const toggleSpinner = () => { if (attr) { $('#spinner').removeAttr('hidden'); $('#search-btn').attr('hidden', ''); - } - else { + } else { $('#search-btn').removeAttr('hidden'); $('#spinner').attr('hidden', ''); } @@ -98,8 +99,7 @@ const search = () => { const path = new URL(input).pathname; const match = /\/title\/([0-9]+)/.exec(path); int_id = parseInt(match[1]); - } - catch(e) { + } catch (e) { int_id = parseInt(input); } @@ -139,8 +139,12 @@ const search = () => { const comp = (a, b) => { var ai; var bi; - try {ai = parseFloat(a);} catch(e) {} - try {bi = parseFloat(b);} catch(e) {} + try { + ai = parseFloat(a); + } catch (e) {} + try { + bi = parseFloat(b); + } catch (e) {} if (typeof ai === 'undefined') return -1; if (typeof bi === 'undefined') return 1; if (ai < bi) return 1; @@ -176,8 +180,7 @@ const parseRange = str => { if (!matches) { alert('danger', `Failed to parse filter input ${str}`); return [null, null]; - } - else if (typeof matches[1] !== 'undefined' && typeof matches[2] !== 'undefined') { + } else if (typeof matches[1] !== 'undefined' && typeof matches[2] !== 'undefined') { // e.g., <= 30 num = parseInt(matches[2]); if (isNaN(num)) { @@ -194,8 +197,7 @@ const parseRange = str => { case '>=': return [num, null]; } - } - else if (typeof matches[3] !== 'undefined') { + } else if (typeof matches[3] !== 'undefined') { // a single number num = parseInt(matches[3]); if (isNaN(num)) { @@ -203,8 +205,7 @@ const parseRange = str => { return [null, null]; } return [num, num]; - } - else if (typeof matches[4] !== 'undefined' && typeof matches[5] !== 'undefined') { + } else if (typeof matches[4] !== 'undefined' && typeof matches[5] !== 'undefined') { // e.g., 10 - 23 num = parseInt(matches[4]); const n2 = parseInt(matches[5]); @@ -213,8 +214,7 @@ const parseRange = str => { return [null, null]; } return [num, n2]; - } - else { + } else { // empty or space only return [null, null]; } @@ -280,7 +280,7 @@ const buildTable = () => { const group_str = Object.entries(chp.groups).map(([k, v]) => { return `${k}`; }).join(' | '); - const dark = getTheme() === 'dark' ? 'dark' : ''; + const dark = loadTheme() === 'dark' ? 'dark' : ''; return ` ${chp.id} ${chp.title} @@ -302,7 +302,7 @@ const buildTable = () => { }; const unescapeHTML = (str) => { - var elt = document.createElement("span"); - elt.innerHTML = str; - return elt.innerText; + var elt = document.createElement("span"); + elt.innerHTML = str; + return elt.innerText; }; diff --git a/public/js/theme.js b/public/js/theme.js index efdd16c..78407e1 100644 --- a/public/js/theme.js +++ b/public/js/theme.js @@ -1,22 +1,44 @@ -const getTheme = () => { - var theme = localStorage.getItem('theme'); - if (!theme) theme = 'light'; - return theme; +// https://flaviocopes.com/javascript-detect-dark-mode/ +const preferDarkMode = () => { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; }; -const saveTheme = theme => { - localStorage.setItem('theme', theme); +const validThemeSetting = (theme) => { + return ['dark', 'light', 'system'].indexOf(theme) >= 0; }; +// dark / light / system +const loadThemeSetting = () => { + let str = localStorage.getItem('theme'); + if (!str || !validThemeSetting(str)) str = 'light'; + return str; +}; + +// dark / light +const loadTheme = () => { + let setting = loadThemeSetting(); + if (setting === 'system') { + setting = preferDarkMode() ? 'dark' : 'light'; + } + return setting; +}; + +const saveThemeSetting = setting => { + if (!validThemeSetting(setting)) setting = 'light'; + localStorage.setItem('theme', setting); +}; + +// when toggled, Auto will be changed to light or dark const toggleTheme = () => { - const theme = getTheme(); + const theme = loadTheme(); const newTheme = theme === 'dark' ? 'light' : 'dark'; + saveThemeSetting(newTheme); setTheme(newTheme); - saveTheme(newTheme); }; -const setTheme = themeStr => { - if (themeStr === 'dark') { +const setTheme = (theme) => { + if (!theme) theme = loadTheme(); + if (theme === 'dark') { $('html').css('background', 'rgb(20, 20, 20)'); $('body').addClass('uk-light'); $('.uk-card').addClass('uk-card-secondary'); @@ -32,7 +54,7 @@ const setTheme = themeStr => { }; const styleModal = () => { - const color = getTheme() === 'dark' ? '#222' : ''; + const color = loadTheme() === 'dark' ? '#222' : ''; $('.uk-modal-header').css('background', color); $('.uk-modal-body').css('background', color); $('.uk-modal-footer').css('background', color); @@ -40,9 +62,18 @@ const styleModal = () => { // do it before document is ready to prevent the initial flash of white on // most pages -setTheme(getTheme()); +setTheme(); $(() => { // hack for the reader page - setTheme(getTheme()); + setTheme(); + + // on system dark mode setting change + if (window.matchMedia) { + window.matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', event => { + if (loadThemeSetting() === 'system') + setTheme(event.matches ? 'dark' : 'light'); + }); + } }); diff --git a/src/views/admin.html.ecr b/src/views/admin.html.ecr index b3e2d96..d7e101e 100644 --- a/src/views/admin.html.ecr +++ b/src/views/admin.html.ecr @@ -7,6 +7,14 @@ +
  • + Theme + +
  • Download Manager
  • diff --git a/src/views/layout.html.ecr b/src/views/layout.html.ecr index 2800ddc..f98d3f8 100644 --- a/src/views/layout.html.ecr +++ b/src/views/layout.html.ecr @@ -56,7 +56,7 @@ diff --git a/src/views/login.html.ecr b/src/views/login.html.ecr index 1bc122a..be186c5 100644 --- a/src/views/login.html.ecr +++ b/src/views/login.html.ecr @@ -27,7 +27,7 @@