From 968b462babf5c9a5e4822411fb65ff0a1007762d Mon Sep 17 00:00:00 2001 From: lartsch Date: Fri, 25 Nov 2022 20:40:41 -0500 Subject: [PATCH] another big update (utilize background script, remove redirect page, introduce ext. follow indicator) --- .gitignore | 3 +- firefox/manifest.json | 5 + manifest.json | 7 +- src/background.js | 87 +++++++++ src/inject.js | 433 ++++++++++++++++++++---------------------- src/popup.html | 36 ++-- src/popup.js | 12 +- 7 files changed, 339 insertions(+), 244 deletions(-) create mode 100644 src/background.js diff --git a/.gitignore b/.gitignore index 6f66c74..0791a4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.zip \ No newline at end of file +*.zip +NOTES.md diff --git a/firefox/manifest.json b/firefox/manifest.json index 713e887..02389e2 100644 --- a/firefox/manifest.json +++ b/firefox/manifest.json @@ -10,8 +10,13 @@ "run_at": "document_start" } ], + "background": { + "scripts": ["src/background.js"], + "persistent": false + }, "permissions": [ "storage", + "alarms", "https://*/*", "http://*/*" ], diff --git a/manifest.json b/manifest.json index cd4574d..53a9268 100644 --- a/manifest.json +++ b/manifest.json @@ -10,8 +10,13 @@ "run_at": "document_start" } ], + "background": { + "service_worker": "src/background.js", + "persistent": false + }, "permissions": [ - "storage" + "storage", + "alarms" ], "host_permissions": [ "https://*/*", diff --git a/src/background.js b/src/background.js new file mode 100644 index 0000000..d23073c --- /dev/null +++ b/src/background.js @@ -0,0 +1,87 @@ +var browser, chrome, settings; +var enableConsoleLog = true; +var logPrepend = "[FediFollow]"; + +var tokenRegex = /"access_token":".*?",/gm; + +// required settings keys with defauls +var settingsDefaults = { + fedifollow_homeinstance: null, + fedifollow_showfollows: true +} + +// create alarm every 3 minutes +chrome.alarms.create('refresh', { periodInMinutes: 1 }); + +function onError(error){ + console.error(`${logPrepend} Error: ${error}`); +} + +// wrapper to prepend to log messages +function log(text) { + if (enableConsoleLog) { + console.log(logPrepend + ' ' + text) + } +} + +async function fetchBearerToken() { + var url = "https://" + settings.fedifollow_homeinstance; + var res = await fetch(url); + var text = await res.text(); + if (text) { + // dom parser is not available in background workers, so we use regex to parse the html.... + // for some reason, regex groups do not seem to work in chrome background workers... the following is ugly but should work fine + var content = text.match(tokenRegex)[0]; + if (content) { + var indexOne = content.search(/"access_token":"/); + var indexTwo = content.search(/",/); + if (indexOne > -1 && indexTwo > -1) { + indexOne = indexOne + 16 + var token = content.substring(indexOne, indexTwo); + console.log(token) + if (token.length > 16) { + settings.fedifollow_token = token; + return; + } + } + } + } + // reset token for inject.js to know + settings.fedifollow_token = null; + log("Token could not be found."); +} + +async function fetchFollows() { + var url = "https://" + settings.fedifollow_homeinstance + "/settings/exports/follows.csv"; + var res = await fetch(url); + var text = await res.text(); + if (text) { + var content = text.substring(text.indexOf("\n") + 1); + var lines = content.split("\n"); + var follows = []; + for (var line of lines) { + follows.push(line.split(",")[0]); + } + settings.fedifollow_follows = follows; + return; + } + log("Could not get follow list."); +} + +async function run() { + settings = await (browser || chrome).storage.local.get(settingsDefaults); + if (settings.fedifollow_homeinstance) { + await fetchBearerToken(); + if (settings.fedifollow_showfollows) { + await fetchFollows(); + } else { + log("External follow indication not enabled.") + } + } else { + log("Home instance not set"); + } + await (browser || chrome).storage.local.set(settings); +} + +chrome.runtime.onInstalled.addListener(run); +chrome.alarms.onAlarm.addListener(run); \ No newline at end of file diff --git a/src/inject.js b/src/inject.js index 17e6073..88ad7db 100644 --- a/src/inject.js +++ b/src/inject.js @@ -8,7 +8,7 @@ const domainRegex = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/; const profileRegex = /^(?:https?:\/\/(www\.)?.*\..*?\/)((?@\w+(?:@([\w-]+\.)+?\w+)?)|explore)\/?$/; const tootsRegex = /^(?:https?:\/\/(www\.)?.*\..*?)(\/explore|\/public|\/public\/local|\d+)$/; const tootRegex = /^(?:https?:\/\/(www\.)?.*\..*?\/)(?@\w+(?:@([\w-]+\.)+?\w+)?)\/\d+\/?$/; -const handleExtractRegex = /^.*(?@\w+)@(?([\w-]+\.)+?\w+)\/?$/; +const handleExtractRegex = /^.*@(?\w+)@(?([\w-]+\.)+?\w+)\/?$/; const enableConsoleLog = true; const logPrepend = "[FediFollow]"; const maxElementWaitFactor = 200; // x 100ms for total time @@ -28,11 +28,15 @@ var settingsDefaults = { fedifollow_whitelist: null, fedifollow_blacklist: null, fedifollow_target: "_self", - fedifollow_autoaction: true + fedifollow_autoaction: true, + fedifollow_token: null, + fedifollow_follows: null, + fedifollow_showfollows: true, + fedifollow_redirects: true } // fix for cross-browser storage api compatibility and other public vars -var browser, chrome, instanceUri, fediParamValue, fediParamActionValue, settings; +var browser, chrome, settings; // wrappers to prepend to log messages function log(text) { @@ -133,163 +137,139 @@ var getUrlParameter = function getUrlParameter(sParam) { return false; }; -function redirectToHomeInstance(searchString, action) { - // build url - var url = "https://" + settings.fedifollow_homeinstance + "/?" + fediParamName + "=" + encodeURIComponent(searchString); - if (action) { - // add action parameter if set - url = url + "&" + fediParamActionName + "=" + action; - } - log("Redirecting to " + url); - // alert if set - if (settings.fedifollow_alert) { - alert("Redirecting to "+url); - } - // open window according to settings - var win = window.open(url, settings.fedifollow_target); - // focus the new tab if open was successfull - if (win) { - win.focus(); +function redirectTo(url) { + if (settings.fedifollow_redirects) { + if (settings.fedifollow_alert) { + alert("Redirecting...") + } + // open the url in same/new tab + var win = window.open(url, settings.fedifollow_target); + log("Redirected to " + url) + // focus the new tab if open was successfull + if (win) { + win.focus(); + } else { + // otherwise notify user... + log('Could not open new window. Please allow popups for this website.'); + } } else { - // otherwise notify user... - log('Could not open new window. Please allow popups for this website.'); + log("Redirects disabled.") } } -// process fedifollow redirects to the home instance -async function processHomeInstance() { - // first we need the api token - waitForEl(0, tokenPaths, function(found){ - if (found) { - // then we wait for the appHolder - waitForEl(0, appHolderPaths, async function(holder) { - if (holder) { - // hide the app holder - $(holder).hide(); - // append our notification div - $('body').append('
'); - // extract the token - var token = JSON.parse($(found).text()).meta.access_token; - if (token) { - // update notification div - $('div#fedifollow').html("

Resolving search...

"); - var requestUrl = location.protocol + '//' + location.hostname + searchApi + "/?q="+fediParamValue+"&resolve=true&limit=10"; - var headers = {"Authorization":"Bearer "+token,}; - // api request: search endpoint, resolve search string locally (best support for edge cases (for ex. where subdomain does not equal the handle domain) and prevents uncached profile issue) - var response = await makeRequest("GET", requestUrl, headers); - if (response) { - response = JSON.parse(response); - // decode for additional checks - var decodedParam = decodeURIComponent(fediParamValue); - // if we got no data (failed resolve) we can at least try to resolve a user by swapping the domain in case we got a domain in the handle - // this does not work for resolving post IDs so we check against the handle regex - if (!response.accounts.length && !response.statuses.length && handleExtractRegex.test(decodedParam)) { - // get matches - var matches = decodedParam.match(handleExtractRegex); - if (matches.groups.handle && matches.groups.handledomain) { - // we got handle + handledomain, so try to put the handle domain as host for this fallback (not guaranteed to resolve) - $('div#fedifollow').append("

Failed, trying domain swap...

"); - var searchstring = encodeURIComponent("https://" + matches.groups.handledomain + "/" + matches.groups.handle); - var requestUrl = location.protocol + '//' + location.hostname + searchApi + "/?q="+searchstring+"&resolve=true&limit=10"; - // update response var - response = await makeRequest("GET", requestUrl, headers); - response = JSON.parse(response); - } - } - // set to false initially - var redirect = false; - // if we got an account but no statuses, redirect to profile (first result) - if (response.accounts.length && !response.statuses.length) { - // build redirect url - var redirect = location.protocol + "//" + location.hostname + "/@" + response.accounts[0].acct; - $('div#fedifollow').append("

Success!

"); - // if auto actions are enbaled... - if (settings.fedifollow_autoaction) { - $('div#fedifollow').append("

Attempting auto-follow...

"); - // build follow post request - var requestUrl = location.protocol + "//" + location.hostname + "/api/v1/accounts/" + response.accounts[0].id + "/follow"; - var responseFollow = await makeRequest("POST",requestUrl,headers); - // check if it worked (it is ignored if the user was already followed) - if (responseFollow) { - responseFollow = JSON.parse(responseFollow); - if (responseFollow.following || responseFollow.requested) { - $('div#fedifollow').append("

Success!

"); - } else { - $('div#fedifollow').append("

Failed.

"); - } - } - } - } else if (!response.accounts.length && response.statuses.length) { - $('div#fedifollow').append("

Success!

"); - // if statuses but no accounts, redirect to status (first result) - var status = response.statuses[0]; - var statusData = { - "id": status.id, - "account": status.account.acct - } - // build redirect url - var redirect = location.protocol + "//" + location.hostname + "/@" + statusData.account + "/" + statusData.id; - // if autoactions enabled and fediParamValue is okay... - if (settings.fedifollow_autoaction && (fediParamActionValue == "boost" || fediParamActionValue == "favourite")) { - $('div#fedifollow').append("

Attempting auto-" + fediParamActionValue + "...

"); - // build favourite/boost post request - var actionRequest = location.protocol + "//" + location.hostname + "/api/v1/statuses/" + statusData.id + "/"; - if (fediParamActionValue == "boost") { - actionRequest = actionRequest + "reblog"; - } else { - actionRequest = actionRequest + "favourite"; - } - var actionResponse = await makeRequest("POST", actionRequest, headers); - // check if it worked (it is ignored if the post was already fav'ed / boosted) - if (actionResponse) { - actionResponse = JSON.parse(actionResponse); - if (actionResponse.reblogged || actionResponse.favourited) { - $('div#fedifollow').append("

Success!

"); - } else { - $('div#fedifollow').append("

Failed.

"); - } - } - } - } - // if we got a redirect url... - if (redirect) { - $('div#fedifollow').append("

Redirecting...

"); - // open the url in current tab - var win = window.open(redirect, "_self"); - log("Redirected to " + redirect) - // focus the new tab if open was successfull - if (win) { - win.focus(); - return true; - } else { - // otherwise notify user... - log('Could not open new window. Please allow popups for this website.'); - $('div#fedifollow').text('Could not open new window. Please allow popups for this website.'); - } - } else { - log("Could not resolve a match for this search..."); - $('div#fedifollow').text("Could not resolve a match for this search....") - } - } else { - log("API call failed...") - $('div#fedifollow').text("API call failed...") - } - } else { - log("Could not extract API token.") - $('div#fedifollow').text("Could not get API token..."); - } - // show app holder after 1.5s (in case we did not redirect) - setTimeout(function(){ - $(holder).show() - }, 1500); - } else { - log("Could not find app holder element.") - } - }); +async function followHomeInstance(id, headers, unfollow) { + // if auto actions are enbaled... + if (settings.fedifollow_autoaction) { + // build follow post request + var requestUrl = 'https://' + settings.fedifollow_homeinstance + "/api/v1/accounts/" + id + "/"; + if (unfollow) { + requestUrl = requestUrl + "unfollow"; } else { - log("Could not find API token.") + requestUrl = requestUrl + "follow"; } - }); + var responseFollow = await makeRequest("POST",requestUrl,headers); + // check if it worked (it is ignored if the user was already followed) + if (responseFollow) { + responseFollow = JSON.parse(responseFollow); + if (!responseFollow.following && !responseFollow.requested) { + log("Follow failed."); + } + } + } else { + log("Auto-action disabled.") + } +} + +async function boostHomeInstance(id, headers) { + // if auto actions are enbaled... + if (settings.fedifollow_autoaction) { + // build follow post request + var requestUrl = 'https://' + settings.fedifollow_homeinstance + "/api/v1/statuses/" + id + "/reblog"; + var responseBoost = await makeRequest("POST",requestUrl,headers); + // check if it worked (it is ignored if the user was already followed) + if (responseBoost) { + responseBoost = JSON.parse(responseBoost); + if (!responseBoost.reblogged) { + log("Boost failed."); + } + } + } else { + log("Auto-action disabled.") + } +} + +async function favouriteHomeInstance(id, headers) { + // if auto actions are enbaled... + if (settings.fedifollow_autoaction) { + // build follow post request + var requestUrl = 'https://' + settings.fedifollow_homeinstance + "/api/v1/statuses/" + id + "/favourite"; + var responseFav = await makeRequest("POST",requestUrl,headers); + // check if it worked (it is ignored if the user was already followed) + if (responseFav) { + responseFav = JSON.parse(responseFav); + if (!responseFav.favourited) { + log("Favourite failed."); + } + } + } else { + log("Auto-action disabled.") + } +} + +// resolve content uri on home instance +async function resolveHomeInstance(searchstring, action, unfollow) { + var requestUrl = 'https://' + settings.fedifollow_homeinstance + searchApi + "/?q="+encodeURIComponent(searchstring)+"&resolve=true&limit=10"; + var headers = {"Authorization":"Bearer " + settings.fedifollow_token,}; + // api request: search endpoint, resolve search string locally (best support for edge cases (for ex. where subdomain does not equal the handle domain) and prevents uncached profile issue) + var response = await makeRequest("GET", requestUrl, headers); + if (response) { + response = JSON.parse(response); + // if we got no data (failed resolve) we can at least try to resolve a user by swapping the domain in case we got a domain in the handle + // this does not work for resolving post IDs so we check against the handle regex + if (!response.accounts.length && !response.statuses.length && handleExtractRegex.test(searchstring)) { + // get matches + var matches = searchstring.match(handleExtractRegex); + if (matches.groups.handle && matches.groups.handledomain) { + // we got handle + handledomain, so try to put the handle domain as host for this fallback (not guaranteed to resolve) + var searchstring = "https://" + matches.groups.handledomain + "/@" + matches.groups.handle; + var requestUrl = 'https://' + settings.fedifollow_homeinstance + searchApi + "/?q="+encodeURIComponent(searchstring)+"&resolve=true&limit=10"; + // update response var + response = await makeRequest("GET", requestUrl, headers); + response = JSON.parse(response); + } + } + // set to false initially + var redirect = false; + // if we got an account but no statuses, redirect to profile (first result) + if (response.accounts.length && !response.statuses.length) { + // build redirect url + var redirect = 'https://' + settings.fedifollow_homeinstance + "/@" + response.accounts[0].acct; + await followHomeInstance(response.accounts[0].id, headers, unfollow); + } else if (!response.accounts.length && response.statuses.length) { + // if statuses but no accounts, redirect to status (first result) + var status = response.statuses[0]; + var statusData = { + "id": status.id, + "account": status.account.acct + } + // build redirect url + var redirect = 'https://' + settings.fedifollow_homeinstance + "/@" + statusData.account + "/" + statusData.id; + // perform action if set + if (action == "boost") { + await boostHomeInstance(statusData.id, headers) + } else if (action == "favourite") { + await favouriteHomeInstance(statusData.id, headers) + } + } + // if we got a redirect url... + if (redirect) { + redirectTo(redirect); + } else { + log("Could not resolve a match for this search..."); + } + } else { + log("API call failed...") + } } // process any toots found on supported sites @@ -320,7 +300,7 @@ async function processToots() { // first check if there is an sibling with the actual post URL (easiest and fastest) if ($(this).siblings("a.status__relative-time").attr("href")) { var redirected = true; - redirectToHomeInstance((this).siblings("a.status__relative-time").attr("href"), action); + resolveHomeInstance((this).siblings("a.status__relative-time").attr("href"), action, null); } else if ($(e.target).closest("div.status").attr("data-id")) { // no? then check if there is a closest div.status with the ID in data-id attribute closestTootId = $(e.target).closest("div.status").attr("data-id").replace(/[^0-9]/gi,''); @@ -354,7 +334,7 @@ async function processToots() { var postUri = JSON.parse(response).url.replace("/activity/","").replace("/activity",""); if (postUri) { // redirect to home instance - redirectToHomeInstance(postUri, action); + resolveHomeInstance(postUri, action, null); } else { log("Could not find post url.") } @@ -378,67 +358,73 @@ function processFollow() { // check if this is a profile url if (profileRegex.test(window.location.href.split("?")[0])) { // wait until follow button appears (document is already ready, but most content is loaded afterwards) - waitForEl(0, followButtonPaths, function(found) { + waitForEl(0, followButtonPaths, async function(found) { if (found) { - // setup the button click listener - $(found).click(async function(e) { - // prevent default action and other handlers - e.preventDefault(); - e.stopImmediatePropagation(); - // backup the button text - var originaltext = $(found).html(); - var handleEl; - var handleDomain; - var handle; - // dirty fix for some v3 instance views - if (~window.location.href.indexOf("/explore")) { - var temp = $(e.target).closest("div.account-card").find("div.display-name > span"); - if (temp.length) { - handleEl = temp; + var unfollow, handleEl, handleDomain, handle; + // dirty fix for some v3 instance views + if (~window.location.href.indexOf("/explore")) { + var temp = $(e.target).closest("div.account-card").find("div.display-name > span"); + if (temp.length) { + handleEl = temp; + } + } else { + // check all defined selectors for the username element + for (const selector of profileNamePaths) { + if ($(selector).length) { + handleEl = $(selector) + break; } - } else { - // check all defined selectors for the username element - for (const selector of profileNamePaths) { - if ($(selector).length) { - handleEl = $(selector) - break; + } + } + if (handleEl) { + // match content of first found element against handle regex (with match grups) + var handleDomainMatches = handleEl.text().trim().match(handleExtractRegex); + handleDomain = handleDomainMatches.groups.handledomain; + handle = handleDomainMatches.groups.handle; + } + // if extraction worked... + if (handleDomain && handle) { + if (settings.fedifollow_showfollows) { + if (settings.fedifollow_follows) { + if ($.inArray(handle + "@" + handleDomain, settings.fedifollow_follows) > -1) { + $(found).text("Unfollow"); + unfollow = true; } } } - if (handleEl) { - // match content of first found element against handle regex (with match grups) - var handleDomainMatches = handleEl.text().trim().match(handleExtractRegex); - handleDomain = handleDomainMatches.groups.handledomain; - handle = handleDomainMatches.groups.handle; - } - // if extraction worked... - if (handleDomain && handle) { - // make request to the external instances search endpoint to make sure we get the correct url for the searchstring - // (for ex. another external instance, also instance domain can differ from handle domain) - var requestUrl = location.protocol + "//" + location.hostname + searchApi + "/?q=" + encodeURIComponent(handle+"@"+handleDomain) + "&resolve=false&limit=10"; - var response = await makeRequest("GET", requestUrl, null); - var result; - if (response) { - response = JSON.parse(response); - // if there are any accounts in the response - if (response.accounts.length) { - // get url of first account (which will be the one we need since we searched user+domain) - result = response.accounts[0].url; - // set result for searchstring - var url = document.createElement('a'); - url.setAttribute('href', response.accounts[0].url); - result = url.protocol + "//" + url.hostname; - } + // make request to the external instances search endpoint to make sure we get the correct url for the searchstring + // (for ex. another external instance, also instance domain can differ from handle domain) + var requestUrl = location.protocol + "//" + location.hostname + searchApi + "/?q=" + encodeURIComponent("@"+handle+"@"+handleDomain) + "&resolve=false&limit=10"; + var response = await makeRequest("GET", requestUrl, null); + var result; + if (response) { + response = JSON.parse(response); + // if there are any accounts in the response + if (response.accounts.length) { + // get url of first account (which will be the one we need since we searched user+domain) + result = response.accounts[0].url; + // set result for searchstring + var url = document.createElement('a'); + url.setAttribute('href', response.accounts[0].url); + result = url.protocol + "//" + url.hostname; } + } + // setup the button click listener + $(found).click(async function(e) { + // prevent default action and other handlers + e.preventDefault(); + e.stopImmediatePropagation(); + // backup the button text + var originaltext = $(found).html(); // replace the button text to indicate redirection $(found).text("Redirecting..."); // if we could resolve the user domain... if (result) { // add the handle - var redirectUrl = result + "/" + handle; + var redirectUrl = result + "/@" + handle; // timeout 1000ms to make it possible to notice the redirection indication setTimeout(function() { - redirectToHomeInstance(redirectUrl, null); + resolveHomeInstance(redirectUrl, null, unfollow); // restore original button text $(found).html(originaltext); }, 1000); @@ -447,19 +433,19 @@ function processFollow() { var rawRedirect = window.location.href; // dirty fix for some v3 views if (~rawRedirect.indexOf("/explore")) { - rawRedirect = "https://" + handleDomain + "/" + handle; + rawRedirect = "https://" + handleDomain + "/@" + handle; } // timeout 1000ms to make it possible to notice the redirection indication setTimeout(function() { - redirectToHomeInstance(rawRedirect, null); + resolveHomeInstance(rawRedirect, null, unfollow); // restore original button text $(found).html(originaltext); }, 1000); } - } else { - log("Could not extract user handle.") - } - }); + }); + } else { + log("Could not extract user handle.") + } } else { log("Could not find any follow button."); } @@ -491,6 +477,11 @@ function checkSettings() { log("Mastodon home instance is not set."); return false; } + // no token for api available (see background.js) + if (!settings.fedifollow_token) { + log("No API token available. Are you logged in to your home instance?"); + return false; + } // if the value looks like a domain... if (!(domainRegex.test(settings.fedifollow_homeinstance))) { log("Instance setting is not a valid domain name."); @@ -522,16 +513,8 @@ function checkSettings() { async function checkSite(callback) { // is this site on our home instance? if (location.hostname == settings.fedifollow_homeinstance) { - // do we have a fedifollow param? - fediParamValue = getUrlParameter(fediParamName); - fediParamActionValue = getUrlParameter(fediParamActionName); - if (fediParamValue) { - // if so, run home mode - return "home"; - } else { - log("Current site is your home instance."); - return false; - } + log("Current site is your home instance."); + return false; } // are we in whitelist mode? if (settings.fedifollow_mode == "whitelist") { @@ -554,10 +537,8 @@ async function checkSite(callback) { if (response) { var uri = JSON.parse(response).uri; if (uri) { - // update global var - instanceUri = uri; // run external mode - return "external"; + return true; } } log("Does not look like a Mastodon instance."); @@ -572,14 +553,10 @@ async function run() { // validate settings if (checkSettings()) { // check site (if and which scripts should run) - var mode = await checkSite(); - // run or exit - if (mode == "external") { + if (await checkSite()) { processFollow(); processToots(); urlChangeLoop(); - } else if (mode == "home") { - processHomeInstance(); } else { log("Will not process this site.") } diff --git a/src/popup.html b/src/popup.html index df983dd..0417b69 100644 --- a/src/popup.html +++ b/src/popup.html @@ -5,34 +5,48 @@ .hide { display: none; } + label.pad { + margin-bottom: 4px; + display: block; + } + p { + margin-bottom: 5px; + } - +
-
+

General

+
-
-
-
-
-
- +
+ +
+ +
-
+

Other

+ +
+ +
+

Mode


-
+

-
+


diff --git a/src/popup.js b/src/popup.js index 0c9bb50..43f022b 100644 --- a/src/popup.js +++ b/src/popup.js @@ -1,5 +1,5 @@ -// settings keys with defauls +// required settings keys with defauls var settings = { fedifollow_homeinstance: null, fedifollow_alert: false, @@ -7,14 +7,16 @@ var settings = { fedifollow_whitelist: null, fedifollow_blacklist: null, fedifollow_target: "_self", - fedifollow_autoaction: true + fedifollow_autoaction: true, + fedifollow_showfollows: true, + fedifollow_redirects: true } // fix for cross-browser storage api compatibility var browser, chrome; function onError(error){ - console.error(`[FediFollow] Error: ${error}`) + console.error(`[FediFollow] Error: ${error}`); } // this performs loading the settings into the popup, reacting to changes and saving changes @@ -36,6 +38,8 @@ function popupTasks(settings) { settings.fedifollow_blacklist = $("textarea#blacklist_content").val(); settings.fedifollow_target = $("select#target").val(); settings.fedifollow_autoaction = $("input#autoaction").is(':checked'); + settings.fedifollow_showfollows = $("input#showfollows").is(':checked'); + settings.fedifollow_redirects = $("input#redirects").is(':checked'); // write to storage const waitForSaved = (browser || chrome).storage.local.set(settings); // show saved indicator after successful save @@ -51,6 +55,8 @@ function popupTasks(settings) { $("select#target").val(settings.fedifollow_target); $("input#alert").prop('checked', settings.fedifollow_alert); $("input#autoaction").prop('checked', settings.fedifollow_autoaction); + $("input#showfollows").prop('checked', settings.fedifollow_showfollows); + $("input#redirects").prop('checked', settings.fedifollow_redirects); // both containers are hidden by default if ($("select#mode").val() == "whitelist") { $("div#whitelist_input").show();