From b61dbc4a577a3b79fcfb0b38e1352b975a2de23d Mon Sep 17 00:00:00 2001 From: Huey Date: Wed, 16 Nov 2022 23:03:15 +0800 Subject: [PATCH] feat: inject content script to handle follows from remote v4 Mastodon instances --- src/background/modules/AutoRemoteFollow.js | 13 +++ src/content_script/mastodonInject.js | 100 +++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/content_script/mastodonInject.js diff --git a/src/background/modules/AutoRemoteFollow.js b/src/background/modules/AutoRemoteFollow.js index 06bf1b9..bc5662a 100644 --- a/src/background/modules/AutoRemoteFollow.js +++ b/src/background/modules/AutoRemoteFollow.js @@ -144,6 +144,15 @@ function getInteractionType(url) { return [null, null]; } +/** + * Handles changes to the URL of a tab + */ +function onTabUpdate() { + browser.tabs.executeScript(null, { + file: `/content_script/mastodonInject.js` + }) +} + /** * Init AutoRemoteFollower module. * @@ -154,6 +163,10 @@ function init() { NetworkTools.webRequestListen(["http://*/*", "https://*/*"], "onBeforeRequest", (requestDetails) => { return handleWebRequest(requestDetails).catch(handleError).catch(console.error); }); + + browser.tabs.onUpdated.addListener(onTabUpdate, { + properties: ["url"] + }); } init(); diff --git a/src/content_script/mastodonInject.js b/src/content_script/mastodonInject.js new file mode 100644 index 0000000..6d219d3 --- /dev/null +++ b/src/content_script/mastodonInject.js @@ -0,0 +1,100 @@ +/** + * @typedef {Object} VersionNumber + * @property {string} major + * @property {string} minor + * @property {string} patch + */ + +/** + * parses a versionNumber string + * @returns {VersionNumber} + */ +function parseVersionNumber() { + const versionElement = document.querySelector(`.link-footer`).textContent + const versionNumber = versionElement.match(/v?(?\d+)\.(?\d+)\.(?\d+)$/) + return versionNumber.groups +} + + +/** + * Replacement onClick handler for Follow button + * @param {Event} event + */ +function onClickFollow (event) { + event.stopPropagation() + event.preventDefault() + const username = window.location.pathname.split(`/`).slice(-1)[0] + // activate AutoRemoteFollow + window.open(`/users/${username}/remote_follow`, `_blank`) +} + +/** + * wait for element to appear + * @param {string} selector + * @param {number} timeout + */ +function waitForElement (selector, timeout) { + return new Promise((resolve, reject) => { + function getElement() { + return document.querySelector(selector) + } + + const element = getElement() + if(element){ + return resolve(element) + } + + const observer = new MutationObserver(() => { + const element = getElement() + if(element){ + resolve(element) + observer.disconnect() + } + }) + + observer.observe(document.body, { + childList: true, + subtree: true + }) + + window.setTimeout(() => { + reject() + }, timeout) + }) +} + +/** + * Inject replacement onClick handler for Follow button + */ +async function injectFollowButton () { + try { + const followButton = await waitForElement(`.account__header__tabs__buttons button.button`, 20000) + followButton.addEventListener(`click`, onClickFollow) + } catch { + // Follow button failed to appear + } +} + +async function init () { + let versionNumber + + try { + const initialStateObject = JSON.parse(document.getElementById(`initial-state`).innerHTML) + const version = initialStateObject?.meta?.version + if(!initialStateObject || !version){ + // not a Mastodon server + return + } + + versionNumber = parseVersionNumber(version) + } catch { + return + } + + + if(Number.parseInt(versionNumber.major) >= 4){ + await injectFollowButton() + } +} + +init() \ No newline at end of file