feature: inject event listeners mimicking <v4 interaction button functionality on v4 Mastodon instances
This commit is contained in:
parent
497b8a2c78
commit
3c6d43b598
2 changed files with 79 additions and 11 deletions
|
@ -147,12 +147,18 @@ function getInteractionType(url) {
|
||||||
/**
|
/**
|
||||||
* Handles changes to the URL of a tab.
|
* Handles changes to the URL of a tab.
|
||||||
*
|
*
|
||||||
|
* @param {string} tabId
|
||||||
|
* @param {Object} changeInfo
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
function onTabUpdate() {
|
async function onTabUpdate(tabId, changeInfo) {
|
||||||
|
const ownMastodon = await AddonSettings.get("ownMastodon");
|
||||||
|
const currentURL = new URL(changeInfo.url);
|
||||||
|
if(ownMastodon.server !== currentURL.hostname){
|
||||||
browser.tabs.executeScript({
|
browser.tabs.executeScript({
|
||||||
file: "/content_script/mastodonInject.js"
|
file: "/content_script/mastodonInject.js"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const TIMEOUT_DURATION = 20000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replacement onClick handler for Follow button.
|
* Replacement onClick handler for Follow button.
|
||||||
*
|
*
|
||||||
|
@ -14,32 +16,57 @@ function onClickFollow(event) {
|
||||||
window.open(`/users/${username}/remote_follow`, "_blank");
|
window.open(`/users/${username}/remote_follow`, "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement onClick handler for interaction buttons.
|
||||||
|
*
|
||||||
|
* @param {Event} event
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function onClickInteract(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
const articleElement = event.target.closest("article[data-id]");
|
||||||
|
const tootId = (
|
||||||
|
articleElement === null
|
||||||
|
? window.location.pathname.split("/").slice(-1)[0]
|
||||||
|
: articleElement.getAttribute("data-id")
|
||||||
|
);
|
||||||
|
// activate AutoRemoteFollow
|
||||||
|
window.open(`/interact/${tootId}`, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for element to appear.
|
* Wait for element to appear.
|
||||||
*
|
*
|
||||||
* @param {string} selector
|
* @param {string} selector
|
||||||
|
* @param {boolean} multiple
|
||||||
* @param {number} timeoutDuration
|
* @param {number} timeoutDuration
|
||||||
* @see {@link https://github.com/storybookjs/test-runner/blob/6d41927154e8dd1e4c9e7493122e24e2739a7a0f/src/setup-page.ts#L134}
|
* @see {@link https://github.com/storybookjs/test-runner/blob/6d41927154e8dd1e4c9e7493122e24e2739a7a0f/src/setup-page.ts#L134}
|
||||||
* from which this was adapted
|
* from which this was adapted
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
function waitForElement(selector, timeoutDuration) {
|
function waitForElement(selector, multiple = false, timeoutDuration) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const getElement = () => document.querySelector(selector);
|
const getElement = () => (
|
||||||
|
multiple
|
||||||
|
? document.querySelectorAll(selector)
|
||||||
|
: document.querySelector(selector)
|
||||||
|
);
|
||||||
|
const isElementFound = (el) => (!multiple && el) || (multiple && el.length > 0);
|
||||||
|
|
||||||
const timeout = window.setTimeout(() => {
|
const timeout = window.setTimeout(() => {
|
||||||
reject(new Error("waitForElement timed out"));
|
reject(new Error("waitForElement timed out"));
|
||||||
}, timeoutDuration);
|
}, timeoutDuration);
|
||||||
|
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
if(element){
|
if(isElementFound(element)){
|
||||||
window.clearTimeout(timeout);
|
window.clearTimeout(timeout);
|
||||||
return resolve(element);
|
return resolve(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new MutationObserver(() => {
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
if(element){
|
if(isElementFound(element)){
|
||||||
window.clearTimeout(timeout);
|
window.clearTimeout(timeout);
|
||||||
resolve(element);
|
resolve(element);
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
|
@ -62,20 +89,55 @@ function waitForElement(selector, timeoutDuration) {
|
||||||
*/
|
*/
|
||||||
async function injectFollowButton() {
|
async function injectFollowButton() {
|
||||||
try {
|
try {
|
||||||
const followButton = await waitForElement(".account__header__tabs__buttons button:first-of-type", 20000);
|
const followButton = await waitForElement(".account__header__tabs__buttons button:first-of-type", false, TIMEOUT_DURATION);
|
||||||
followButton.addEventListener("click", onClickFollow);
|
followButton.addEventListener("click", onClickFollow);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Follow button failed to appear
|
// Follow button failed to appear
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject replacement onClick handler for Interaction buttons.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
async function injectInteractionButtons() {
|
||||||
|
const INJECTED_REPLY_CLASS = "mastodon-simplified-federation-injected-interaction";
|
||||||
|
try {
|
||||||
|
const replyButtons = await waitForElement(
|
||||||
|
".item-list[role='feed'] article[data-id] .status__action-bar button," +
|
||||||
|
".detailed-status__wrapper .detailed-status__action-bar button",
|
||||||
|
true,
|
||||||
|
TIMEOUT_DURATION,
|
||||||
|
);
|
||||||
|
replyButtons.forEach((button) => {
|
||||||
|
if(!button.classList.contains(INJECTED_REPLY_CLASS)){
|
||||||
|
button.addEventListener("click", onClickInteract);
|
||||||
|
button.classList.add(INJECTED_REPLY_CLASS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Interaction buttons failed to appear
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise injection for Mastodon Follow button.
|
* Initialise injection for Mastodon Follow button.
|
||||||
*
|
*
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
async function init() {
|
function init() {
|
||||||
await injectFollowButton();
|
const observer = new MutationObserver(() => {
|
||||||
|
Promise.allSettled([
|
||||||
|
injectFollowButton(),
|
||||||
|
injectInteractionButtons(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true, subtree: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
Loading…
Add table
Add a link
Reference in a new issue