commit
2e320d4ca3
3 changed files with 231 additions and 2 deletions
|
@ -11,6 +11,7 @@ import * as MastodonDetect from "./Detect/Mastodon.js";
|
||||||
import * as GnuSocialDetect from "./Detect/GnuSocial.js";
|
import * as GnuSocialDetect from "./Detect/GnuSocial.js";
|
||||||
import * as PleromaDetect from "./Detect/Pleroma.js";
|
import * as PleromaDetect from "./Detect/Pleroma.js";
|
||||||
import * as FriendicaDetect from "./Detect/Friendica.js";
|
import * as FriendicaDetect from "./Detect/Friendica.js";
|
||||||
|
import * as MisskeyDetect from "./Detect/Misskey.js";
|
||||||
|
|
||||||
import * as NetworkTools from "/common/modules/NetworkTools.js";
|
import * as NetworkTools from "/common/modules/NetworkTools.js";
|
||||||
import * as MastodonRedirect from "./MastodonRedirect.js";
|
import * as MastodonRedirect from "./MastodonRedirect.js";
|
||||||
|
@ -24,13 +25,15 @@ const FEDIVERSE_TYPE = Object.freeze({
|
||||||
MASTODON: Symbol("Mastodon"),
|
MASTODON: Symbol("Mastodon"),
|
||||||
GNU_SOCIAL: Symbol("GNU Social"),
|
GNU_SOCIAL: Symbol("GNU Social"),
|
||||||
PLEROMA: Symbol("Pleroma"),
|
PLEROMA: Symbol("Pleroma"),
|
||||||
FRIENDICA: Symbol("Friendica")
|
FRIENDICA: Symbol("Friendica"),
|
||||||
|
MISSKEY: Symbol("Misskey"),
|
||||||
});
|
});
|
||||||
const FEDIVERSE_MODULE = Object.freeze({
|
const FEDIVERSE_MODULE = Object.freeze({
|
||||||
[FEDIVERSE_TYPE.MASTODON]: MastodonDetect,
|
[FEDIVERSE_TYPE.MASTODON]: MastodonDetect,
|
||||||
[FEDIVERSE_TYPE.GNU_SOCIAL]: GnuSocialDetect,
|
[FEDIVERSE_TYPE.GNU_SOCIAL]: GnuSocialDetect,
|
||||||
[FEDIVERSE_TYPE.PLEROMA]: PleromaDetect,
|
[FEDIVERSE_TYPE.PLEROMA]: PleromaDetect,
|
||||||
[FEDIVERSE_TYPE.FRIENDICA]: FriendicaDetect
|
[FEDIVERSE_TYPE.FRIENDICA]: FriendicaDetect,
|
||||||
|
[FEDIVERSE_TYPE.MISSKEY]: MisskeyDetect
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,6 +161,9 @@ async function onTabUpdate(tabId, changeInfo) {
|
||||||
browser.tabs.executeScript(tabId, {
|
browser.tabs.executeScript(tabId, {
|
||||||
file: "/content_script/mastodonInject.js",
|
file: "/content_script/mastodonInject.js",
|
||||||
});
|
});
|
||||||
|
browser.tabs.executeScript(tabId, {
|
||||||
|
file: "/content_script/misskeyInject.js",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
77
src/background/modules/Detect/Misskey.js
Normal file
77
src/background/modules/Detect/Misskey.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/**
|
||||||
|
* Module, that detects a Misskey instance and returns the required values.
|
||||||
|
*
|
||||||
|
* @module Detect/Misskey
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {INTERACTION_TYPE} from "../data/INTERACTION_TYPE.js";
|
||||||
|
|
||||||
|
const REMOTE_INTERACT_REGEX = /\/notes\/(\w|\d)+\/?#interact$/;
|
||||||
|
|
||||||
|
/** The URLs to intercept and pass to this module. */
|
||||||
|
export const CATCH_URLS = new Map();
|
||||||
|
CATCH_URLS.set(REMOTE_INTERACT_REGEX, INTERACTION_TYPE.TOOT_INTERACT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the OAUTH site finished loading.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
let redirectSiteFinishedLoading = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determinates whether the redirect should replace the site before or not.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function shouldLoadReplace() {
|
||||||
|
// if site finished loading, replace it
|
||||||
|
return redirectSiteFinishedLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determinates which tab should be redirected.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {Object} requestDetails
|
||||||
|
* @returns {int}
|
||||||
|
*/
|
||||||
|
export function getTabToModify(requestDetails) {
|
||||||
|
return requestDetails.tabId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the status URL.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {URL} url
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
export function getTootUrl(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
resolve(`https://${url.host}${url.pathname}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the username.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {string|undefined}
|
||||||
|
*/
|
||||||
|
export function getUsername() {
|
||||||
|
throw new NotSupportedError("getUsername() is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server from the required URL.
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @param {URL} url
|
||||||
|
* @returns {string|undefined}
|
||||||
|
*/
|
||||||
|
export function getServer(url) {
|
||||||
|
return url.host;
|
||||||
|
}
|
146
src/content_script/misskeyInject.js
Normal file
146
src/content_script/misskeyInject.js
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement onClick handler for interaction buttons.
|
||||||
|
*
|
||||||
|
* @param {Event} event
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function onClickInteract(event) {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
const articleElement = event.target.closest("article");
|
||||||
|
let headerElement = (
|
||||||
|
articleElement === null
|
||||||
|
? null
|
||||||
|
: articleElement.querySelector("header > div > a") // misskey
|
||||||
|
);
|
||||||
|
headerElement = (
|
||||||
|
(articleElement !== null && headerElement === null)
|
||||||
|
? articleElement.querySelector(".created-at") // calckey
|
||||||
|
: headerElement
|
||||||
|
);
|
||||||
|
const tootId = (
|
||||||
|
headerElement === null
|
||||||
|
? window.location.pathname.toString()
|
||||||
|
: headerElement.getAttribute("href")
|
||||||
|
);
|
||||||
|
// activate AutoRemoteFollow
|
||||||
|
window.open(`${tootId}#interact`, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for element to appear.
|
||||||
|
*
|
||||||
|
* @param {string} selector
|
||||||
|
* @param {boolean} [multiple=false]
|
||||||
|
* @param {number} [timeoutDuration=200000]
|
||||||
|
* @see {@link https://github.com/storybookjs/test-runner/blob/6d41927154e8dd1e4c9e7493122e24e2739a7a0f/src/setup-page.ts#L134}
|
||||||
|
* from which this was adapted
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
function waitForElement(selector, multiple = false, timeoutDuration = 200000) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const getElement = () => (
|
||||||
|
multiple
|
||||||
|
? document.querySelectorAll(selector)
|
||||||
|
: document.querySelector(selector)
|
||||||
|
);
|
||||||
|
const isElementFound = (el) => (!multiple && el) || (multiple && el.length > 0);
|
||||||
|
|
||||||
|
const timeout = window.setTimeout(() => {
|
||||||
|
reject(new Error("waitForElement timed out"));
|
||||||
|
}, timeoutDuration);
|
||||||
|
|
||||||
|
const element = getElement();
|
||||||
|
if (isElementFound(element)) {
|
||||||
|
window.clearTimeout(timeout);
|
||||||
|
return resolve(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
const element = getElement();
|
||||||
|
if (isElementFound(element)) {
|
||||||
|
window.clearTimeout(timeout);
|
||||||
|
resolve(element);
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject replacement onClick handler for Interaction buttons.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
async function injectInteractionButtons() {
|
||||||
|
const INJECTED_REPLY_CLASS = "mastodon-simplified-federation-injected-interaction";
|
||||||
|
const SELECTOR = ".mk-app > .main > .contents article footer > button:not(:last-child)";
|
||||||
|
try {
|
||||||
|
const replyButtons = await waitForElement(SELECTOR, true);
|
||||||
|
replyButtons.forEach((button) => {
|
||||||
|
try {
|
||||||
|
if (!button.classList.contains(INJECTED_REPLY_CLASS)) {
|
||||||
|
button.classList.add(INJECTED_REPLY_CLASS);
|
||||||
|
button.addEventListener("click", onClickInteract);
|
||||||
|
button.removeEventListener("mousedown");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Failed to inject interaction buttons
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Interaction buttons failed to appear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise injection for all remote Misskey buttons.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function initInjections() {
|
||||||
|
injectInteractionButtons().catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise script and re-run if there are changes.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
async function init() {
|
||||||
|
if (typeof MISSKEY_INJECTED === "undefined"){
|
||||||
|
// eslint-disable-next-line vars-on-top, no-var
|
||||||
|
var MISSKEY_INJECTED = true;
|
||||||
|
} else {
|
||||||
|
// init has already run
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initInjections();
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
initInjections();
|
||||||
|
});
|
||||||
|
|
||||||
|
// monitor only the main column in the Misskey UI
|
||||||
|
const mainColumn = await waitForElement(
|
||||||
|
".mk-app > .main",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
observer.observe(mainColumn, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init().catch(console.error);
|
Loading…
Add table
Add a link
Reference in a new issue