diff --git a/src/background/background.js b/src/background/background.js index e69de29..22114ca 100644 --- a/src/background/background.js +++ b/src/background/background.js @@ -0,0 +1 @@ +import "./modules/AutoRemoteFollow.js"; diff --git a/src/background/modules/AutoRemoteFollow.js b/src/background/modules/AutoRemoteFollow.js new file mode 100644 index 0000000..a057842 --- /dev/null +++ b/src/background/modules/AutoRemoteFollow.js @@ -0,0 +1,109 @@ +// https://regex101.com/r/eKt3Fm/2 +const REMOTE_FOLLOW_REGEX = /\/users\/(.+)\/remote_follow\/?$/; +// https://regex101.com/r/kyjiHj/2 +const REMOTE_INTERACT_REGEX = /\/interact\/(\d+)\/?$/; + +import {splitMastodonHandle} from "/common/modules/mastodonHandle.js"; + +/** + * Saves the information about the parent tab and their state. + * + * @function + * @private + * @param {integer} tabId + * @param {Object} changeInfo + * @returns {Promise} + */ +function handleTabUpdate(tabId, changeInfo) { + // ignore when URL is not changed + if (!changeInfo.url) { + return Promise.resolve(); + } + + const url = new URL(changeInfo.url); + + // ignore non-Mastodon URLs + const mastodonInteraction = isMastodonInteraction(url); + if (mastodonInteraction === false) { + return Promise.resolve(); + } + + // fallback to content script, if page is unsure + if (mastodonInteraction === null) { + // default = current tab + browser.tabs.executeScript( + { + file: "/content_script/mastodon.js", + runAt: "document_end" + } + ); + return Promise.resolve(); + } + + const gettingHandle = browser.storage.sync.get("insertHandle"); + + // Redirect remote_follow page to own instance directly + const remoteUser = getUsername(url); + if (!remoteUser) { + throw new Error("Could not get remote username for Mastodon page."); + } + + return gettingHandle.then((handleObject) => { + const ownMastodon = splitMastodonHandle(handleObject.insertHandle); + const remoteServer = url.host; + + // construct new URL and redirect + return browser.tabs.update({ + loadReplace: true, + // NOTE: This assumes the own server runs on HTTPS, but hey, it really should nowadays! + url: (new URL(`https://${ownMastodon.server}/authorize_follow?acct=${remoteUser}@${remoteServer}`)).toString() + }); + }); +} + +/** + * Checks whether the given URL is (likely) a social remote interaction/follow + * URL of Mastodon. + * + * @function + * @private + * @param {URL} url + * @returns {boolean|null} it returns "null" when it is not sure whether it is + * a Mastodon instance or not. + */ +function isMastodonInteraction(url) { + if (url.pathname.match(REMOTE_FOLLOW_REGEX)) { + return true; + } + + if (url.pathname.match(REMOTE_INTERACT_REGEX)) { + return null; // url is not so stable + } + + return false; +} + +/** + * Returns the username of the given remote_follow page. + * + * @function + * @private + * @param {URL} url + * @returns {string} + */ +function getUsername(url) { + const match = REMOTE_FOLLOW_REGEX.exec(url.pathname); + return match[1]; +} + +/** + * Init AutoRemoteFollower module. + * + * @function + * @returns {Promise} + */ +function init() { + browser.tabs.onUpdated.addListener(handleTabUpdate); +} + +init(); diff --git a/src/common/modules/mastodonHandle.js b/src/common/modules/mastodonHandle.js new file mode 100644 index 0000000..c55947e --- /dev/null +++ b/src/common/modules/mastodonHandle.js @@ -0,0 +1,18 @@ +// https://regex101.com/r/tZjwx7/1 +const MASTODON_HANDLE_SPLIT = /^@?(.+)@(.*)$/; + +/** + * Splits a Mastodon handle to return the username and server URL. + * + * @function + * @param {string} mastodonHandle + * @returns {{username: string, server: string}} username/server + */ +export function splitMastodonHandle(mastodonHandle) { + const matches = MASTODON_HANDLE_SPLIT.exec(mastodonHandle); + + return { + username: matches[1], + server: matches[2] + }; +} diff --git a/src/content_script/content.js b/src/content_script/mastodon.js similarity index 100% rename from src/content_script/content.js rename to src/content_script/mastodon.js diff --git a/src/manifest.json b/src/manifest.json index 673e558..d45ffd3 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -27,17 +27,10 @@ "https://*/*" ], - "content_scripts": [ - { - "matches": ["https://*/*"], - "js": ["content_script/content.js"] - } - ], - "applications": { "gecko": { "id": "mastodon-auto-remote-follow@rugk.github.io", - "strict_min_version": "60.0" + "strict_min_version": "61.0" } } } diff --git a/src/options/options.css b/src/options/options.css index 3bd1040..7854124 100644 --- a/src/options/options.css +++ b/src/options/options.css @@ -54,35 +54,3 @@ fieldset { label + .setting { margin-left: 8px; } - -.helper-text { - display: block; - color: var(--grey-50); - - margin-top: 4px; -} - -/* some margin to align with checkbox */ -input[type=checkbox] ~ .helper-text, -input[type=radio] ~ .helper-text { - /* 4px margin-left + 16px size + 3px margin-right + 5px space (#text) */ - margin-left: 28px; -} - -/* when a link is used in a helper text, add margin */ -.helper-text > a { - margin-left: 4px; - overflow-wrap: none; -} - -/* special options values */ - -#popupIconColor { - display: inline; -} -/* @TODO: improve ID! size can be anything! Just needed, because of current JS behaviour */ -#size { - width: 4.5em; -} - -/* TODO: make i icon bigger */ diff --git a/src/options/options.html b/src/options/options.html index 2d6e9da..be522a1 100644 --- a/src/options/options.html +++ b/src/options/options.html @@ -12,7 +12,7 @@