lemmy-simplified-federation/src/background/modules/AutoRemoteFollow.js

173 lines
5.5 KiB
JavaScript

/**
* Controller intercepting tab loads and redirecting them to correct modules.
*
* @module AutoRemoteFollow
*/
import { INTERACTION_TYPE } from "./data/INTERACTION_TYPE.js";
import { ADDON_NAME } from "/common/modules/GlobalConstants.js";
import * as MastodonDetect from "./Detect/Mastodon.js";
import * as GnuSocialDetect from "./Detect/GnuSocial.js";
import * as PleromaDetect from "./Detect/Pleroma.js";
import * as FriendicaDetect from "./Detect/Friendica.js";
import * as NetworkTools from "/common/modules/NetworkTools.js";
import * as MastodonRedirect from "./MastodonRedirect.js";
import * as AddonSettings from "/common/modules/AddonSettings/AddonSettings.js";
import * as MastodonHandleCheck from "/common/modules/MastodonHandle/ConfigCheck.js";
import * as MastodonHandleError from "/common/modules/MastodonHandle/ConfigError.js";
import * as Notifications from "/common/modules/Notifications.js";
const FEDIVERSE_TYPE = Object.freeze({
MASTODON: Symbol("Mastodon"),
GNU_SOCIAL: Symbol("GNU Social"),
PLEROMA: Symbol("Pleroma"),
FRIENDICA: Symbol("Friendica")
});
const FEDIVERSE_MODULE = Object.freeze({
[FEDIVERSE_TYPE.MASTODON]: MastodonDetect,
[FEDIVERSE_TYPE.GNU_SOCIAL]: GnuSocialDetect,
[FEDIVERSE_TYPE.PLEROMA]: PleromaDetect,
[FEDIVERSE_TYPE.FRIENDICA]: FriendicaDetect
});
/**
* Listens for Mastodon requests at web request change.
*
* Analyses the web request and redirects it, if appropiate.
*
* @private
* @param {Object} requestDetails
* @returns {Promise}
*/
async function handleWebRequest(requestDetails) {
// ignore when URL is not changed
if (!requestDetails.url) {
return Promise.reject(new Error("URL info not available"));
}
const url = new URL(requestDetails.url);
// detect, which network/software it uses
const [software, interaction] = getInteractionType(url);
if (software === null) {
// ignore unrelated sites, resolves so error handling is not triggered
return Promise.resolve();
}
const detectModule = FEDIVERSE_MODULE[software];
// clear cache of settings
await AddonSettings.loadOptions();
MastodonRedirect.setTabToModify(detectModule.getTabToModify.bind(null, requestDetails));
MastodonRedirect.enableLoadReplace(detectModule.shouldLoadReplace.bind(null, requestDetails));
// and get data and pass to redirect
switch (interaction) {
case INTERACTION_TYPE.FOLLOW: {
const remoteUser = await detectModule.getUsername(url, requestDetails);
const remoteServer = await detectModule.getServer(url, requestDetails);
return MastodonRedirect.redirectFollow(remoteUser, remoteServer);
}
case INTERACTION_TYPE.TOOT_INTERACT: {
const tootUrl = await detectModule.getTootUrl(url, requestDetails);
return MastodonRedirect.redirectToot(tootUrl);
}
default:
throw new Error(`unknown interaction type: ${interaction.toString()}`);
}
}
/**
* Handles errors if web request cannot be redirected.
*
* @private
* @param {Error} error
* @returns {Promise}
*/
async function handleError(error) {
// open options on click
const openOptions = () => {
browser.notifications.onClicked.removeListener(openOptions);
browser.runtime.openOptionsPage();
};
browser.notifications.onClicked.addListener(openOptions);
let title = browser.i18n.getMessage("errorNotificationRedirectingTitle", ADDON_NAME);
let errorIdentifier = "couldNotRedirect";
// verify that Mastodon handle is correctly saved
const mastodonHandle = await AddonSettings.get("ownMastodon");
await MastodonHandleCheck.verifyComplete(mastodonHandle).then(() => {
errorIdentifier = "couldNotRedirect";
}).catch((error) => {
if (error.errorType === MastodonHandleError.ERROR_TYPE.NOT_CONFIGURED) {
errorIdentifier = "addonIsNotYetSetup";
// also adjust title
title = browser.i18n.getMessage("errorNotificationNotSetupTitle", ADDON_NAME);
} else {
errorIdentifier = MastodonHandleError.getMastodonErrorString(error);
}
}).finally(() => {
// show actual error
const errorMessage = browser.i18n.getMessage(errorIdentifier) || errorIdentifier;
const message = (errorIdentifier === "couldNotRedirect") ? errorIdentifier : browser.i18n.getMessage("errorNotificationAdjustSettings", errorMessage);
Notifications.showNotification(message, title);
});
// still throw out for debugging
throw error;
}
/**
* Checks what type of interaction the current URL denotes.
*
* @function
* @private
* @param {URL} url
* @returns {[FEDIVERSE_TYPE, Symbol]|[null, null]}
*/
function getInteractionType(url) {
for (const fedType of Object.values(FEDIVERSE_TYPE)) {
for (const [checkRegEx, interactionType] of FEDIVERSE_MODULE[fedType].CATCH_URLS) {
if (url.pathname.match(checkRegEx)) {
return [fedType, interactionType];
}
}
}
return [null, null];
}
/**
* Handles changes to the URL of a tab.
* @returns {void}
*/
function onTabUpdate() {
browser.tabs.executeScript({
file: "/content_script/mastodonInject.js"
});
}
/**
* Init AutoRemoteFollower module.
*
* @function
* @returns {Promise}
*/
function init() {
NetworkTools.webRequestListen(["http://*/*", "https://*/*"], "onBeforeRequest", (requestDetails) => {
return handleWebRequest(requestDetails).catch(handleError).catch(console.error);
});
browser.tabs.onUpdated.addListener(onTabUpdate, {
properties: ["url"]
});
}
init();