From 4ded22d4551f20c0e49d3bdb5adf6282fed3c8ad Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 15 Feb 2023 15:56:00 -0800 Subject: [PATCH 01/21] allow scroll to bottom on "embedded blog view" --- src/scripts/scroll_to_bottom.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 768cdfef9..e39ee38d9 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -1,7 +1,7 @@ import { keyToClasses, keyToCss } from '../util/css_map.js'; import { translate } from '../util/language_data.js'; import { pageModifications } from '../util/mutations.js'; -import { blogViewSelector, buildStyle } from '../util/interface.js'; +import { buildStyle } from '../util/interface.js'; const scrollToBottomButtonId = 'xkit-scroll-to-bottom-button'; $(`[id="${scrollToBottomButtonId}"]`).remove(); @@ -17,11 +17,6 @@ let scrollToBottomButton; let active = false; const styleElement = buildStyle(` -${keyToCss('isPeeprShowing')} #${scrollToBottomButtonId} { - opacity: 0; - pointer-events: none; -} - .${activeClass} svg use { --icon-color-primary: rgb(var(--yellow)); } @@ -29,8 +24,7 @@ ${keyToCss('isPeeprShowing')} #${scrollToBottomButtonId} { const scrollToBottom = () => { window.scrollTo({ top: document.documentElement.scrollHeight }); - const loaders = [...document.querySelectorAll(knightRiderLoaderSelector)] - .filter(element => element.matches(blogViewSelector) === false); + const loaders = [...document.querySelectorAll(knightRiderLoaderSelector)]; if (loaders.length === 0) { stopScrolling(); From 2238907a6d1e46018ca73f6a9199779741e3454f Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 15 Feb 2023 16:17:11 -0800 Subject: [PATCH 02/21] WIP: allow scroll to bottom on modal blog view --- src/scripts/scroll_to_bottom.js | 61 +++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index e39ee38d9..083bf705a 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -3,8 +3,8 @@ import { translate } from '../util/language_data.js'; import { pageModifications } from '../util/mutations.js'; import { buildStyle } from '../util/interface.js'; -const scrollToBottomButtonId = 'xkit-scroll-to-bottom-button'; -$(`[id="${scrollToBottomButtonId}"]`).remove(); +const buttonClass = 'xkit-scroll-to-bottom-button'; +$(`.${buttonClass}`).remove(); const activeClass = 'xkit-scroll-to-bottom-active'; const loaderSelector = ` @@ -14,8 +14,11 @@ ${keyToCss('notifications')} + ${keyToCss('loader')} const knightRiderLoaderSelector = `:is(${loaderSelector}) > ${keyToCss('knightRiderLoader')}`; let scrollToBottomButton; +let modalScrollToBottomButton; let active = false; +let scrollElement; + const styleElement = buildStyle(` .${activeClass} svg use { --icon-color-primary: rgb(var(--yellow)); @@ -23,8 +26,8 @@ const styleElement = buildStyle(` `); const scrollToBottom = () => { - window.scrollTo({ top: document.documentElement.scrollHeight }); - const loaders = [...document.querySelectorAll(knightRiderLoaderSelector)]; + scrollElement.scrollTo({ top: scrollElement.scrollHeight }); + const loaders = [...scrollElement.querySelectorAll(knightRiderLoaderSelector)]; if (loaders.length === 0) { stopScrolling(); @@ -33,9 +36,13 @@ const scrollToBottom = () => { const observer = new ResizeObserver(scrollToBottom); const startScrolling = () => { - observer.observe(document.documentElement); + const modalScrollContainer = document.querySelector(`${keyToCss('drawerContent')} > ${keyToCss('scrollContainer')}`); + + scrollElement = modalScrollContainer || document.documentElement; + observer.observe(modalScrollContainer?.firstElementChild || document.documentElement); active = true; scrollToBottomButton.classList.add(activeClass); + modalScrollToBottomButton.classList.add(activeClass); scrollToBottom(); }; @@ -43,18 +50,19 @@ const stopScrolling = () => { observer.disconnect(); active = false; scrollToBottomButton?.classList.remove(activeClass); + modalScrollToBottomButton?.classList.remove(activeClass); }; const onClick = () => active ? stopScrolling() : startScrolling(); const onKeyDown = ({ key }) => key === '.' && stopScrolling(); -const checkForButtonRemoved = () => { - const buttonWasRemoved = document.documentElement.contains(scrollToBottomButton) === false; - if (buttonWasRemoved) { - if (active) stopScrolling(); - pageModifications.unregister(checkForButtonRemoved); - } -}; +// const checkForButtonRemoved = () => { +// const buttonWasRemoved = document.documentElement.contains(scrollToBottomButton) === false; +// if (buttonWasRemoved) { +// if (active) stopScrolling(); +// pageModifications.unregister(checkForButtonRemoved); +// } +// }; const addButtonToPage = async function ([scrollToTopButton]) { if (!scrollToBottomButton) { @@ -66,7 +74,7 @@ const addButtonToPage = async function ([scrollToTopButton]) { scrollToBottomButton.style.marginTop = '0.5ch'; scrollToBottomButton.style.transform = 'rotate(180deg)'; scrollToBottomButton.addEventListener('click', onClick); - scrollToBottomButton.id = scrollToBottomButtonId; + scrollToBottomButton.classList.add(buttonClass); scrollToBottomButton.classList[active ? 'add' : 'remove'](activeClass); } @@ -74,17 +82,40 @@ const addButtonToPage = async function ([scrollToTopButton]) { scrollToTopButton.after(scrollToBottomButton); scrollToTopButton.addEventListener('click', stopScrolling); document.documentElement.addEventListener('keydown', onKeyDown); - pageModifications.register('*', checkForButtonRemoved); + // pageModifications.register('*', checkForButtonRemoved); +}; + +const addModalButtonToPage = async function ([modalScrollToTopButton]) { + if (!modalScrollToBottomButton) { + const hiddenClasses = keyToClasses('hidden'); + + modalScrollToBottomButton = modalScrollToTopButton.cloneNode(true); + hiddenClasses.forEach(className => modalScrollToBottomButton.classList.remove(className)); + modalScrollToBottomButton.removeAttribute('aria-label'); + modalScrollToBottomButton.style.marginTop = '1ch'; + modalScrollToBottomButton.style.transform = 'rotate(180deg)'; + modalScrollToBottomButton.addEventListener('click', onClick); + modalScrollToBottomButton.classList.add(buttonClass); + + modalScrollToBottomButton.classList[active ? 'add' : 'remove'](activeClass); + } + + modalScrollToTopButton.after(modalScrollToBottomButton); + modalScrollToTopButton.addEventListener('click', stopScrolling); + document.documentElement.addEventListener('keydown', onKeyDown); + // pageModifications.register('*', checkForButtonRemoved); }; export const main = async function () { pageModifications.register(`button[aria-label="${translate('Scroll to top')}"]`, addButtonToPage); + pageModifications.register(`button[aria-label="${translate('Back to top')}"]`, addModalButtonToPage); + document.documentElement.append(styleElement); }; export const clean = async function () { pageModifications.unregister(addButtonToPage); - pageModifications.unregister(checkForButtonRemoved); + // pageModifications.unregister(checkForButtonRemoved); stopScrolling(); scrollToBottomButton?.remove(); styleElement.remove(); From 21743e769b130d93bd8140405a813aa4fd5bbfc8 Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 22 Feb 2023 10:24:38 -0800 Subject: [PATCH 03/21] fix missing optional chaining crash --- src/scripts/scroll_to_bottom.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 083bf705a..dd650b717 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -41,8 +41,8 @@ const startScrolling = () => { scrollElement = modalScrollContainer || document.documentElement; observer.observe(modalScrollContainer?.firstElementChild || document.documentElement); active = true; - scrollToBottomButton.classList.add(activeClass); - modalScrollToBottomButton.classList.add(activeClass); + scrollToBottomButton?.classList.add(activeClass); + modalScrollToBottomButton?.classList.add(activeClass); scrollToBottom(); }; From a6aa4e081fcde13de8b0d483012ce50411bf26db Mon Sep 17 00:00:00 2001 From: Marcus Date: Mon, 6 Mar 2023 14:55:51 -0800 Subject: [PATCH 04/21] Revert "Scroll to bottom: Don't deactivate on "embedded blog view" (#976)" This reverts commit 41f58fa27844e297c49123c63f6eae39d8ab5c7e. --- src/scripts/scroll_to_bottom.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index e39ee38d9..768cdfef9 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -1,7 +1,7 @@ import { keyToClasses, keyToCss } from '../util/css_map.js'; import { translate } from '../util/language_data.js'; import { pageModifications } from '../util/mutations.js'; -import { buildStyle } from '../util/interface.js'; +import { blogViewSelector, buildStyle } from '../util/interface.js'; const scrollToBottomButtonId = 'xkit-scroll-to-bottom-button'; $(`[id="${scrollToBottomButtonId}"]`).remove(); @@ -17,6 +17,11 @@ let scrollToBottomButton; let active = false; const styleElement = buildStyle(` +${keyToCss('isPeeprShowing')} #${scrollToBottomButtonId} { + opacity: 0; + pointer-events: none; +} + .${activeClass} svg use { --icon-color-primary: rgb(var(--yellow)); } @@ -24,7 +29,8 @@ const styleElement = buildStyle(` const scrollToBottom = () => { window.scrollTo({ top: document.documentElement.scrollHeight }); - const loaders = [...document.querySelectorAll(knightRiderLoaderSelector)]; + const loaders = [...document.querySelectorAll(knightRiderLoaderSelector)] + .filter(element => element.matches(blogViewSelector) === false); if (loaders.length === 0) { stopScrolling(); From d76f47c2d8f8279cba4b13fdc2693cf17c41a2df Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 15 Mar 2023 13:35:50 -0700 Subject: [PATCH 05/21] stop scrolling on modal close --- src/scripts/scroll_to_bottom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index dd650b717..fd4f64e35 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -29,7 +29,7 @@ const scrollToBottom = () => { scrollElement.scrollTo({ top: scrollElement.scrollHeight }); const loaders = [...scrollElement.querySelectorAll(knightRiderLoaderSelector)]; - if (loaders.length === 0) { + if (!scrollElement.isConnected || loaders.length === 0) { stopScrolling(); } }; From a7c946f821fd6012f33dae09f1b5c57ee35c280b Mon Sep 17 00:00:00 2001 From: Marcus Date: Wed, 15 Mar 2023 13:50:42 -0700 Subject: [PATCH 06/21] refine scroll cancel logic --- src/scripts/scroll_to_bottom.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index fd4f64e35..3393545ca 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -13,6 +13,8 @@ ${keyToCss('notifications')} + ${keyToCss('loader')} `; const knightRiderLoaderSelector = `:is(${loaderSelector}) > ${keyToCss('knightRiderLoader')}`; +const modalScrollContainerSelector = `${keyToCss('drawerContent')} > ${keyToCss('scrollContainer')}`; + let scrollToBottomButton; let modalScrollToBottomButton; let active = false; @@ -25,24 +27,34 @@ const styleElement = buildStyle(` } `); +const getScrollElement = () => + document.querySelector(modalScrollContainerSelector) || + document.documentElement; + +const getObserveElement = () => + document.querySelector(modalScrollContainerSelector)?.firstElementChild || + document.documentElement; + const scrollToBottom = () => { scrollElement.scrollTo({ top: scrollElement.scrollHeight }); + + const buttonConnected = scrollToBottomButton?.isConnected || modalScrollToBottomButton?.isConnected; const loaders = [...scrollElement.querySelectorAll(knightRiderLoaderSelector)]; - if (!scrollElement.isConnected || loaders.length === 0) { + if (!buttonConnected || scrollElement !== getScrollElement() || loaders.length === 0) { stopScrolling(); } }; const observer = new ResizeObserver(scrollToBottom); const startScrolling = () => { - const modalScrollContainer = document.querySelector(`${keyToCss('drawerContent')} > ${keyToCss('scrollContainer')}`); + scrollElement = getScrollElement(); - scrollElement = modalScrollContainer || document.documentElement; - observer.observe(modalScrollContainer?.firstElementChild || document.documentElement); + observer.observe(getObserveElement()); active = true; scrollToBottomButton?.classList.add(activeClass); modalScrollToBottomButton?.classList.add(activeClass); + scrollToBottom(); }; @@ -56,14 +68,6 @@ const stopScrolling = () => { const onClick = () => active ? stopScrolling() : startScrolling(); const onKeyDown = ({ key }) => key === '.' && stopScrolling(); -// const checkForButtonRemoved = () => { -// const buttonWasRemoved = document.documentElement.contains(scrollToBottomButton) === false; -// if (buttonWasRemoved) { -// if (active) stopScrolling(); -// pageModifications.unregister(checkForButtonRemoved); -// } -// }; - const addButtonToPage = async function ([scrollToTopButton]) { if (!scrollToBottomButton) { const hiddenClasses = keyToClasses('hidden'); @@ -82,7 +86,6 @@ const addButtonToPage = async function ([scrollToTopButton]) { scrollToTopButton.after(scrollToBottomButton); scrollToTopButton.addEventListener('click', stopScrolling); document.documentElement.addEventListener('keydown', onKeyDown); - // pageModifications.register('*', checkForButtonRemoved); }; const addModalButtonToPage = async function ([modalScrollToTopButton]) { @@ -103,7 +106,6 @@ const addModalButtonToPage = async function ([modalScrollToTopButton]) { modalScrollToTopButton.after(modalScrollToBottomButton); modalScrollToTopButton.addEventListener('click', stopScrolling); document.documentElement.addEventListener('keydown', onKeyDown); - // pageModifications.register('*', checkForButtonRemoved); }; export const main = async function () { @@ -115,7 +117,6 @@ export const main = async function () { export const clean = async function () { pageModifications.unregister(addButtonToPage); - // pageModifications.unregister(checkForButtonRemoved); stopScrolling(); scrollToBottomButton?.remove(); styleElement.remove(); From ec85f7086379ec31a4333a2dd3b16bb489bf45cb Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 28 Jul 2023 03:31:30 -0700 Subject: [PATCH 07/21] Remove style attribute manipulation --- src/scripts/scroll_to_bottom.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 3393545ca..a3c0443e1 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -22,6 +22,15 @@ let active = false; let scrollElement; const styleElement = buildStyle(` +.${buttonClass} { + margin-top: 0.5ch; + transform: rotate(180deg); +} + +${keyToCss('drawer')} .${buttonClass} { + margin-top: 1ch; +} + .${activeClass} svg use { --icon-color-primary: rgb(var(--yellow)); } @@ -75,8 +84,6 @@ const addButtonToPage = async function ([scrollToTopButton]) { scrollToBottomButton = scrollToTopButton.cloneNode(true); hiddenClasses.forEach(className => scrollToBottomButton.classList.remove(className)); scrollToBottomButton.removeAttribute('aria-label'); - scrollToBottomButton.style.marginTop = '0.5ch'; - scrollToBottomButton.style.transform = 'rotate(180deg)'; scrollToBottomButton.addEventListener('click', onClick); scrollToBottomButton.classList.add(buttonClass); @@ -95,8 +102,6 @@ const addModalButtonToPage = async function ([modalScrollToTopButton]) { modalScrollToBottomButton = modalScrollToTopButton.cloneNode(true); hiddenClasses.forEach(className => modalScrollToBottomButton.classList.remove(className)); modalScrollToBottomButton.removeAttribute('aria-label'); - modalScrollToBottomButton.style.marginTop = '1ch'; - modalScrollToBottomButton.style.transform = 'rotate(180deg)'; modalScrollToBottomButton.addEventListener('click', onClick); modalScrollToBottomButton.classList.add(buttonClass); From be4736fbbb62049c0e504de6b06e2c6b5ec4027e Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 28 Jul 2023 03:59:19 -0700 Subject: [PATCH 08/21] Extract identical logic --- src/scripts/scroll_to_bottom.js | 35 ++++++++++++--------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index a3c0443e1..646321b9e 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -77,18 +77,19 @@ const stopScrolling = () => { const onClick = () => active ? stopScrolling() : startScrolling(); const onKeyDown = ({ key }) => key === '.' && stopScrolling(); -const addButtonToPage = async function ([scrollToTopButton]) { - if (!scrollToBottomButton) { - const hiddenClasses = keyToClasses('hidden'); - - scrollToBottomButton = scrollToTopButton.cloneNode(true); - hiddenClasses.forEach(className => scrollToBottomButton.classList.remove(className)); - scrollToBottomButton.removeAttribute('aria-label'); - scrollToBottomButton.addEventListener('click', onClick); - scrollToBottomButton.classList.add(buttonClass); +const cloneButton = target => { + const clonedButton = target.cloneNode(true); + keyToClasses('hidden').forEach(className => clonedButton.classList.remove(className)); + clonedButton.removeAttribute('aria-label'); + clonedButton.addEventListener('click', onClick); + clonedButton.classList.add(buttonClass); + + clonedButton.classList[active ? 'add' : 'remove'](activeClass); + return clonedButton; +}; - scrollToBottomButton.classList[active ? 'add' : 'remove'](activeClass); - } +const addButtonToPage = async function ([scrollToTopButton]) { + scrollToBottomButton ??= cloneButton(scrollToTopButton); scrollToTopButton.after(scrollToBottomButton); scrollToTopButton.addEventListener('click', stopScrolling); @@ -96,17 +97,7 @@ const addButtonToPage = async function ([scrollToTopButton]) { }; const addModalButtonToPage = async function ([modalScrollToTopButton]) { - if (!modalScrollToBottomButton) { - const hiddenClasses = keyToClasses('hidden'); - - modalScrollToBottomButton = modalScrollToTopButton.cloneNode(true); - hiddenClasses.forEach(className => modalScrollToBottomButton.classList.remove(className)); - modalScrollToBottomButton.removeAttribute('aria-label'); - modalScrollToBottomButton.addEventListener('click', onClick); - modalScrollToBottomButton.classList.add(buttonClass); - - modalScrollToBottomButton.classList[active ? 'add' : 'remove'](activeClass); - } + modalScrollToBottomButton ??= cloneButton(modalScrollToTopButton); modalScrollToTopButton.after(modalScrollToBottomButton); modalScrollToTopButton.addEventListener('click', stopScrolling); From 1c28031813bbbf9f486821366467bf8290d5a6fa Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 28 Jul 2023 04:06:58 -0700 Subject: [PATCH 09/21] Fix button color desync There must be a better way to do this. --- src/scripts/scroll_to_bottom.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 646321b9e..30e6cbf9e 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -96,12 +96,18 @@ const addButtonToPage = async function ([scrollToTopButton]) { document.documentElement.addEventListener('keydown', onKeyDown); }; +const modalButtonColorObserver = new MutationObserver(([mutation]) => { + modalScrollToBottomButton.style = mutation.target.style.cssText; +}); + const addModalButtonToPage = async function ([modalScrollToTopButton]) { modalScrollToBottomButton ??= cloneButton(modalScrollToTopButton); modalScrollToTopButton.after(modalScrollToBottomButton); modalScrollToTopButton.addEventListener('click', stopScrolling); document.documentElement.addEventListener('keydown', onKeyDown); + + modalButtonColorObserver.observe(modalScrollToTopButton, { attributeFilter: ['style'] }); }; export const main = async function () { @@ -113,6 +119,7 @@ export const main = async function () { export const clean = async function () { pageModifications.unregister(addButtonToPage); + modalButtonColorObserver.disconnect(); stopScrolling(); scrollToBottomButton?.remove(); styleElement.remove(); From 875992fa913daabfe82f0065c3fea43ed6be6c62 Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 28 Jul 2023 04:11:13 -0700 Subject: [PATCH 10/21] add missing clean logic --- src/scripts/scroll_to_bottom.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 30e6cbf9e..08eb4e1e2 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -122,5 +122,6 @@ export const clean = async function () { modalButtonColorObserver.disconnect(); stopScrolling(); scrollToBottomButton?.remove(); + modalScrollToBottomButton?.remove(); styleElement.remove(); }; From d6a8777f9edf0f55c09019636bb549113c5c1825 Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 28 Jul 2023 04:13:50 -0700 Subject: [PATCH 11/21] factor out keydown listener --- src/scripts/scroll_to_bottom.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 08eb4e1e2..25c164363 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -93,7 +93,6 @@ const addButtonToPage = async function ([scrollToTopButton]) { scrollToTopButton.after(scrollToBottomButton); scrollToTopButton.addEventListener('click', stopScrolling); - document.documentElement.addEventListener('keydown', onKeyDown); }; const modalButtonColorObserver = new MutationObserver(([mutation]) => { @@ -105,7 +104,6 @@ const addModalButtonToPage = async function ([modalScrollToTopButton]) { modalScrollToTopButton.after(modalScrollToBottomButton); modalScrollToTopButton.addEventListener('click', stopScrolling); - document.documentElement.addEventListener('keydown', onKeyDown); modalButtonColorObserver.observe(modalScrollToTopButton, { attributeFilter: ['style'] }); }; @@ -113,6 +111,7 @@ const addModalButtonToPage = async function ([modalScrollToTopButton]) { export const main = async function () { pageModifications.register(`button[aria-label="${translate('Scroll to top')}"]`, addButtonToPage); pageModifications.register(`button[aria-label="${translate('Back to top')}"]`, addModalButtonToPage); + document.documentElement.addEventListener('keydown', onKeyDown); document.documentElement.append(styleElement); }; From ffe08bc8681b40a140876f13ef51f789e80a6260 Mon Sep 17 00:00:00 2001 From: Marcus Date: Mon, 7 Aug 2023 15:37:32 -0700 Subject: [PATCH 12/21] Fix button color desync when navigating normally --- src/scripts/scroll_to_bottom.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 25c164363..2b25ad76b 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -105,6 +105,7 @@ const addModalButtonToPage = async function ([modalScrollToTopButton]) { modalScrollToTopButton.after(modalScrollToBottomButton); modalScrollToTopButton.addEventListener('click', stopScrolling); + modalScrollToBottomButton.style = modalScrollToTopButton.style.cssText; modalButtonColorObserver.observe(modalScrollToTopButton, { attributeFilter: ['style'] }); }; From 38df8535429e8d2c93f3fbec7a6ae74783d51083 Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 15 Mar 2024 03:12:03 -0700 Subject: [PATCH 13/21] Update manifest to require `:has()` support --- src/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/manifest.json b/src/manifest.json index 8902a972b..3bc650710 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -65,10 +65,10 @@ } ], - "minimum_chrome_version": "89", + "minimum_chrome_version": "105", "browser_specific_settings": { "gecko": { - "strict_min_version": "89.0a1" + "strict_min_version": "121.0" }, "gecko_android": { "strict_min_version": "113.0" From f86426d55072667f9392621c023bec1146def456 Mon Sep 17 00:00:00 2001 From: Marcus Date: Mon, 25 Mar 2024 19:49:53 -0700 Subject: [PATCH 14/21] hide regular button in modal blog view still trying to find a better method for this --- src/scripts/scroll_to_bottom.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 0cf2f2248..0195f519f 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -27,6 +27,11 @@ const styleElement = buildStyle(` transform: rotate(180deg); } +#base-container:has(> #glass-container ${modalScrollContainerSelector}) ${keyToCss('lowerRightButtons')} > .${buttonClass} { + opacity: 0; + pointer-events: none; +} + ${keyToCss('drawer')} .${buttonClass} { margin-top: 1ch; } From 1ca19d0ad518f78037f431ac6d58d68807c8a2ee Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 26 Mar 2024 00:35:12 -0700 Subject: [PATCH 15/21] Update manifest to require `:has()` support oops. --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index 3bc650710..ba850c397 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -71,7 +71,7 @@ "strict_min_version": "121.0" }, "gecko_android": { - "strict_min_version": "113.0" + "strict_min_version": "121.0" } } } From 9a59a57816607c22c82e652b29e89ceba3f77e53 Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 26 Mar 2024 00:45:10 -0700 Subject: [PATCH 16/21] override active modal button background color Co-Authored-By: April Sylph <28949509+AprilSylph@users.noreply.github.com> --- src/scripts/scroll_to_bottom.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 0195f519f..420725e74 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -39,6 +39,9 @@ ${keyToCss('drawer')} .${buttonClass} { .${activeClass} svg use { --icon-color-primary: rgb(var(--yellow)); } +${keyToCss('drawer')} .${activeClass}.${buttonClass} { + background-color: rgb(var(--black)) !important; +} `); const getScrollElement = () => From ff4c38d619ebb15772878a8bc64375ecfeea8777 Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 26 Mar 2024 02:05:56 -0700 Subject: [PATCH 17/21] refine css --- src/scripts/scroll_to_bottom.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 420725e74..2d3a5728e 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -26,20 +26,19 @@ const styleElement = buildStyle(` margin-top: 0.5ch; transform: rotate(180deg); } +.${buttonClass}.modal { + margin-top: 1ch; +} -#base-container:has(> #glass-container ${modalScrollContainerSelector}) ${keyToCss('lowerRightButtons')} > .${buttonClass} { +#base-container:has(> #glass-container ${modalScrollContainerSelector}) .${buttonClass}.normal { opacity: 0; pointer-events: none; } -${keyToCss('drawer')} .${buttonClass} { - margin-top: 1ch; -} - .${activeClass} svg use { --icon-color-primary: rgb(var(--yellow)); } -${keyToCss('drawer')} .${activeClass}.${buttonClass} { +.${activeClass}.modal { background-color: rgb(var(--black)) !important; } `); @@ -85,19 +84,19 @@ const stopScrolling = () => { const onClick = () => active ? stopScrolling() : startScrolling(); const onKeyDown = ({ key }) => key === '.' && stopScrolling(); -const cloneButton = target => { +const cloneButton = (target, mode) => { const clonedButton = target.cloneNode(true); keyToClasses('hidden').forEach(className => clonedButton.classList.remove(className)); clonedButton.removeAttribute('aria-label'); clonedButton.addEventListener('click', onClick); - clonedButton.classList.add(buttonClass); + clonedButton.classList.add(buttonClass, mode); clonedButton.classList[active ? 'add' : 'remove'](activeClass); return clonedButton; }; const addButtonToPage = async function ([scrollToTopButton]) { - scrollToBottomButton ??= cloneButton(scrollToTopButton); + scrollToBottomButton ??= cloneButton(scrollToTopButton, 'normal'); scrollToTopButton.after(scrollToBottomButton); scrollToTopButton.addEventListener('click', stopScrolling); @@ -108,7 +107,7 @@ const modalButtonColorObserver = new MutationObserver(([mutation]) => { }); const addModalButtonToPage = async function ([modalScrollToTopButton]) { - modalScrollToBottomButton ??= cloneButton(modalScrollToTopButton); + modalScrollToBottomButton ??= cloneButton(modalScrollToTopButton, 'modal'); modalScrollToTopButton.after(modalScrollToBottomButton); modalScrollToTopButton.addEventListener('click', stopScrolling); From 1ec8d6fd0f685a4b3fe1ebc8ac1442c670cec4ac Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 26 Mar 2024 02:18:12 -0700 Subject: [PATCH 18/21] cleanup; reduce duplication --- src/scripts/scroll_to_bottom.js | 34 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 2d3a5728e..5a68db29c 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -17,9 +17,7 @@ const modalScrollContainerSelector = `${keyToCss('drawerContent')} > ${keyToCss( let scrollToBottomButton; let modalScrollToBottomButton; -let active = false; - -let scrollElement; +let activeElement = false; const styleElement = buildStyle(` .${buttonClass} { @@ -52,36 +50,35 @@ const getObserveElement = () => document.documentElement; const scrollToBottom = () => { - scrollElement.scrollTo({ top: scrollElement.scrollHeight }); + activeElement.scrollTo({ top: activeElement.scrollHeight }); const buttonConnected = scrollToBottomButton?.isConnected || modalScrollToBottomButton?.isConnected; - const loaders = [...scrollElement.querySelectorAll(knightRiderLoaderSelector)]; + const loaders = [...activeElement.querySelectorAll(knightRiderLoaderSelector)]; - if (!buttonConnected || scrollElement !== getScrollElement() || loaders.length === 0) { + if (!buttonConnected || activeElement !== getScrollElement() || loaders.length === 0) { stopScrolling(); } }; const observer = new ResizeObserver(scrollToBottom); const startScrolling = () => { - scrollElement = getScrollElement(); - - observer.observe(getObserveElement()); - active = true; scrollToBottomButton?.classList.add(activeClass); modalScrollToBottomButton?.classList.add(activeClass); + activeElement = getScrollElement(); + observer.observe(getObserveElement()); scrollToBottom(); }; const stopScrolling = () => { observer.disconnect(); - active = false; + activeElement = undefined; + scrollToBottomButton?.classList.remove(activeClass); modalScrollToBottomButton?.classList.remove(activeClass); }; -const onClick = () => active ? stopScrolling() : startScrolling(); +const onClick = () => activeElement ? stopScrolling() : startScrolling(); const onKeyDown = ({ key }) => key === '.' && stopScrolling(); const cloneButton = (target, mode) => { @@ -91,7 +88,7 @@ const cloneButton = (target, mode) => { clonedButton.addEventListener('click', onClick); clonedButton.classList.add(buttonClass, mode); - clonedButton.classList[active ? 'add' : 'remove'](activeClass); + clonedButton.classList[activeElement ? 'add' : 'remove'](activeClass); return clonedButton; }; @@ -125,10 +122,15 @@ export const main = async function () { }; export const clean = async function () { - pageModifications.unregister(addButtonToPage); - modalButtonColorObserver.disconnect(); stopScrolling(); + + pageModifications.unregister(addButtonToPage); + pageModifications.unregister(addModalButtonToPage); + document.documentElement.removeEventListener('keydown', onKeyDown); + + styleElement.remove(); + scrollToBottomButton?.remove(); modalScrollToBottomButton?.remove(); - styleElement.remove(); + modalButtonColorObserver.disconnect(); }; From 1b0010e55413a0e8cdd27c4edff5d229a55a077b Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 26 Mar 2024 02:22:31 -0700 Subject: [PATCH 19/21] cleanup --- src/scripts/scroll_to_bottom.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scripts/scroll_to_bottom.js b/src/scripts/scroll_to_bottom.js index 5a68db29c..06cbe98ce 100644 --- a/src/scripts/scroll_to_bottom.js +++ b/src/scripts/scroll_to_bottom.js @@ -52,10 +52,10 @@ const getObserveElement = () => const scrollToBottom = () => { activeElement.scrollTo({ top: activeElement.scrollHeight }); - const buttonConnected = scrollToBottomButton?.isConnected || modalScrollToBottomButton?.isConnected; const loaders = [...activeElement.querySelectorAll(knightRiderLoaderSelector)]; + const buttonConnected = scrollToBottomButton?.isConnected || modalScrollToBottomButton?.isConnected; - if (!buttonConnected || activeElement !== getScrollElement() || loaders.length === 0) { + if (loaders.length === 0 || !buttonConnected || activeElement !== getScrollElement()) { stopScrolling(); } }; @@ -72,7 +72,7 @@ const startScrolling = () => { const stopScrolling = () => { observer.disconnect(); - activeElement = undefined; + activeElement = false; scrollToBottomButton?.classList.remove(activeClass); modalScrollToBottomButton?.classList.remove(activeClass); From b879a489d00d819f6f86b3453785ec9e94f7cbc0 Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 28 May 2024 03:16:41 -0700 Subject: [PATCH 20/21] Revert "Update manifest to require `:has()` support" This reverts commit 1ca19d0ad518f78037f431ac6d58d68807c8a2ee. --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index ba850c397..3bc650710 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -71,7 +71,7 @@ "strict_min_version": "121.0" }, "gecko_android": { - "strict_min_version": "121.0" + "strict_min_version": "113.0" } } } From f89811c69a6f15b871e564b19d080f03acc679c8 Mon Sep 17 00:00:00 2001 From: Marcus Date: Tue, 28 May 2024 03:17:35 -0700 Subject: [PATCH 21/21] Revert "Update manifest to require `:has()` support" This reverts commit 38df8535429e8d2c93f3fbec7a6ae74783d51083. --- src/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/manifest.json b/src/manifest.json index 3bc650710..8902a972b 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -65,10 +65,10 @@ } ], - "minimum_chrome_version": "105", + "minimum_chrome_version": "89", "browser_specific_settings": { "gecko": { - "strict_min_version": "121.0" + "strict_min_version": "89.0a1" }, "gecko_android": { "strict_min_version": "113.0"