diff options
author | Allan Wang <me@allanwang.ca> | 2019-02-05 23:16:29 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-05 23:16:29 -0500 |
commit | 8e3bfc168009c8682c4f6191d655f3ca10ae9f21 (patch) | |
tree | cf8419a06e193c08622ead5e6854b995a5eeba77 /app/src/web/assets/js | |
parent | 83fb36f666fbb934b74b5f763b8ffb2e56ca7761 (diff) | |
parent | ddfc310fde5f50ba52ef930287449c2e08faaca8 (diff) | |
download | frost-8e3bfc168009c8682c4f6191d655f3ca10ae9f21.tar.gz frost-8e3bfc168009c8682c4f6191d655f3ca10ae9f21.tar.bz2 frost-8e3bfc168009c8682c4f6191d655f3ca10ae9f21.zip |
Merge pull request #1334 from AllanWang/fix/offline-crash
Fix/offline crash
Diffstat (limited to 'app/src/web/assets/js')
-rw-r--r-- | app/src/web/assets/js/click_a.js | 46 | ||||
-rw-r--r-- | app/src/web/assets/js/click_a.ts | 57 | ||||
-rw-r--r-- | app/src/web/assets/js/click_debugger.js | 12 | ||||
-rw-r--r-- | app/src/web/assets/js/click_debugger.ts | 15 | ||||
-rw-r--r-- | app/src/web/assets/js/context_a.js | 92 | ||||
-rw-r--r-- | app/src/web/assets/js/context_a.ts | 116 | ||||
-rw-r--r-- | app/src/web/assets/js/document_watcher.js | 23 | ||||
-rw-r--r-- | app/src/web/assets/js/document_watcher.ts | 27 | ||||
-rw-r--r-- | app/src/web/assets/js/header_badges.js | 7 | ||||
-rw-r--r-- | app/src/web/assets/js/header_badges.ts | 7 | ||||
-rw-r--r-- | app/src/web/assets/js/header_hider.js | 12 | ||||
-rw-r--r-- | app/src/web/assets/js/header_hider.ts | 17 | ||||
-rw-r--r-- | app/src/web/assets/js/media.js | 41 | ||||
-rw-r--r-- | app/src/web/assets/js/media.ts | 47 | ||||
-rw-r--r-- | app/src/web/assets/js/menu.js | 55 | ||||
-rw-r--r-- | app/src/web/assets/js/menu.ts | 59 | ||||
-rw-r--r-- | app/src/web/assets/js/notif_msg.js | 25 | ||||
-rw-r--r-- | app/src/web/assets/js/notif_msg.ts | 25 | ||||
-rw-r--r-- | app/src/web/assets/js/textarea_listener.js | 23 | ||||
-rw-r--r-- | app/src/web/assets/js/textarea_listener.ts | 31 |
20 files changed, 737 insertions, 0 deletions
diff --git a/app/src/web/assets/js/click_a.js b/app/src/web/assets/js/click_a.js new file mode 100644 index 00000000..be69bb8c --- /dev/null +++ b/app/src/web/assets/js/click_a.js @@ -0,0 +1,46 @@ +"use strict"; +(function () { + var prevented = false; + var _frostAClick = function (e) { + var target = e.target || e.currentTarget || e.srcElement; + if (!(target instanceof Element)) { + console.log("No element found"); + return; + } + var element = target; + for (var i = 0; i < 2; i++) { + if (element.tagName !== 'A') { + element = element.parentElement; + } + } + if (element.tagName === 'A') { + if (!prevented) { + var url = element.getAttribute('href'); + if (!url || url === '#') { + return; + } + console.log("Click intercept " + url); + if (Frost.loadUrl(url)) { + e.stopPropagation(); + e.preventDefault(); + } + } + else { + console.log("Click intercept prevented"); + } + } + }; + var _frostPreventClick = function () { + console.log("Click _frostPrevented"); + prevented = true; + }; + document.addEventListener('click', _frostAClick, true); + var clickTimeout = undefined; + document.addEventListener('touchstart', function () { + clickTimeout = setTimeout(_frostPreventClick, 400); + }, true); + document.addEventListener('touchend', function () { + prevented = false; + clearTimeout(clickTimeout); + }, true); +}).call(undefined); diff --git a/app/src/web/assets/js/click_a.ts b/app/src/web/assets/js/click_a.ts new file mode 100644 index 00000000..5023610e --- /dev/null +++ b/app/src/web/assets/js/click_a.ts @@ -0,0 +1,57 @@ +(function () { + let prevented = false; + + const _frostAClick = (e: Event) => { + // check for valid target + const target = e.target || e.currentTarget || e.srcElement; + if (!(target instanceof Element)) { + console.log("No element found"); + return + } + let element: Element = target; + // Notifications are two layers under + for (let i = 0; i < 2; i++) { + if (element.tagName !== 'A') { + element = <Element>element.parentElement; + } + } + if (element.tagName === 'A') { + if (!prevented) { + const url = element.getAttribute('href'); + if (!url || url === '#') { + return + } + console.log(`Click intercept ${url}`); + // If Frost is injected, check if loading the url through an overlay works + if (Frost.loadUrl(url)) { + e.stopPropagation(); + e.preventDefault(); + } + } else { + console.log("Click intercept prevented") + } + } + }; + + /* + * On top of the click event, we must stop it for long presses + * Since that will conflict with the context menu + * Note that we only override it on conditions where the context menu + * Will occur + */ + const _frostPreventClick = () => { + console.log("Click _frostPrevented"); + prevented = true; + }; + + document.addEventListener('click', _frostAClick, true); + let clickTimeout: number | undefined = undefined; + document.addEventListener('touchstart', () => { + clickTimeout = setTimeout(_frostPreventClick, 400); + }, true); + document.addEventListener('touchend', () => { + prevented = false; + clearTimeout(clickTimeout) + }, true); +}).call(undefined); + diff --git a/app/src/web/assets/js/click_debugger.js b/app/src/web/assets/js/click_debugger.js new file mode 100644 index 00000000..16729899 --- /dev/null +++ b/app/src/web/assets/js/click_debugger.js @@ -0,0 +1,12 @@ +"use strict"; +(function () { + var _frostAContext = function (e) { + var element = e.target || e.currentTarget || e.srcElement; + if (!(element instanceof Element)) { + console.log("No element found"); + return; + } + console.log("Clicked element " + element.tagName + " " + element.className); + }; + document.addEventListener('contextmenu', _frostAContext, true); +}).call(undefined); diff --git a/app/src/web/assets/js/click_debugger.ts b/app/src/web/assets/js/click_debugger.ts new file mode 100644 index 00000000..088271fa --- /dev/null +++ b/app/src/web/assets/js/click_debugger.ts @@ -0,0 +1,15 @@ +// For desktop only + +(function () { + const _frostAContext = (e: Event) => { + // Commonality; check for valid target + const element = e.target || e.currentTarget || e.srcElement; + if (!(element instanceof Element)) { + console.log("No element found"); + return + } + console.log(`Clicked element ${element.tagName} ${element.className}`); + }; + + document.addEventListener('contextmenu', _frostAContext, true); +}).call(undefined); diff --git a/app/src/web/assets/js/context_a.js b/app/src/web/assets/js/context_a.js new file mode 100644 index 00000000..410553bd --- /dev/null +++ b/app/src/web/assets/js/context_a.js @@ -0,0 +1,92 @@ +"use strict"; +(function () { + var longClick = false; + var _frostCopyComment = function (e, target) { + if (!target.hasAttribute('data-commentid')) { + return false; + } + var text = target.innerText; + console.log("Copy comment " + text); + Frost.contextMenu(null, text); + return true; + }; + var _frostCopyPost = function (e, target) { + if (target.tagName !== 'A') { + return false; + } + var parent1 = target.parentElement; + if (!parent1 || parent1.tagName !== 'DIV') { + return false; + } + var parent2 = parent1.parentElement; + if (!parent2 || !parent2.classList.contains('story_body_container')) { + return false; + } + var url = target.getAttribute('href'); + var text = parent1.innerText; + console.log("Copy post " + url + " " + text); + Frost.contextMenu(url, text); + return true; + }; + var _frostImage = function (e, target) { + var element = target; + for (var i = 0; i < 2; i++) { + if (element.tagName !== 'A') { + element = element.parentElement; + } + } + if (element.tagName !== 'A') { + return false; + } + var url = element.getAttribute('href'); + if (!url || url === '#') { + return false; + } + var text = element.parentElement.innerText; + var image = element.querySelector("[style*=\"background-image: url(\"]"); + if (!image) { + image = element.parentElement.querySelector("[style*=\"background-image: url(\"]"); + } + if (image) { + var imageUrl = window.getComputedStyle(image, null).backgroundImage.trim().slice(4, -1); + console.log("Context image: " + imageUrl); + Frost.loadImage(imageUrl, text); + return true; + } + var img = element.querySelector("img[src*=scontent]"); + if (img instanceof HTMLMediaElement) { + var imgUrl = img.src; + console.log("Context img: " + imgUrl); + Frost.loadImage(imgUrl, text); + return true; + } + console.log("Context content " + url + " " + text); + Frost.contextMenu(url, text); + return true; + }; + var handlers = [_frostCopyComment, _frostCopyPost, _frostImage]; + var _frostAContext = function (e) { + Frost.longClick(true); + longClick = true; + var target = e.target || e.currentTarget || e.srcElement; + if (!(target instanceof HTMLElement)) { + console.log("No element found"); + return; + } + for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) { + var h = handlers_1[_i]; + if (h(e, target)) { + e.stopPropagation(); + e.preventDefault(); + return; + } + } + }; + document.addEventListener('contextmenu', _frostAContext, true); + document.addEventListener('touchend', function () { + if (longClick) { + Frost.longClick(false); + longClick = false; + } + }, true); +}).call(undefined); diff --git a/app/src/web/assets/js/context_a.ts b/app/src/web/assets/js/context_a.ts new file mode 100644 index 00000000..4751bbdc --- /dev/null +++ b/app/src/web/assets/js/context_a.ts @@ -0,0 +1,116 @@ +/** + * Context menu for links + * Largely mimics click_a.js + */ + +(function () { + let longClick = false; + + /** + * Given event and target, return true if handled and false otherwise. + */ + type EventHandler = (e: Event, target: HTMLElement) => Boolean + + const _frostCopyComment: EventHandler = (e, target) => { + if (!target.hasAttribute('data-commentid')) { + return false; + } + const text = target.innerText; + console.log(`Copy comment ${text}`); + Frost.contextMenu(null, text); + return true; + }; + + /** + * Posts should click a tag, with two parents up being div.story_body_container + */ + const _frostCopyPost: EventHandler = (e, target) => { + if (target.tagName !== 'A') { + return false; + } + const parent1 = target.parentElement; + if (!parent1 || parent1.tagName !== 'DIV') { + return false; + } + const parent2 = parent1.parentElement; + if (!parent2 || !parent2.classList.contains('story_body_container')) { + return false; + } + const url = target.getAttribute('href'); + const text = parent1.innerText; + console.log(`Copy post ${url} ${text}`); + Frost.contextMenu(url, text); + return true; + }; + + const _frostImage: EventHandler = (e, target) => { + let element: Element = target; + // Notifications are two layers under + for (let i = 0; i < 2; i++) { + if (element.tagName !== 'A') { + element = <Element>element.parentElement; + } + } + if (element.tagName !== 'A') { + return false; + } + const url = element.getAttribute('href'); + if (!url || url === '#') { + return false; + } + const text = (<HTMLElement>element.parentElement).innerText; + // Check if image item exists, first in children and then in parent + let image = element.querySelector("[style*=\"background-image: url(\"]"); + if (!image) { + image = (<Element>element.parentElement).querySelector("[style*=\"background-image: url(\"]") + } + if (image) { + const imageUrl = (<String>window.getComputedStyle(image, null).backgroundImage).trim().slice(4, -1); + console.log(`Context image: ${imageUrl}`); + Frost.loadImage(imageUrl, text); + return true; + } + // Check if true img exists + const img = element.querySelector("img[src*=scontent]"); + if (img instanceof HTMLMediaElement) { + const imgUrl = img.src; + console.log(`Context img: ${imgUrl}`); + Frost.loadImage(imgUrl, text); + return true; + } + console.log(`Context content ${url} ${text}`); + Frost.contextMenu(url, text); + return true; + }; + + const handlers = [_frostCopyComment, _frostCopyPost, _frostImage]; + + const _frostAContext = (e: Event) => { + Frost.longClick(true); + longClick = true; + + /* + * Commonality; check for valid target + */ + const target = e.target || e.currentTarget || e.srcElement; + if (!(target instanceof HTMLElement)) { + console.log("No element found"); + return + } + for (const h of handlers) { + if (h(e, target)) { + e.stopPropagation(); + e.preventDefault(); + return + } + } + }; + + document.addEventListener('contextmenu', _frostAContext, true); + document.addEventListener('touchend', () => { + if (longClick) { + Frost.longClick(false); + longClick = false + } + }, true); +}).call(undefined); diff --git a/app/src/web/assets/js/document_watcher.js b/app/src/web/assets/js/document_watcher.js new file mode 100644 index 00000000..12252201 --- /dev/null +++ b/app/src/web/assets/js/document_watcher.js @@ -0,0 +1,23 @@ +"use strict"; +(function () { + var isReady = function () { + return document.body.scrollHeight > innerHeight + 100; + }; + if (isReady()) { + console.log('Already ready'); + Frost.isReady(); + return; + } + console.log('Injected document watcher'); + var observer = new MutationObserver(function () { + if (isReady()) { + observer.disconnect(); + Frost.isReady(); + console.log("Documented surpassed height in " + performance.now()); + } + }); + observer.observe(document, { + childList: true, + subtree: true + }); +}).call(undefined); diff --git a/app/src/web/assets/js/document_watcher.ts b/app/src/web/assets/js/document_watcher.ts new file mode 100644 index 00000000..e671149c --- /dev/null +++ b/app/src/web/assets/js/document_watcher.ts @@ -0,0 +1,27 @@ +// Emit key once half the viewport is covered +(function () { + const isReady = () => { + return document.body.scrollHeight > innerHeight + 100 + }; + + if (isReady()) { + console.log('Already ready'); + Frost.isReady(); + return + } + + console.log('Injected document watcher'); + + const observer = new MutationObserver(() => { + if (isReady()) { + observer.disconnect(); + Frost.isReady(); + console.log(`Documented surpassed height in ${performance.now()}`); + } + }); + + observer.observe(document, { + childList: true, + subtree: true + }) +}).call(undefined); diff --git a/app/src/web/assets/js/header_badges.js b/app/src/web/assets/js/header_badges.js new file mode 100644 index 00000000..b1ceee05 --- /dev/null +++ b/app/src/web/assets/js/header_badges.js @@ -0,0 +1,7 @@ +"use strict"; +(function () { + var header = document.getElementById('mJewelNav'); + if (header) { + Frost.handleHeader(header.outerHTML); + } +}).call(undefined); diff --git a/app/src/web/assets/js/header_badges.ts b/app/src/web/assets/js/header_badges.ts new file mode 100644 index 00000000..473749f2 --- /dev/null +++ b/app/src/web/assets/js/header_badges.ts @@ -0,0 +1,7 @@ +// Fetches the header contents if it exists +(function() { + const header = document.getElementById('mJewelNav'); + if (header) { + Frost.handleHeader(header.outerHTML); + } +}).call(undefined); diff --git a/app/src/web/assets/js/header_hider.js b/app/src/web/assets/js/header_hider.js new file mode 100644 index 00000000..faa9f66d --- /dev/null +++ b/app/src/web/assets/js/header_hider.js @@ -0,0 +1,12 @@ +"use strict"; +(function () { + var header = document.querySelector('#header'); + if (!header) { + return; + } + var jewel = header.querySelector('#mJewelNav'); + if (!jewel) { + return; + } + header.style.display = 'none'; +}).call(undefined); diff --git a/app/src/web/assets/js/header_hider.ts b/app/src/web/assets/js/header_hider.ts new file mode 100644 index 00000000..1a8f27f2 --- /dev/null +++ b/app/src/web/assets/js/header_hider.ts @@ -0,0 +1,17 @@ +(function () { + const header = document.querySelector('#header'); + + if (!header) { + return + } + + const jewel = header.querySelector('#mJewelNav'); + + if (!jewel) { + return + } + + (<HTMLElement>header).style.display = 'none' +}).call(undefined); + + diff --git a/app/src/web/assets/js/media.js b/app/src/web/assets/js/media.js new file mode 100644 index 00000000..baeba0a1 --- /dev/null +++ b/app/src/web/assets/js/media.js @@ -0,0 +1,41 @@ +"use strict"; +(function () { + var _frostMediaClick = function (e) { + var target = e.target || e.srcElement; + if (!(target instanceof HTMLElement)) { + return; + } + var element = target; + var dataset = element.dataset; + if (!dataset || !dataset.sigil || dataset.sigil.toLowerCase().indexOf('inlinevideo') == -1) { + return; + } + var i = 0; + while (!element.hasAttribute('data-store')) { + if (++i > 2) { + return; + } + element = element.parentNode; + } + var store = element.dataset.store; + if (!store) { + return; + } + var dataStore; + try { + dataStore = JSON.parse(store); + } + catch (e) { + return; + } + var url = dataStore.src; + if (!url || url.lastIndexOf('http', 0) !== 0) { + return; + } + console.log("Inline video " + url); + if (Frost.loadVideo(url, dataStore.animatedGifVideo || false)) { + e.stopPropagation(); + } + }; + document.addEventListener('click', _frostMediaClick, true); +}).call(undefined); diff --git a/app/src/web/assets/js/media.ts b/app/src/web/assets/js/media.ts new file mode 100644 index 00000000..5b9b1a54 --- /dev/null +++ b/app/src/web/assets/js/media.ts @@ -0,0 +1,47 @@ +// Handles media events +(function () { + const _frostMediaClick = (e: Event) => { + const target = e.target || e.srcElement; + if (!(target instanceof HTMLElement)) { + return + } + let element: HTMLElement = target; + const dataset = element.dataset; + if (!dataset || !dataset.sigil || dataset.sigil.toLowerCase().indexOf('inlinevideo') == -1) { + return + } + let i = 0; + while (!element.hasAttribute('data-store')) { + if (++i > 2) { + return + } + element = <HTMLElement>element.parentNode; + } + const store = element.dataset.store; + if (!store) { + return + } + + let dataStore; + + try { + dataStore = JSON.parse(store) + } catch (e) { + return + } + + const url = dataStore.src; + + // !startsWith; see https://stackoverflow.com/a/36876507/4407321 + if (!url || url.lastIndexOf('http', 0) !== 0) { + return + } + + console.log(`Inline video ${url}`); + if (Frost.loadVideo(url, dataStore.animatedGifVideo || false)) { + e.stopPropagation() + } + }; + + document.addEventListener('click', _frostMediaClick, true); +}).call(undefined); diff --git a/app/src/web/assets/js/menu.js b/app/src/web/assets/js/menu.js new file mode 100644 index 00000000..b6a30209 --- /dev/null +++ b/app/src/web/assets/js/menu.js @@ -0,0 +1,55 @@ +"use strict"; +(function () { + var viewport = document.querySelector("#viewport"); + var root = document.querySelector("#root"); + var bookmarkJewel = document.querySelector("#bookmarks_jewel"); + if (!viewport || !root || !bookmarkJewel) { + console.log('Menu.js: main elements not found'); + Frost.emit(0); + return; + } + var menuA = bookmarkJewel.querySelector("a"); + if (!menuA) { + console.log('Menu.js: menu links not found'); + Frost.emit(0); + return; + } + var jewel = document.querySelector('#mJewelNav'); + if (!jewel) { + console.log('Menu.js: jewel is null'); + return; + } + var y = new MutationObserver(function () { + viewport.removeAttribute('style'); + root.removeAttribute('style'); + }); + y.observe(viewport, { + attributes: true + }); + y.observe(root, { + attributes: true + }); + var x = new MutationObserver(function () { + var menu = document.querySelector('.mSideMenu'); + if (menu) { + x.disconnect(); + console.log("Found side menu"); + while (root.firstChild) { + root.removeChild(root.firstChild); + } + while (menu.childNodes.length) { + viewport.appendChild(menu.childNodes[0]); + } + Frost.emit(0); + setTimeout(function () { + y.disconnect(); + console.log('Unhook styler'); + }, 500); + } + }); + x.observe(jewel, { + childList: true, + subtree: true + }); + menuA.click(); +}).call(undefined); diff --git a/app/src/web/assets/js/menu.ts b/app/src/web/assets/js/menu.ts new file mode 100644 index 00000000..6f9dbf16 --- /dev/null +++ b/app/src/web/assets/js/menu.ts @@ -0,0 +1,59 @@ +// Click menu and move contents to main view +(function () { + const viewport = document.querySelector("#viewport"); + const root = document.querySelector("#root"); + const bookmarkJewel = document.querySelector("#bookmarks_jewel"); + if (!viewport || !root || !bookmarkJewel) { + console.log('Menu.js: main elements not found'); + Frost.emit(0); + return + } + const menuA = bookmarkJewel.querySelector("a"); + if (!menuA) { + console.log('Menu.js: menu links not found'); + Frost.emit(0); + return + } + const jewel = document.querySelector('#mJewelNav'); + if (!jewel) { + console.log('Menu.js: jewel is null'); + return + } + + const y = new MutationObserver(() => { + viewport.removeAttribute('style'); + root.removeAttribute('style'); + }); + + y.observe(viewport, { + attributes: true + }); + y.observe(root, { + attributes: true + }); + + const x = new MutationObserver(() => { + const menu = document.querySelector('.mSideMenu'); + if (menu) { + x.disconnect(); + console.log("Found side menu"); + // Transfer elements + while (root.firstChild) { + root.removeChild(root.firstChild); + } + while (menu.childNodes.length) { + viewport.appendChild(menu.childNodes[0]); + } + Frost.emit(0); + setTimeout(() => { + y.disconnect(); + console.log('Unhook styler'); + }, 500); + } + }); + x.observe(jewel, { + childList: true, + subtree: true + }); + menuA.click(); +}).call(undefined); diff --git a/app/src/web/assets/js/notif_msg.js b/app/src/web/assets/js/notif_msg.js new file mode 100644 index 00000000..bcff697b --- /dev/null +++ b/app/src/web/assets/js/notif_msg.js @@ -0,0 +1,25 @@ +"use strict"; +(function () { + var finished = false; + var x = new MutationObserver(function () { + var _f_thread = document.querySelector('#threadlist_rows'); + if (!_f_thread) { + return; + } + console.log("Found message threads " + _f_thread.outerHTML); + Frost.handleHtml(_f_thread.outerHTML); + finished = true; + x.disconnect(); + }); + x.observe(document, { + childList: true, + subtree: true + }); + setTimeout(function () { + if (!finished) { + finished = true; + console.log('Message thread timeout cancellation'); + Frost.handleHtml(""); + } + }, 20000); +}).call(undefined); diff --git a/app/src/web/assets/js/notif_msg.ts b/app/src/web/assets/js/notif_msg.ts new file mode 100644 index 00000000..b7ce7a19 --- /dev/null +++ b/app/src/web/assets/js/notif_msg.ts @@ -0,0 +1,25 @@ +// Binds callback to an invisible webview to take in the search events +(function () { + let finished = false; + const x = new MutationObserver(() => { + const _f_thread = document.querySelector('#threadlist_rows'); + if (!_f_thread) { + return + } + console.log(`Found message threads ${_f_thread.outerHTML}`); + Frost.handleHtml(_f_thread.outerHTML); + finished = true; + x.disconnect(); + }); + x.observe(document, { + childList: true, + subtree: true + }); + setTimeout(() => { + if (!finished) { + finished = true; + console.log('Message thread timeout cancellation'); + Frost.handleHtml("") + } + }, 20000); +}).call(undefined); diff --git a/app/src/web/assets/js/textarea_listener.js b/app/src/web/assets/js/textarea_listener.js new file mode 100644 index 00000000..1ec9b663 --- /dev/null +++ b/app/src/web/assets/js/textarea_listener.js @@ -0,0 +1,23 @@ +"use strict"; +(function () { + var _frostFocus = function (e) { + var element = e.target || e.srcElement; + if (!(element instanceof Element)) { + return; + } + console.log("FrostJSI focus, " + element.tagName); + if (element.tagName === 'TEXTAREA') { + Frost.disableSwipeRefresh(true); + } + }; + var _frostBlur = function (e) { + var element = e.target || e.srcElement; + if (!(element instanceof Element)) { + return; + } + console.log("FrostJSI blur, " + element.tagName); + Frost.disableSwipeRefresh(false); + }; + document.addEventListener("focus", _frostFocus, true); + document.addEventListener("blur", _frostBlur, true); +}).call(undefined); diff --git a/app/src/web/assets/js/textarea_listener.ts b/app/src/web/assets/js/textarea_listener.ts new file mode 100644 index 00000000..062f5bf6 --- /dev/null +++ b/app/src/web/assets/js/textarea_listener.ts @@ -0,0 +1,31 @@ +/* + * focus listener for textareas + * since swipe to refresh is quite sensitive, we will disable it + * when we detect a user typing + * note that this extends passed having a keyboard opened, + * as a user may still be reviewing his/her post + * swiping should automatically be reset on refresh + */ +(function () { + const _frostFocus = (e: Event) => { + const element = e.target || e.srcElement; + if (!(element instanceof Element)) { + return + } + console.log(`FrostJSI focus, ${element.tagName}`); + if (element.tagName === 'TEXTAREA') { + Frost.disableSwipeRefresh(true); + } + }; + + const _frostBlur = (e: Event) => { + const element = e.target || e.srcElement; + if (!(element instanceof Element)) { + return + } + console.log(`FrostJSI blur, ${element.tagName}`); + Frost.disableSwipeRefresh(false); + }; + document.addEventListener("focus", _frostFocus, true); + document.addEventListener("blur", _frostBlur, true); +}).call(undefined); |